Browse Source

Merge branch 'develop' of github.com:ethereum/cpp-ethereum into develop

cl-refactor
Gav Wood 10 years ago
parent
commit
11d972c34f
  1. 104
      libsolidity/ArrayUtils.cpp
  2. 6
      libsolidity/ArrayUtils.h
  3. 107
      libsolidity/ExpressionCompiler.cpp
  4. 10
      mix/qml/CodeEditorView.qml
  5. 7
      mix/qml/WebPreview.qml
  6. 37
      test/ClientBase.cpp

104
libsolidity/ArrayUtils.cpp

@ -440,6 +440,110 @@ void ArrayUtils::retrieveLength(ArrayType const& _arrayType) const
} }
} }
void ArrayUtils::accessIndex(ArrayType const& _arrayType) const
{
ArrayType::Location location = _arrayType.getLocation();
eth::Instruction load =
location == ArrayType::Location::Storage ? eth::Instruction::SLOAD :
location == ArrayType::Location::Memory ? eth::Instruction::MLOAD :
eth::Instruction::CALLDATALOAD;
// retrieve length
if (!_arrayType.isDynamicallySized())
m_context << _arrayType.getLength();
else if (location == ArrayType::Location::CallData)
// length is stored on the stack
m_context << eth::Instruction::SWAP1;
else
m_context << eth::Instruction::DUP2 << load;
// stack: <base_ref> <index> <length>
// check out-of-bounds access
m_context << eth::Instruction::DUP2 << eth::Instruction::LT;
eth::AssemblyItem legalAccess = m_context.appendConditionalJump();
// out-of-bounds access throws exception (just STOP for now)
m_context << eth::Instruction::STOP;
m_context << legalAccess;
// stack: <base_ref> <index>
if (_arrayType.isByteArray())
switch (location)
{
case ArrayType::Location::Storage:
// byte array index storage lvalue on stack (goal):
// <ref> <byte_number> = <base_ref + index / 32> <index % 32>
m_context << u256(32) << eth::Instruction::SWAP2;
CompilerUtils(m_context).computeHashStatic();
// stack: 32 index data_ref
m_context
<< eth::Instruction::DUP3 << eth::Instruction::DUP3
<< eth::Instruction::DIV << eth::Instruction::ADD
// stack: 32 index (data_ref + index / 32)
<< eth::Instruction::SWAP2 << eth::Instruction::SWAP1
<< eth::Instruction::MOD;
break;
case ArrayType::Location::CallData:
// no lvalue, just retrieve the value
m_context
<< eth::Instruction::ADD << eth::Instruction::CALLDATALOAD
<< ((u256(0xff) << (256 - 8))) << eth::Instruction::AND;
break;
case ArrayType::Location::Memory:
solAssert(false, "Memory lvalues not yet implemented.");
}
else
{
// stack: <base_ref> <index>
m_context << eth::Instruction::SWAP1;
if (_arrayType.isDynamicallySized())
{
if (location == ArrayType::Location::Storage)
CompilerUtils(m_context).computeHashStatic();
else if (location == ArrayType::Location::Memory)
m_context << u256(32) << eth::Instruction::ADD;
}
// stack: <index> <data_ref>
switch (location)
{
case ArrayType::Location::CallData:
m_context
<< eth::Instruction::SWAP1 << _arrayType.getBaseType()->getCalldataEncodedSize()
<< eth::Instruction::MUL << eth::Instruction::ADD;
if (_arrayType.getBaseType()->isValueType())
CompilerUtils(m_context).loadFromMemoryDynamic(*_arrayType.getBaseType(), true, true, false);
break;
case ArrayType::Location::Storage:
m_context << eth::Instruction::SWAP1;
if (_arrayType.getBaseType()->getStorageBytes() <= 16)
{
// stack: <data_ref> <index>
// goal:
// <ref> <byte_number> = <base_ref + index / itemsPerSlot> <(index % itemsPerSlot) * byteSize>
unsigned byteSize = _arrayType.getBaseType()->getStorageBytes();
solAssert(byteSize != 0, "");
unsigned itemsPerSlot = 32 / byteSize;
m_context << u256(itemsPerSlot) << eth::Instruction::SWAP2;
// stack: itemsPerSlot index data_ref
m_context
<< eth::Instruction::DUP3 << eth::Instruction::DUP3
<< eth::Instruction::DIV << eth::Instruction::ADD
// stack: itemsPerSlot index (data_ref + index / itemsPerSlot)
<< eth::Instruction::SWAP2 << eth::Instruction::SWAP1
<< eth::Instruction::MOD
<< u256(byteSize) << eth::Instruction::MUL;
}
else
{
if (_arrayType.getBaseType()->getStorageSize() != 1)
m_context << _arrayType.getBaseType()->getStorageSize() << eth::Instruction::MUL;
m_context << eth::Instruction::ADD << u256(0);
}
break;
case ArrayType::Location::Memory:
solAssert(false, "Memory lvalues not yet implemented.");
}
}
}
void ArrayUtils::incrementByteOffset(unsigned _byteSize, unsigned _byteOffsetPosition, unsigned _storageOffsetPosition) const void ArrayUtils::incrementByteOffset(unsigned _byteSize, unsigned _byteOffsetPosition, unsigned _storageOffsetPosition) const
{ {
solAssert(_byteSize < 32, ""); solAssert(_byteSize < 32, "");

6
libsolidity/ArrayUtils.h

@ -70,6 +70,12 @@ public:
/// Stack pre: reference (excludes byte offset for dynamic storage arrays) /// Stack pre: reference (excludes byte offset for dynamic storage arrays)
/// Stack post: reference length /// Stack post: reference length
void retrieveLength(ArrayType const& _arrayType) const; void retrieveLength(ArrayType const& _arrayType) const;
/// Retrieves the value at a specific index. If the location is storage, only retrieves the
/// position.
/// Stack pre: reference [length] index
/// Stack post for storage: slot byte_offset
/// Stack post for calldata: value
void accessIndex(ArrayType const& _arrayType) const;
private: private:
/// Adds the given number of bytes to a storage byte offset counter and also increments /// Adds the given number of bytes to a storage byte offset counter and also increments

107
libsolidity/ExpressionCompiler.cpp

@ -755,113 +755,20 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
{ {
ArrayType const& arrayType = dynamic_cast<ArrayType const&>(baseType); ArrayType const& arrayType = dynamic_cast<ArrayType const&>(baseType);
solAssert(_indexAccess.getIndexExpression(), "Index expression expected."); solAssert(_indexAccess.getIndexExpression(), "Index expression expected.");
ArrayType::Location location = arrayType.getLocation();
eth::Instruction load =
location == ArrayType::Location::Storage ? eth::Instruction::SLOAD :
location == ArrayType::Location::Memory ? eth::Instruction::MLOAD :
eth::Instruction::CALLDATALOAD;
// remove storage byte offset // remove storage byte offset
if (location == ArrayType::Location::Storage) if (arrayType.getLocation() == ArrayType::Location::Storage)
m_context << eth::Instruction::POP; m_context << eth::Instruction::POP;
// stack layout: <base_ref> [<length>] <index>
_indexAccess.getIndexExpression()->accept(*this); _indexAccess.getIndexExpression()->accept(*this);
// retrieve length // stack layout: <base_ref> [<length>] <index>
if (!arrayType.isDynamicallySized()) ArrayUtils(m_context).accessIndex(arrayType);
m_context << arrayType.getLength(); if (arrayType.getLocation() == ArrayType::Location::Storage)
else if (location == ArrayType::Location::CallData)
// length is stored on the stack
m_context << eth::Instruction::SWAP1;
else
m_context << eth::Instruction::DUP2 << load;
// stack: <base_ref> <index> <length>
// check out-of-bounds access
m_context << eth::Instruction::DUP2 << eth::Instruction::LT;
eth::AssemblyItem legalAccess = m_context.appendConditionalJump();
// out-of-bounds access throws exception (just STOP for now)
m_context << eth::Instruction::STOP;
m_context << legalAccess;
// stack: <base_ref> <index>
if (arrayType.isByteArray())
switch (location)
{
case ArrayType::Location::Storage:
// byte array index storage lvalue on stack (goal):
// <ref> <byte_number> = <base_ref + index / 32> <index % 32>
m_context << u256(32) << eth::Instruction::SWAP2;
CompilerUtils(m_context).computeHashStatic();
// stack: 32 index data_ref
m_context
<< eth::Instruction::DUP3 << eth::Instruction::DUP3
<< eth::Instruction::DIV << eth::Instruction::ADD
// stack: 32 index (data_ref + index / 32)
<< eth::Instruction::SWAP2 << eth::Instruction::SWAP1
<< eth::Instruction::MOD;
setLValue<StorageByteArrayElement>(_indexAccess);
break;
case ArrayType::Location::CallData:
// no lvalue, just retrieve the value
m_context
<< eth::Instruction::ADD << eth::Instruction::CALLDATALOAD
<< ((u256(0xff) << (256 - 8))) << eth::Instruction::AND;
break;
case ArrayType::Location::Memory:
solAssert(false, "Memory lvalues not yet implemented.");
}
else
{ {
// stack: <base_ref> <index> if (arrayType.isByteArray())
m_context << eth::Instruction::SWAP1; setLValue<StorageByteArrayElement>(_indexAccess);
if (arrayType.isDynamicallySized()) else
{
if (location == ArrayType::Location::Storage)
CompilerUtils(m_context).computeHashStatic();
else if (location == ArrayType::Location::Memory)
m_context << u256(32) << eth::Instruction::ADD;
}
// stack: <index> <data_ref>
switch (location)
{
case ArrayType::Location::CallData:
m_context
<< eth::Instruction::SWAP1 << arrayType.getBaseType()->getCalldataEncodedSize()
<< eth::Instruction::MUL << eth::Instruction::ADD;
if (arrayType.getBaseType()->isValueType())
CompilerUtils(m_context).loadFromMemoryDynamic(*arrayType.getBaseType(), true, true, false);
break;
case ArrayType::Location::Storage:
m_context << eth::Instruction::SWAP1;
if (arrayType.getBaseType()->getStorageBytes() <= 16)
{
// stack: <data_ref> <index>
// goal:
// <ref> <byte_number> = <base_ref + index / itemsPerSlot> <(index % itemsPerSlot) * byteSize>
unsigned byteSize = arrayType.getBaseType()->getStorageBytes();
solAssert(byteSize != 0, "");
unsigned itemsPerSlot = 32 / byteSize;
m_context << u256(itemsPerSlot) << eth::Instruction::SWAP2;
// stack: itemsPerSlot index data_ref
m_context
<< eth::Instruction::DUP3 << eth::Instruction::DUP3
<< eth::Instruction::DIV << eth::Instruction::ADD
// stack: itemsPerSlot index (data_ref + index / itemsPerSlot)
<< eth::Instruction::SWAP2 << eth::Instruction::SWAP1
<< eth::Instruction::MOD
<< u256(byteSize) << eth::Instruction::MUL;
}
else
{
if (arrayType.getBaseType()->getStorageSize() != 1)
m_context << arrayType.getBaseType()->getStorageSize() << eth::Instruction::MUL;
m_context << eth::Instruction::ADD << u256(0);
}
setLValueToStorageItem(_indexAccess); setLValueToStorageItem(_indexAccess);
break;
case ArrayType::Location::Memory:
solAssert(false, "Memory lvalues not yet implemented.");
}
} }
} }
else else

10
mix/qml/CodeEditorView.qml

@ -11,6 +11,16 @@ Item {
signal breakpointsChanged(string documentId) signal breakpointsChanged(string documentId)
signal isCleanChanged(var isClean, string documentId) signal isCleanChanged(var isClean, string documentId)
function getDocumentIdByName(fileName)
{
for (var i = 0; i < editorListModel.count; i++) {
if (editorListModel.get(i).fileName === fileName) {
return editorListModel.get(i).documentId;
}
}
return null;
}
function getDocumentText(documentId) { function getDocumentText(documentId) {
for (var i = 0; i < editorListModel.count; i++) { for (var i = 0; i < editorListModel.count; i++) {
if (editorListModel.get(i).documentId === documentId) { if (editorListModel.get(i).documentId === documentId) {

7
mix/qml/WebPreview.qml

@ -155,18 +155,19 @@ Item {
//document request //document request
if (urlPath === "/") if (urlPath === "/")
urlPath = "/index.html"; urlPath = "/index.html";
var documentId = urlPath.substr(urlPath.lastIndexOf("/") + 1); var documentName = urlPath.substr(urlPath.lastIndexOf("/") + 1);
var documentId = projectModel.codeEditor.getDocumentIdByName(documentName);
var content = ""; var content = "";
if (projectModel.codeEditor.isDocumentOpen(documentId)) if (projectModel.codeEditor.isDocumentOpen(documentId))
content = projectModel.codeEditor.getDocumentText(documentId); content = projectModel.codeEditor.getDocumentText(documentId);
else else
{ {
var doc = projectModel.getDocument(documentId); var doc = projectModel.getDocument(documentId);
if (doc !== undefined) if (doc)
content = fileIo.readFile(doc.path); content = fileIo.readFile(doc.path);
} }
if (documentId === urlInput.text.replace(httpServer.url + "/", "")) { if (documentName === urlInput.text.replace(httpServer.url + "/", "")) {
//root page, inject deployment script //root page, inject deployment script
content = "<script>web3=parent.web3;contracts=parent.contracts;</script>\n" + content; content = "<script>web3=parent.web3;contracts=parent.contracts;</script>\n" + content;
_request.setResponseContentType("text/html"); _request.setResponseContentType("text/html");

37
test/ClientBase.cpp

@ -34,34 +34,45 @@ BOOST_AUTO_TEST_CASE(blocks)
{ {
enumerateClients([](Json::Value const& _json, dev::eth::ClientBase& _client) -> void enumerateClients([](Json::Value const& _json, dev::eth::ClientBase& _client) -> void
{ {
for (string const& name: _json["postState"].getMemberNames()) auto compareState = [&_client](Json::Value const& _o, string const& _name, BlockNumber _blockNumber) -> void
{ {
Json::Value o = _json["postState"][name]; Address address(_name);
Address address(name);
// balanceAt // balanceAt
u256 expectedBalance = u256(o["balance"].asString()); u256 expectedBalance = u256(_o["balance"].asString());
u256 balance = _client.balanceAt(address); u256 balance = _client.balanceAt(address, _blockNumber);
ETH_CHECK_EQUAL(expectedBalance, balance); ETH_CHECK_EQUAL(expectedBalance, balance);
// countAt // countAt
u256 expectedCount = u256(o["nonce"].asString()); u256 expectedCount = u256(_o["nonce"].asString());
u256 count = _client.countAt(address); u256 count = _client.countAt(address, _blockNumber);
ETH_CHECK_EQUAL(expectedCount, count); ETH_CHECK_EQUAL(expectedCount, count);
// stateAt // stateAt
for (string const& pos: o["storage"].getMemberNames()) for (string const& pos: _o["storage"].getMemberNames())
{ {
u256 expectedState = u256(o["storage"][pos].asString()); u256 expectedState = u256(_o["storage"][pos].asString());
u256 state = _client.stateAt(address, u256(pos)); u256 state = _client.stateAt(address, u256(pos), _blockNumber);
ETH_CHECK_EQUAL(expectedState, state); ETH_CHECK_EQUAL(expectedState, state);
} }
// codeAt // codeAt
bytes expectedCode = fromHex(o["code"].asString()); bytes expectedCode = fromHex(_o["code"].asString());
bytes code = _client.codeAt(address); bytes code = _client.codeAt(address, _blockNumber);
ETH_CHECK_EQUAL_COLLECTIONS(expectedCode.begin(), expectedCode.end(), ETH_CHECK_EQUAL_COLLECTIONS(expectedCode.begin(), expectedCode.end(),
code.begin(), code.end()); code.begin(), code.end());
};
for (string const& name: _json["postState"].getMemberNames())
{
Json::Value o = _json["postState"][name];
compareState(o, name, PendingBlock);
}
for (string const& name: _json["pre"].getMemberNames())
{
Json::Value o = _json["pre"][name];
compareState(o, name, 0);
} }
// number // number

Loading…
Cancel
Save