diff options
Diffstat (limited to 'libsolidity/codegen')
-rw-r--r-- | libsolidity/codegen/ABIFunctions.cpp | 232 | ||||
-rw-r--r-- | libsolidity/codegen/ABIFunctions.h | 22 | ||||
-rw-r--r-- | libsolidity/codegen/ArrayUtils.cpp | 6 | ||||
-rw-r--r-- | libsolidity/codegen/Compiler.h | 15 | ||||
-rw-r--r-- | libsolidity/codegen/CompilerContext.cpp | 16 | ||||
-rw-r--r-- | libsolidity/codegen/CompilerContext.h | 30 | ||||
-rw-r--r-- | libsolidity/codegen/CompilerUtils.cpp | 22 | ||||
-rw-r--r-- | libsolidity/codegen/CompilerUtils.h | 12 | ||||
-rw-r--r-- | libsolidity/codegen/ContractCompiler.cpp | 12 | ||||
-rw-r--r-- | libsolidity/codegen/ContractCompiler.h | 4 | ||||
-rw-r--r-- | libsolidity/codegen/ExpressionCompiler.cpp | 14 | ||||
-rw-r--r-- | libsolidity/codegen/ExpressionCompiler.h | 2 |
12 files changed, 284 insertions, 103 deletions
diff --git a/libsolidity/codegen/ABIFunctions.cpp b/libsolidity/codegen/ABIFunctions.cpp index a2938ed7..9f6c55ba 100644 --- a/libsolidity/codegen/ABIFunctions.cpp +++ b/libsolidity/codegen/ABIFunctions.cpp @@ -30,65 +30,73 @@ using namespace std; using namespace dev; using namespace dev::solidity; -ABIFunctions::~ABIFunctions() -{ - // This throws an exception and thus might cause immediate termination, but hey, - // it's a failed assertion anyway :-) - solAssert(m_requestedFunctions.empty(), "Forgot to call ``requestedFunctions()``."); -} - string ABIFunctions::tupleEncoder( TypePointers const& _givenTypes, TypePointers const& _targetTypes, bool _encodeAsLibraryTypes ) { - // stack: <$value0> <$value1> ... <$value(n-1)> <$headStart> + string functionName = string("abi_encode_tuple_"); + for (auto const& t: _givenTypes) + functionName += t->identifier() + "_"; + functionName += "_to_"; + for (auto const& t: _targetTypes) + functionName += t->identifier() + "_"; + if (_encodeAsLibraryTypes) + functionName += "_library"; - solAssert(!_givenTypes.empty(), ""); - size_t const headSize_ = headSize(_targetTypes); + return createFunction(functionName, [&]() { + solAssert(!_givenTypes.empty(), ""); - Whiskers encoder(R"( + // Note that the values are in reverse due to the difference in calling semantics. + Whiskers templ(R"( + function <functionName>(headStart <valueParams>) -> tail { + tail := add(headStart, <headSize>) + <encodeElements> + } + )"); + templ("functionName", functionName); + size_t const headSize_ = headSize(_targetTypes); + templ("headSize", to_string(headSize_)); + string valueParams; + string encodeElements; + size_t headPos = 0; + size_t stackPos = 0; + for (size_t i = 0; i < _givenTypes.size(); ++i) { - let tail := add($headStart, <headSize>) - <encodeElements> - <deepestStackElement> := tail + solAssert(_givenTypes[i], ""); + solAssert(_targetTypes[i], ""); + size_t sizeOnStack = _givenTypes[i]->sizeOnStack(); + string valueNames = ""; + for (size_t j = 0; j < sizeOnStack; j++) + { + valueNames += "value" + to_string(stackPos) + ", "; + valueParams = ", value" + to_string(stackPos) + valueParams; + stackPos++; + } + bool dynamic = _targetTypes[i]->isDynamicallyEncoded(); + Whiskers elementTempl( + dynamic ? + string(R"( + mstore(add(headStart, <pos>), sub(tail, headStart)) + tail := <abiEncode>(<values> tail) + )") : + string(R"( + <abiEncode>(<values> add(headStart, <pos>)) + )") + ); + elementTempl("values", valueNames); + elementTempl("pos", to_string(headPos)); + elementTempl("abiEncode", abiEncodingFunction(*_givenTypes[i], *_targetTypes[i], _encodeAsLibraryTypes, false)); + encodeElements += elementTempl.render(); + headPos += dynamic ? 0x20 : _targetTypes[i]->calldataEncodedSize(); } - )"); - encoder("headSize", to_string(headSize_)); - string encodeElements; - size_t headPos = 0; - size_t stackPos = 0; - for (size_t i = 0; i < _givenTypes.size(); ++i) - { - solAssert(_givenTypes[i], ""); - solAssert(_targetTypes[i], ""); - size_t sizeOnStack = _givenTypes[i]->sizeOnStack(); - string valueNames = ""; - for (size_t j = 0; j < sizeOnStack; j++) - valueNames += "$value" + to_string(stackPos++) + ", "; - bool dynamic = _targetTypes[i]->isDynamicallyEncoded(); - Whiskers elementTempl( - dynamic ? - string(R"( - mstore(add($headStart, <pos>), sub(tail, $headStart)) - tail := <abiEncode>(<values> tail) - )") : - string(R"( - <abiEncode>(<values> add($headStart, <pos>)) - )") - ); - elementTempl("values", valueNames); - elementTempl("pos", to_string(headPos)); - elementTempl("abiEncode", abiEncodingFunction(*_givenTypes[i], *_targetTypes[i], _encodeAsLibraryTypes, false)); - encodeElements += elementTempl.render(); - headPos += dynamic ? 0x20 : _targetTypes[i]->calldataEncodedSize(); - } - solAssert(headPos == headSize_, ""); - encoder("encodeElements", encodeElements); - encoder("deepestStackElement", stackPos > 0 ? "$value0" : "$headStart"); + solAssert(headPos == headSize_, ""); + templ("valueParams", valueParams); + templ("encodeElements", encodeElements); - return encoder.render(); + return templ.render(); + }); } string ABIFunctions::requestedFunctions() @@ -396,9 +404,11 @@ string ABIFunctions::abiEncodingFunction( else solAssert(false, ""); } - else if (dynamic_cast<StructType const*>(&to)) + else if (auto const* toStruct = dynamic_cast<StructType const*>(&to)) { - solUnimplementedAssert(false, "Structs not yet implemented."); + StructType const* fromStruct = dynamic_cast<StructType const*>(&_from); + solAssert(fromStruct, ""); + return abiEncodingFunctionStruct(*fromStruct, *toStruct, _encodeAsLibraryTypes); } else if (_from.category() == Type::Category::Function) return abiEncodingFunctionFunctionType( @@ -526,7 +536,7 @@ string ABIFunctions::abiEncodingFunctionSimpleArray( for { let i := 0 } lt(i, length) { i := add(i, 1) } { mstore(pos, sub(tail, headStart)) - tail := <encodeToMemoryFun>(<arrayElementAccess>(srcPtr), tail) + tail := <encodeToMemoryFun>(<arrayElementAccess>, tail) srcPtr := <nextArrayElement>(srcPtr) pos := add(pos, <elementEncodedSize>) } @@ -541,7 +551,7 @@ string ABIFunctions::abiEncodingFunctionSimpleArray( let srcPtr := <dataAreaFun>(value) for { let i := 0 } lt(i, length) { i := add(i, 1) } { - <encodeToMemoryFun>(<arrayElementAccess>(srcPtr), pos) + <encodeToMemoryFun>(<arrayElementAccess>, pos) srcPtr := <nextArrayElement>(srcPtr) pos := add(pos, <elementEncodedSize>) } @@ -565,7 +575,7 @@ string ABIFunctions::abiEncodingFunctionSimpleArray( _encodeAsLibraryTypes, true )); - templ("arrayElementAccess", inMemory ? "mload" : "sload"); + templ("arrayElementAccess", inMemory ? "mload(srcPtr)" : _from.baseType()->isValueType() ? "sload(srcPtr)" : "srcPtr" ); templ("nextArrayElement", nextArrayElementFunction(_from)); return templ.render(); }); @@ -718,6 +728,122 @@ string ABIFunctions::abiEncodingFunctionCompactStorageArray( }); } +string ABIFunctions::abiEncodingFunctionStruct( + StructType const& _from, + StructType const& _to, + bool _encodeAsLibraryTypes +) +{ + string functionName = + "abi_encode_" + + _from.identifier() + + "_to_" + + _to.identifier() + + (_encodeAsLibraryTypes ? "_library" : ""); + + solUnimplementedAssert(!_from.dataStoredIn(DataLocation::CallData), ""); + solAssert(&_from.structDefinition() == &_to.structDefinition(), ""); + + return createFunction(functionName, [&]() { + bool fromStorage = _from.location() == DataLocation::Storage; + bool dynamic = _to.isDynamicallyEncoded(); + Whiskers templ(R"( + function <functionName>(value, pos) <return> { + let tail := add(pos, <headSize>) + <init> + <#members> + { + // <memberName> + <encode> + } + </members> + <assignEnd> + } + )"); + templ("functionName", functionName); + templ("return", dynamic ? " -> end " : ""); + templ("assignEnd", dynamic ? "end := tail" : ""); + // to avoid multiple loads from the same slot for subsequent members + templ("init", fromStorage ? "let slotValue := 0" : ""); + u256 previousSlotOffset(-1); + u256 encodingOffset = 0; + vector<map<string, string>> members; + for (auto const& member: _to.members(nullptr)) + { + solAssert(member.type, ""); + if (!member.type->canLiveOutsideStorage()) + continue; + solUnimplementedAssert( + member.type->mobileType() && + member.type->mobileType()->interfaceType(_encodeAsLibraryTypes) && + member.type->mobileType()->interfaceType(_encodeAsLibraryTypes)->encodingType(), + "Encoding type \"" + member.type->toString() + "\" not yet implemented." + ); + auto memberTypeTo = member.type->mobileType()->interfaceType(_encodeAsLibraryTypes)->encodingType(); + auto memberTypeFrom = _from.memberType(member.name); + solAssert(memberTypeFrom, ""); + bool dynamicMember = memberTypeTo->isDynamicallyEncoded(); + if (dynamicMember) + solAssert(dynamic, ""); + Whiskers memberTempl(R"( + <preprocess> + let memberValue := <retrieveValue> + )" + ( + dynamicMember ? + string(R"( + mstore(add(pos, <encodingOffset>), sub(tail, pos)) + tail := <abiEncode>(memberValue, tail) + )") : + string(R"( + <abiEncode>(memberValue, add(pos, <encodingOffset>)) + )") + ) + ); + if (fromStorage) + { + solAssert(memberTypeFrom->isValueType() == memberTypeTo->isValueType(), ""); + u256 storageSlotOffset; + size_t intraSlotOffset; + tie(storageSlotOffset, intraSlotOffset) = _from.storageOffsetsOfMember(member.name); + if (memberTypeFrom->isValueType()) + { + if (storageSlotOffset != previousSlotOffset) + { + memberTempl("preprocess", "slotValue := sload(add(value, " + toCompactHexWithPrefix(storageSlotOffset) + "))"); + previousSlotOffset = storageSlotOffset; + } + else + memberTempl("preprocess", ""); + memberTempl("retrieveValue", shiftRightFunction(intraSlotOffset * 8, false) + "(slotValue)"); + } + else + { + solAssert(memberTypeFrom->dataStoredIn(DataLocation::Storage), ""); + solAssert(intraSlotOffset == 0, ""); + memberTempl("preprocess", ""); + memberTempl("retrieveValue", "add(value, " + toCompactHexWithPrefix(storageSlotOffset) + ")"); + } + } + else + { + memberTempl("preprocess", ""); + string sourceOffset = toCompactHexWithPrefix(_from.memoryOffsetOfMember(member.name)); + memberTempl("retrieveValue", "mload(add(value, " + sourceOffset + "))"); + } + memberTempl("encodingOffset", toCompactHexWithPrefix(encodingOffset)); + encodingOffset += dynamicMember ? 0x20 : memberTypeTo->calldataEncodedSize(); + memberTempl("abiEncode", abiEncodingFunction(*memberTypeFrom, *memberTypeTo, _encodeAsLibraryTypes, false)); + + members.push_back({}); + members.back()["encode"] = memberTempl.render(); + members.back()["memberName"] = member.name; + } + templ("members", members); + templ("headSize", toCompactHexWithPrefix(encodingOffset)); + return templ.render(); + }); +} + string ABIFunctions::abiEncodingFunctionStringLiteral( Type const& _from, Type const& _to, diff --git a/libsolidity/codegen/ABIFunctions.h b/libsolidity/codegen/ABIFunctions.h index e43e2323..de2a140a 100644 --- a/libsolidity/codegen/ABIFunctions.h +++ b/libsolidity/codegen/ABIFunctions.h @@ -44,15 +44,18 @@ using TypePointers = std::vector<TypePointer>; /// multiple times. /// /// Make sure to include the result of ``requestedFunctions()`` to a block that -/// is visible from the code that was generated here. +/// is visible from the code that was generated here, or use named labels. class ABIFunctions { public: - ~ABIFunctions(); - - /// @returns assembly code block to ABI-encode values of @a _givenTypes residing on the stack + /// @returns name of an assembly function to ABI-encode values of @a _givenTypes /// into memory, converting the types to @a _targetTypes on the fly. - /// Assumed variables to be present: <$value0> <$value1> ... <$value(n-1)> <$headStart> + /// Parameters are: <headStart> <value_n> ... <value_1>, i.e. + /// the layout on the stack is <value_1> ... <value_n> <headStart> with + /// the top of the stack on the right. + /// The values represent stack slots. If a type occupies more or less than one + /// stack slot, it takes exactly that number of values. + /// Returns a pointer to the end of the area written in memory. /// Does not allocate memory (does not change the memory head pointer), but writes /// to memory starting at $headStart and an unrestricted amount after that. /// Assigns the end of encoded memory either to $value0 or (if that is not present) @@ -63,7 +66,7 @@ public: bool _encodeAsLibraryTypes = false ); - /// @returns auxiliary functions referenced from the block generated in @a tupleEncoder + /// @returns concatenation of all generated functions. std::string requestedFunctions(); private: @@ -120,6 +123,13 @@ private: bool _encodeAsLibraryTypes ); + /// Part of @a abiEncodingFunction for struct types. + std::string abiEncodingFunctionStruct( + StructType const& _givenType, + StructType const& _targetType, + bool _encodeAsLibraryTypes + ); + // @returns the name of the ABI encoding function with the given type // and queues the generation of the function to the requested functions. // Case for _givenType being a string literal diff --git a/libsolidity/codegen/ArrayUtils.cpp b/libsolidity/codegen/ArrayUtils.cpp index 67ca22f1..e17188c2 100644 --- a/libsolidity/codegen/ArrayUtils.cpp +++ b/libsolidity/codegen/ArrayUtils.cpp @@ -913,10 +913,10 @@ void ArrayUtils::accessIndex(ArrayType const& _arrayType, bool _doBoundsCheck) c switch (location) { case DataLocation::Memory: - if (_arrayType.isDynamicallySized()) - m_context << u256(32) << Instruction::ADD; - // fall-through case DataLocation::CallData: + if (location == DataLocation::Memory && _arrayType.isDynamicallySized()) + m_context << u256(32) << Instruction::ADD; + if (!_arrayType.isByteArray()) { m_context << Instruction::SWAP1; diff --git a/libsolidity/codegen/Compiler.h b/libsolidity/codegen/Compiler.h index 8c63ea9c..06654486 100644 --- a/libsolidity/codegen/Compiler.h +++ b/libsolidity/codegen/Compiler.h @@ -40,6 +40,8 @@ public: m_context(&m_runtimeContext) { } + /// Compiles a contract. + /// @arg _metadata contains the to be injected metadata CBOR void compileContract( ContractDefinition const& _contract, std::map<ContractDefinition const*, eth::Assembly const*> const& _contracts, @@ -51,14 +53,21 @@ public: ContractDefinition const& _contract, std::map<ContractDefinition const*, eth::Assembly const*> const& _contracts ); + /// @returns Entire assembly. eth::Assembly const& assembly() const { return m_context.assembly(); } + /// @returns The entire assembled object (with constructor). eth::LinkerObject assembledObject() const { return m_context.assembledObject(); } + /// @returns Only the runtime object (without constructor). eth::LinkerObject runtimeObject() const { return m_context.assembledRuntimeObject(m_runtimeSub); } /// @arg _sourceCodes is the map of input files to source code strings - /// @arg _inJsonFromat shows whether the out should be in Json format - Json::Value streamAssembly(std::ostream& _stream, StringMap const& _sourceCodes = StringMap(), bool _inJsonFormat = false) const + std::string assemblyString(StringMap const& _sourceCodes = StringMap()) const { - return m_context.streamAssembly(_stream, _sourceCodes, _inJsonFormat); + return m_context.assemblyString(_sourceCodes); + } + /// @arg _sourceCodes is the map of input files to source code strings + Json::Value assemblyJSON(StringMap const& _sourceCodes = StringMap()) const + { + return m_context.assemblyJSON(_sourceCodes); } /// @returns Assembly items of the normal compiler context eth::AssemblyItems const& assemblyItems() const { return m_context.assembly().items(); } diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index ed780d0b..5a77162e 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -266,19 +266,9 @@ void CompilerContext::resetVisitedNodes(ASTNode const* _node) void CompilerContext::appendInlineAssembly( string const& _assembly, vector<string> const& _localVariables, - map<string, string> const& _replacements + bool _system ) { - string replacedAssembly; - string const* assembly = &_assembly; - if (!_replacements.empty()) - { - replacedAssembly = _assembly; - for (auto const& replacement: _replacements) - replacedAssembly = boost::algorithm::replace_all_copy(replacedAssembly, replacement.first, replacement.second); - assembly = &replacedAssembly; - } - int startStackHeight = stackHeight(); julia::ExternalIdentifierAccess identifierAccess; @@ -320,7 +310,7 @@ void CompilerContext::appendInlineAssembly( ErrorList errors; ErrorReporter errorReporter(errors); - auto scanner = make_shared<Scanner>(CharStream(*assembly), "--CODEGEN--"); + auto scanner = make_shared<Scanner>(CharStream(_assembly), "--CODEGEN--"); auto parserResult = assembly::Parser(errorReporter).parse(scanner); solAssert(parserResult, "Failed to parse inline assembly block."); solAssert(errorReporter.errors().empty(), "Failed to parse inline assembly block."); @@ -329,7 +319,7 @@ void CompilerContext::appendInlineAssembly( assembly::AsmAnalyzer analyzer(analysisInfo, errorReporter, false, identifierAccess.resolve); solAssert(analyzer.analyze(*parserResult), "Failed to analyze inline assembly block."); solAssert(errorReporter.errors().empty(), "Failed to analyze inline assembly block."); - assembly::CodeGenerator::assemble(*parserResult, analysisInfo, *m_asm, identifierAccess); + assembly::CodeGenerator::assemble(*parserResult, analysisInfo, *m_asm, identifierAccess, _system); } FunctionDefinition const& CompilerContext::resolveVirtualFunction( diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h index 96cbf6c1..7743fd3f 100644 --- a/libsolidity/codegen/CompilerContext.h +++ b/libsolidity/codegen/CompilerContext.h @@ -22,6 +22,8 @@ #pragma once +#include <libsolidity/codegen/ABIFunctions.h> + #include <libsolidity/ast/ASTForward.h> #include <libsolidity/ast/Types.h> #include <libsolidity/ast/ASTAnnotations.h> @@ -56,7 +58,9 @@ public: m_runtimeSub = size_t(m_asm->newSub(m_runtimeContext->m_asm).data()); } + /// Update currently enabled set of experimental features. void setExperimentalFeatures(std::set<ExperimentalFeature> const& _features) { m_experimentalFeatures = _features; } + /// @returns true if the given feature is enabled. bool experimentalFeatureActive(ExperimentalFeature _feature) const { return m_experimentalFeatures.count(_feature); } void addStateVariable(VariableDeclaration const& _declaration, u256 const& _storageOffset, unsigned _byteOffset); @@ -78,13 +82,15 @@ public: /// @returns the entry label of the given function. Might return an AssemblyItem of type /// UndefinedItem if it does not exist yet. eth::AssemblyItem functionEntryLabelIfExists(Declaration const& _declaration) const; - void setInheritanceHierarchy(std::vector<ContractDefinition const*> const& _hierarchy) { m_inheritanceHierarchy = _hierarchy; } /// @returns the entry label of the given function and takes overrides into account. FunctionDefinition const& resolveVirtualFunction(FunctionDefinition const& _function); /// @returns the function that overrides the given declaration from the most derived class just /// above _base in the current inheritance hierarchy. FunctionDefinition const& superFunction(FunctionDefinition const& _function, ContractDefinition const& _base); + /// @returns the next constructor in the inheritance hierarchy. FunctionDefinition const* nextConstructor(ContractDefinition const& _contract) const; + /// Sets the current inheritance hierarchy from derived to base. + void setInheritanceHierarchy(std::vector<ContractDefinition const*> const& _hierarchy) { m_inheritanceHierarchy = _hierarchy; } /// @returns the next function in the queue of functions that are still to be compiled /// (i.e. that were referenced during compilation but where we did not yet generate code for). @@ -117,6 +123,7 @@ public: ); /// Generates the code for missing low-level functions, i.e. calls the generators passed above. void appendMissingLowLevelFunctions(); + ABIFunctions& abiFunctions() { return m_abiFunctions; } ModifierDefinition const& functionModifier(std::string const& _name) const; /// Returns the distance of the given local variable from the bottom of the stack (of the current function). @@ -152,9 +159,12 @@ public: eth::AssemblyItem pushNewTag() { return m_asm->append(m_asm->newPushTag()).tag(); } /// @returns a new tag without pushing any opcodes or data eth::AssemblyItem newTag() { return m_asm->newTag(); } + /// @returns a new tag identified by name. + eth::AssemblyItem namedTag(std::string const& _name) { return m_asm->namedTag(_name); } /// Adds a subroutine to the code (in the data section) and pushes its size (via a tag) /// on the stack. @returns the pushsub assembly item. eth::AssemblyItem addSubroutine(eth::AssemblyPointer const& _assembly) { return m_asm->appendSubroutine(_assembly); } + /// Pushes the size of the subroutine. void pushSubroutineSize(size_t _subRoutine) { m_asm->pushSubroutineSize(_subRoutine); } /// Pushes the offset of the subroutine. void pushSubroutineOffset(size_t _subRoutine) { m_asm->pushSubroutineOffset(_subRoutine); } @@ -180,15 +190,17 @@ public: /// Appends inline assembly. @a _replacements are string-matching replacements that are performed /// prior to parsing the inline assembly. /// @param _localVariables assigns stack positions to variables with the last one being the stack top + /// @param _system if true, this is a "system-level" assembly where all functions use named labels. void appendInlineAssembly( std::string const& _assembly, std::vector<std::string> const& _localVariables = std::vector<std::string>(), - std::map<std::string, std::string> const& _replacements = std::map<std::string, std::string>{} + bool _system = false ); /// Appends arbitrary data to the end of the bytecode. void appendAuxiliaryData(bytes const& _data) { m_asm->appendAuxiliaryDataToEnd(_data); } + /// Run optimisation step. void optimise(bool _fullOptimsation, unsigned _runs = 200) { m_asm->optimise(_fullOptimsation, true, _runs); } /// @returns the runtime context if in creation mode and runtime context is set, nullptr otherwise. @@ -196,16 +208,22 @@ public: /// @returns the identifier of the runtime subroutine. size_t runtimeSub() const { return m_runtimeSub; } + /// @returns a const reference to the underlying assembly. eth::Assembly const& assembly() const { return *m_asm; } /// @returns non-const reference to the underlying assembly. Should be avoided in favour of /// wrappers in this class. eth::Assembly& nonConstAssembly() { return *m_asm; } /// @arg _sourceCodes is the map of input files to source code strings - /// @arg _inJsonFormat shows whether the out should be in Json format - Json::Value streamAssembly(std::ostream& _stream, StringMap const& _sourceCodes = StringMap(), bool _inJsonFormat = false) const + std::string assemblyString(StringMap const& _sourceCodes = StringMap()) const + { + return m_asm->assemblyString(_sourceCodes); + } + + /// @arg _sourceCodes is the map of input files to source code strings + Json::Value assemblyJSON(StringMap const& _sourceCodes = StringMap()) const { - return m_asm->stream(_stream, "", _sourceCodes, _inJsonFormat); + return m_asm->assemblyJSON(_sourceCodes); } eth::LinkerObject const& assembledObject() const { return m_asm->assemble(); } @@ -287,6 +305,8 @@ private: size_t m_runtimeSub = -1; /// An index of low-level function labels by name. std::map<std::string, eth::AssemblyItem> m_lowLevelFunctions; + /// Container for ABI functions to be generated. + ABIFunctions m_abiFunctions; /// The queue of low-level functions to generate. std::queue<std::tuple<std::string, unsigned, unsigned, std::function<void(CompilerContext&)>>> m_lowLevelFunctionGenerationQueue; }; diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index a0fc5d55..37aa1aea 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -121,7 +121,7 @@ void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBound { if (auto ref = dynamic_cast<ReferenceType const*>(&_type)) { - solAssert(ref->location() == DataLocation::Memory, ""); + solUnimplementedAssert(ref->location() == DataLocation::Memory, ""); storeInMemoryDynamic(IntegerType(256), _padToWordBoundaries); } else if (auto str = dynamic_cast<StringLiteralType const*>(&_type)) @@ -310,18 +310,13 @@ void CompilerUtils::abiEncode( { // stack: <$value0> <$value1> ... <$value(n-1)> <$headStart> - vector<string> variables; - size_t numValues = sizeOnStack(_givenTypes); - for (size_t i = 0; i < numValues; ++i) - variables.push_back("$value" + to_string(i)); - variables.push_back("$headStart"); - - ABIFunctions funs; - string routine = funs.tupleEncoder(_givenTypes, _targetTypes, _encodeAsLibraryTypes); - routine += funs.requestedFunctions(); - m_context.appendInlineAssembly("{" + routine + "}", variables); - // Remove everyhing except for "value0" / the final memory pointer. - popStackSlots(numValues); + auto ret = m_context.pushNewTag(); + moveIntoStack(sizeOnStack(_givenTypes) + 1); + + string encoderName = m_context.abiFunctions().tupleEncoder(_givenTypes, _targetTypes, _encodeAsLibraryTypes); + m_context.appendJumpTo(m_context.namedTag(encoderName)); + m_context.adjustStackOffset(-int(sizeOnStack(_givenTypes)) - 1); + m_context << ret.tag(); } void CompilerUtils::zeroInitialiseMemoryArray(ArrayType const& _type) @@ -829,6 +824,7 @@ void CompilerUtils::convertType( break; } } + // fall-through default: // All other types should not be convertible to non-equal types. solAssert(_typeOnStack == _targetType, "Invalid type conversion requested."); diff --git a/libsolidity/codegen/CompilerUtils.h b/libsolidity/codegen/CompilerUtils.h index 18b70250..5e45699b 100644 --- a/libsolidity/codegen/CompilerUtils.h +++ b/libsolidity/codegen/CompilerUtils.h @@ -38,14 +38,20 @@ public: /// Stores the initial value of the free-memory-pointer at its position; void initialiseFreeMemoryPointer(); /// Copies the free memory pointer to the stack. + /// Stack pre: + /// Stack post: <mem_start> void fetchFreeMemoryPointer(); /// Stores the free memory pointer from the stack. + /// Stack pre: <mem_end> + /// Stack post: void storeFreeMemoryPointer(); /// Allocates a number of bytes in memory as given on the stack. /// Stack pre: <size> /// Stack post: <mem_start> void allocateMemory(); /// Appends code that transforms memptr to (memptr - free_memptr) memptr + /// Stack pre: <mem_end> + /// Stack post: <size> <mem_start> void toSizeAfterFreeMemoryPointer(); /// Loads data from memory to the stack. @@ -105,6 +111,8 @@ public: /// Special case of @a encodeToMemory which assumes that everything is padded to words /// and dynamic data is not copied in place (i.e. a proper ABI encoding). + /// Stack pre: <value0> <value1> ... <valueN-1> <head_start> + /// Stack post: <mem_ptr> void abiEncode( TypePointers const& _givenTypes, TypePointers const& _targetTypes, @@ -185,9 +193,13 @@ public: static unsigned sizeOnStack(std::vector<std::shared_ptr<Type const>> const& _variableTypes); /// Helper function to shift top value on the stack to the left. + /// Stack pre: <value> <shift_by_bits> + /// Stack post: <shifted_value> void leftShiftNumberOnStack(unsigned _bits); /// Helper function to shift top value on the stack to the right. + /// Stack pre: <value> <shift_by_bits> + /// Stack post: <shifted_value> void rightShiftNumberOnStack(unsigned _bits, bool _isSigned = false); /// Appends code that computes tha Keccak-256 hash of the topmost stack element of 32 byte type. diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index e53f1b94..92782b8d 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -39,6 +39,9 @@ using namespace std; using namespace dev; using namespace dev::solidity; +namespace +{ + /** * Simple helper class to ensure that the stack height is the same at certain places in the code. */ @@ -53,6 +56,8 @@ private: unsigned stackHeight; }; +} + void ContractCompiler::compileContract( ContractDefinition const& _contract, std::map<const ContractDefinition*, eth::Assembly const*> const& _contracts @@ -117,6 +122,7 @@ void ContractCompiler::appendCallValueCheck() void ContractCompiler::appendInitAndConstructorCode(ContractDefinition const& _contract) { + CompilerContext::LocationSetter locationSetter(m_context, _contract); // Determine the arguments that are used for the base constructors. std::vector<ContractDefinition const*> const& bases = _contract.annotation().linearizedBaseContracts; for (ContractDefinition const* contract: bases) @@ -169,6 +175,7 @@ size_t ContractCompiler::packIntoContractCreator(ContractDefinition const& _cont appendMissingFunctions(); m_runtimeCompiler->appendMissingFunctions(); + CompilerContext::LocationSetter locationSetter(m_context, _contract); m_context << deployRoutine; solAssert(m_context.runtimeSub() != size_t(-1), "Runtime sub not registered"); @@ -326,7 +333,7 @@ void ContractCompiler::appendCalldataUnpacker(TypePointers const& _typeParameter { // stack: v1 v2 ... v(k-1) base_offset current_offset TypePointer type = parameterType->decodingType(); - solAssert(type, "No decoding type found."); + solUnimplementedAssert(type, "No decoding type found."); if (type->category() == Type::Category::Array) { auto const& arrayType = dynamic_cast<ArrayType const&>(*type); @@ -887,6 +894,9 @@ void ContractCompiler::appendMissingFunctions() solAssert(m_context.nextFunctionToCompile() != function, "Compiled the wrong function?"); } m_context.appendMissingLowLevelFunctions(); + string abiFunctions = m_context.abiFunctions().requestedFunctions(); + if (!abiFunctions.empty()) + m_context.appendInlineAssembly("{" + move(abiFunctions) + "}", {}, true); } void ContractCompiler::appendModifierOrFunctionCode() diff --git a/libsolidity/codegen/ContractCompiler.h b/libsolidity/codegen/ContractCompiler.h index 38c1e045..7c5ee59f 100644 --- a/libsolidity/codegen/ContractCompiler.h +++ b/libsolidity/codegen/ContractCompiler.h @@ -96,8 +96,8 @@ private: virtual bool visit(IfStatement const& _ifStatement) override; virtual bool visit(WhileStatement const& _whileStatement) override; virtual bool visit(ForStatement const& _forStatement) override; - virtual bool visit(Continue const& _continue) override; - virtual bool visit(Break const& _break) override; + virtual bool visit(Continue const& _continueStatement) override; + virtual bool visit(Break const& _breakStatement) override; virtual bool visit(Return const& _return) override; virtual bool visit(Throw const& _throw) override; virtual bool visit(VariableDeclarationStatement const& _variableDeclarationStatement) override; diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 639bfc32..c94baa10 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -644,8 +644,8 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) strings(), FunctionType::Kind::BareCall, false, - nullptr, StateMutability::NonPayable, + nullptr, true, true ), @@ -1047,6 +1047,7 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) if (!alsoSearchInteger) break; } + // fall-through case Type::Category::Integer: if (member == "balance") { @@ -1067,7 +1068,14 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) solAssert(false, "Invalid member access to integer"); break; case Type::Category::Function: - solAssert(!!_memberAccess.expression().annotation().type->memberType(member), + if (member == "selector") + { + m_context << Instruction::SWAP1 << Instruction::POP; + /// need to store store it as bytes4 + utils().leftShiftNumberOnStack(224); + } + else + solAssert(!!_memberAccess.expression().annotation().type->memberType(member), "Invalid member access to function."); break; case Type::Category::Magic: @@ -1811,7 +1819,7 @@ void ExpressionCompiler::setLValueToStorageItem(Expression const& _expression) setLValue<StorageItem>(_expression, *_expression.annotation().type); } -bool ExpressionCompiler::cleanupNeededForOp(Type::Category _type, Token::Value _op) const +bool ExpressionCompiler::cleanupNeededForOp(Type::Category _type, Token::Value _op) { if (Token::isCompareOp(_op) || Token::isShiftOp(_op)) return true; diff --git a/libsolidity/codegen/ExpressionCompiler.h b/libsolidity/codegen/ExpressionCompiler.h index 5f6c3d64..cdfa096e 100644 --- a/libsolidity/codegen/ExpressionCompiler.h +++ b/libsolidity/codegen/ExpressionCompiler.h @@ -119,7 +119,7 @@ private: /// @returns true if the operator applied to the given type requires a cleanup prior to the /// operation. - bool cleanupNeededForOp(Type::Category _type, Token::Value _op) const; + static bool cleanupNeededForOp(Type::Category _type, Token::Value _op); /// @returns the CompilerUtils object containing the current context. CompilerUtils utils(); |