diff options
author | chriseth <chris@ethereum.org> | 2019-01-22 20:49:41 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-01-22 20:49:41 +0800 |
commit | 10d17f245839f208ec5085309022a32cd2502f55 (patch) | |
tree | b2c9f68980d0d418cd6f511e9f3f3f71369abe25 /libsolidity/codegen | |
parent | 1df8f40cd2fd7b47698d847907b8ca7b47eb488d (diff) | |
parent | 0ecafe032a84cb6960545dd7f18733430c1f782d (diff) | |
download | dexon-solidity-10d17f245839f208ec5085309022a32cd2502f55.tar.gz dexon-solidity-10d17f245839f208ec5085309022a32cd2502f55.tar.zst dexon-solidity-10d17f245839f208ec5085309022a32cd2502f55.zip |
Merge pull request #5836 from ethereum/develop
Merge develop into release for 0.5.3.
Diffstat (limited to 'libsolidity/codegen')
-rw-r--r-- | libsolidity/codegen/ABIFunctions.cpp | 101 | ||||
-rw-r--r-- | libsolidity/codegen/ABIFunctions.h | 35 | ||||
-rw-r--r-- | libsolidity/codegen/Compiler.cpp | 12 | ||||
-rw-r--r-- | libsolidity/codegen/Compiler.h | 6 | ||||
-rw-r--r-- | libsolidity/codegen/CompilerContext.cpp | 15 | ||||
-rw-r--r-- | libsolidity/codegen/CompilerContext.h | 16 | ||||
-rw-r--r-- | libsolidity/codegen/CompilerUtils.cpp | 23 | ||||
-rw-r--r-- | libsolidity/codegen/CompilerUtils.h | 7 | ||||
-rw-r--r-- | libsolidity/codegen/ContractCompiler.cpp | 14 | ||||
-rw-r--r-- | libsolidity/codegen/ContractCompiler.h | 6 | ||||
-rw-r--r-- | libsolidity/codegen/ExpressionCompiler.cpp | 38 |
11 files changed, 183 insertions, 90 deletions
diff --git a/libsolidity/codegen/ABIFunctions.cpp b/libsolidity/codegen/ABIFunctions.cpp index c1ab03e3..c0fa81ce 100644 --- a/libsolidity/codegen/ABIFunctions.cpp +++ b/libsolidity/codegen/ABIFunctions.cpp @@ -39,14 +39,19 @@ string ABIFunctions::tupleEncoder( bool _encodeAsLibraryTypes ) { + EncodingOptions options; + options.encodeAsLibraryTypes = _encodeAsLibraryTypes; + options.encodeFunctionFromStack = true; + options.padded = true; + options.dynamicInplace = false; + 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"; + functionName += options.toFunctionNameSuffix(); return createExternallyUsedFunction(functionName, [&]() { solAssert(!_givenTypes.empty(), ""); @@ -90,7 +95,7 @@ string ABIFunctions::tupleEncoder( ); elementTempl("values", valueNames); elementTempl("pos", to_string(headPos)); - elementTempl("abiEncode", abiEncodingFunction(*_givenTypes[i], *_targetTypes[i], _encodeAsLibraryTypes, true)); + elementTempl("abiEncode", abiEncodingFunction(*_givenTypes[i], *_targetTypes[i], options)); encodeElements += elementTempl.render(); headPos += dynamic ? 0x20 : _targetTypes[i]->calldataEncodedSize(); } @@ -184,6 +189,20 @@ pair<string, set<string>> ABIFunctions::requestedFunctions() return make_pair(result, std::move(m_externallyUsedFunctions)); } +string ABIFunctions::EncodingOptions::toFunctionNameSuffix() const +{ + string suffix; + if (!padded) + suffix += "_nonPadded"; + if (dynamicInplace) + suffix += "_inplace"; + if (encodeFunctionFromStack) + suffix += "_fromStack"; + if (encodeAsLibraryTypes) + suffix += "_library"; + return suffix; +} + string ABIFunctions::cleanupFunction(Type const& _type, bool _revertOnFailure) { string functionName = string("cleanup_") + (_revertOnFailure ? "revert_" : "assert_") + _type.identifier(); @@ -490,32 +509,31 @@ string ABIFunctions::splitExternalFunctionIdFunction() string ABIFunctions::abiEncodingFunction( Type const& _from, Type const& _to, - bool _encodeAsLibraryTypes, - bool _fromStack + EncodingOptions const& _options ) { - TypePointer toInterface = _to.fullEncodingType(_encodeAsLibraryTypes, true, false); + TypePointer toInterface = _to.fullEncodingType(_options.encodeAsLibraryTypes, true, false); solUnimplementedAssert(toInterface, "Encoding type \"" + _to.toString() + "\" not yet implemented."); Type const& to = *toInterface; if (_from.category() == Type::Category::StringLiteral) - return abiEncodingFunctionStringLiteral(_from, to, _encodeAsLibraryTypes); + return abiEncodingFunctionStringLiteral(_from, to, _options); else if (auto toArray = dynamic_cast<ArrayType const*>(&to)) { solAssert(_from.category() == Type::Category::Array, ""); solAssert(to.dataStoredIn(DataLocation::Memory), ""); ArrayType const& fromArray = dynamic_cast<ArrayType const&>(_from); if (fromArray.location() == DataLocation::CallData) - return abiEncodingFunctionCalldataArray(fromArray, *toArray, _encodeAsLibraryTypes); + return abiEncodingFunctionCalldataArray(fromArray, *toArray, _options); else if (!fromArray.isByteArray() && ( fromArray.location() == DataLocation::Memory || fromArray.baseType()->storageBytes() > 16 )) - return abiEncodingFunctionSimpleArray(fromArray, *toArray, _encodeAsLibraryTypes); + return abiEncodingFunctionSimpleArray(fromArray, *toArray, _options); else if (fromArray.location() == DataLocation::Memory) - return abiEncodingFunctionMemoryByteArray(fromArray, *toArray, _encodeAsLibraryTypes); + return abiEncodingFunctionMemoryByteArray(fromArray, *toArray, _options); else if (fromArray.location() == DataLocation::Storage) - return abiEncodingFunctionCompactStorageArray(fromArray, *toArray, _encodeAsLibraryTypes); + return abiEncodingFunctionCompactStorageArray(fromArray, *toArray, _options); else solAssert(false, ""); } @@ -523,14 +541,13 @@ string ABIFunctions::abiEncodingFunction( { StructType const* fromStruct = dynamic_cast<StructType const*>(&_from); solAssert(fromStruct, ""); - return abiEncodingFunctionStruct(*fromStruct, *toStruct, _encodeAsLibraryTypes); + return abiEncodingFunctionStruct(*fromStruct, *toStruct, _options); } else if (_from.category() == Type::Category::Function) return abiEncodingFunctionFunctionType( dynamic_cast<FunctionType const&>(_from), to, - _encodeAsLibraryTypes, - _fromStack + _options ); solAssert(_from.sizeOnStack() == 1, ""); @@ -541,7 +558,7 @@ string ABIFunctions::abiEncodingFunction( _from.identifier() + "_to_" + to.identifier() + - (_encodeAsLibraryTypes ? "_library" : ""); + _options.toFunctionNameSuffix(); return createFunction(functionName, [&]() { solAssert(!to.isDynamicallyEncoded(), ""); @@ -556,7 +573,7 @@ string ABIFunctions::abiEncodingFunction( { // special case: convert storage reference type to value type - this is only // possible for library calls where we just forward the storage reference - solAssert(_encodeAsLibraryTypes, ""); + solAssert(_options.encodeAsLibraryTypes, ""); solAssert(to == IntegerType::uint256(), ""); templ("cleanupConvert", "value"); } @@ -574,7 +591,7 @@ string ABIFunctions::abiEncodingFunction( string ABIFunctions::abiEncodingFunctionCalldataArray( Type const& _from, Type const& _to, - bool _encodeAsLibraryTypes + EncodingOptions const& _options ) { solAssert(_to.isDynamicallySized(), ""); @@ -596,7 +613,7 @@ string ABIFunctions::abiEncodingFunctionCalldataArray( _from.identifier() + "_to_" + _to.identifier() + - (_encodeAsLibraryTypes ? "_library" : ""); + _options.toFunctionNameSuffix(); return createFunction(functionName, [&]() { solUnimplementedAssert(fromArrayType.isByteArray(), "Only byte arrays can be encoded from calldata currently."); // TODO if this is not a byte array, we might just copy byte-by-byte anyway, @@ -622,7 +639,7 @@ string ABIFunctions::abiEncodingFunctionCalldataArray( string ABIFunctions::abiEncodingFunctionSimpleArray( ArrayType const& _from, ArrayType const& _to, - bool _encodeAsLibraryTypes + EncodingOptions const& _options ) { string functionName = @@ -630,7 +647,7 @@ string ABIFunctions::abiEncodingFunctionSimpleArray( _from.identifier() + "_to_" + _to.identifier() + - (_encodeAsLibraryTypes ? "_library" : ""); + _options.toFunctionNameSuffix(); solAssert(_from.isDynamicallySized() == _to.isDynamicallySized(), ""); solAssert(_from.length() == _to.length(), ""); @@ -691,11 +708,13 @@ string ABIFunctions::abiEncodingFunctionSimpleArray( templ("storeLength", ""); templ("dataAreaFun", arrayDataAreaFunction(_from)); templ("elementEncodedSize", toCompactHexWithPrefix(_to.baseType()->calldataEncodedSize())); + + EncodingOptions subOptions(_options); + subOptions.encodeFunctionFromStack = false; templ("encodeToMemoryFun", abiEncodingFunction( *_from.baseType(), *_to.baseType(), - _encodeAsLibraryTypes, - false + subOptions )); templ("arrayElementAccess", inMemory ? "mload(srcPtr)" : _from.baseType()->isValueType() ? "sload(srcPtr)" : "srcPtr" ); templ("nextArrayElement", nextArrayElementFunction(_from)); @@ -706,7 +725,7 @@ string ABIFunctions::abiEncodingFunctionSimpleArray( string ABIFunctions::abiEncodingFunctionMemoryByteArray( ArrayType const& _from, ArrayType const& _to, - bool _encodeAsLibraryTypes + EncodingOptions const& _options ) { string functionName = @@ -714,7 +733,7 @@ string ABIFunctions::abiEncodingFunctionMemoryByteArray( _from.identifier() + "_to_" + _to.identifier() + - (_encodeAsLibraryTypes ? "_library" : ""); + _options.toFunctionNameSuffix(); solAssert(_from.isDynamicallySized() == _to.isDynamicallySized(), ""); solAssert(_from.length() == _to.length(), ""); @@ -742,7 +761,7 @@ string ABIFunctions::abiEncodingFunctionMemoryByteArray( string ABIFunctions::abiEncodingFunctionCompactStorageArray( ArrayType const& _from, ArrayType const& _to, - bool _encodeAsLibraryTypes + EncodingOptions const& _options ) { string functionName = @@ -750,7 +769,7 @@ string ABIFunctions::abiEncodingFunctionCompactStorageArray( _from.identifier() + "_to_" + _to.identifier() + - (_encodeAsLibraryTypes ? "_library" : ""); + _options.toFunctionNameSuffix(); solAssert(_from.isDynamicallySized() == _to.isDynamicallySized(), ""); solAssert(_from.length() == _to.length(), ""); @@ -840,11 +859,13 @@ string ABIFunctions::abiEncodingFunctionCompactStorageArray( templ("itemsPerSlot", to_string(itemsPerSlot)); string elementEncodedSize = toCompactHexWithPrefix(_to.baseType()->calldataEncodedSize()); templ("elementEncodedSize", elementEncodedSize); + + EncodingOptions subOptions(_options); + subOptions.encodeFunctionFromStack = false; string encodeToMemoryFun = abiEncodingFunction( *_from.baseType(), *_to.baseType(), - _encodeAsLibraryTypes, - false + subOptions ); templ("encodeToMemoryFun", encodeToMemoryFun); std::vector<std::map<std::string, std::string>> items(itemsPerSlot); @@ -859,7 +880,7 @@ string ABIFunctions::abiEncodingFunctionCompactStorageArray( string ABIFunctions::abiEncodingFunctionStruct( StructType const& _from, StructType const& _to, - bool _encodeAsLibraryTypes + EncodingOptions const& _options ) { string functionName = @@ -867,7 +888,7 @@ string ABIFunctions::abiEncodingFunctionStruct( _from.identifier() + "_to_" + _to.identifier() + - (_encodeAsLibraryTypes ? "_library" : ""); + _options.toFunctionNameSuffix(); solUnimplementedAssert(!_from.dataStoredIn(DataLocation::CallData), "Encoding struct from calldata is not yet supported."); solAssert(&_from.structDefinition() == &_to.structDefinition(), ""); @@ -904,7 +925,7 @@ string ABIFunctions::abiEncodingFunctionStruct( solAssert(member.type, ""); if (!member.type->canLiveOutsideStorage()) continue; - TypePointer memberTypeTo = member.type->fullEncodingType(_encodeAsLibraryTypes, true, false); + TypePointer memberTypeTo = member.type->fullEncodingType(_options.encodeAsLibraryTypes, true, false); solUnimplementedAssert(memberTypeTo, "Encoding type \"" + member.type->toString() + "\" not yet implemented."); auto memberTypeFrom = _from.memberType(member.name); solAssert(memberTypeFrom, ""); @@ -958,7 +979,10 @@ string ABIFunctions::abiEncodingFunctionStruct( } memberTempl("encodingOffset", toCompactHexWithPrefix(encodingOffset)); encodingOffset += dynamicMember ? 0x20 : memberTypeTo->calldataEncodedSize(); - memberTempl("abiEncode", abiEncodingFunction(*memberTypeFrom, *memberTypeTo, _encodeAsLibraryTypes, false)); + + EncodingOptions subOptions(_options); + subOptions.encodeFunctionFromStack = false; + memberTempl("abiEncode", abiEncodingFunction(*memberTypeFrom, *memberTypeTo, subOptions)); members.push_back({}); members.back()["encode"] = memberTempl.render(); @@ -973,7 +997,7 @@ string ABIFunctions::abiEncodingFunctionStruct( string ABIFunctions::abiEncodingFunctionStringLiteral( Type const& _from, Type const& _to, - bool _encodeAsLibraryTypes + EncodingOptions const& _options ) { solAssert(_from.category() == Type::Category::StringLiteral, ""); @@ -983,7 +1007,7 @@ string ABIFunctions::abiEncodingFunctionStringLiteral( _from.identifier() + "_to_" + _to.identifier() + - (_encodeAsLibraryTypes ? "_library" : ""); + _options.toFunctionNameSuffix(); return createFunction(functionName, [&]() { auto const& strType = dynamic_cast<StringLiteralType const&>(_from); string const& value = strType.value(); @@ -1034,8 +1058,7 @@ string ABIFunctions::abiEncodingFunctionStringLiteral( string ABIFunctions::abiEncodingFunctionFunctionType( FunctionType const& _from, Type const& _to, - bool _encodeAsLibraryTypes, - bool _fromStack + EncodingOptions const& _options ) { solAssert(_from.kind() == FunctionType::Kind::External, ""); @@ -1046,10 +1069,9 @@ string ABIFunctions::abiEncodingFunctionFunctionType( _from.identifier() + "_to_" + _to.identifier() + - (_fromStack ? "_fromStack" : "") + - (_encodeAsLibraryTypes ? "_library" : ""); + _options.toFunctionNameSuffix(); - if (_fromStack) + if (_options.encodeFunctionFromStack) return createFunction(functionName, [&]() { return Whiskers(R"( function <functionName>(addr, function_id, pos) { @@ -1716,3 +1738,4 @@ size_t ABIFunctions::headSize(TypePointers const& _targetTypes) return headSize; } + diff --git a/libsolidity/codegen/ABIFunctions.h b/libsolidity/codegen/ABIFunctions.h index 1e0cf7fa..c5443236 100644 --- a/libsolidity/codegen/ABIFunctions.h +++ b/libsolidity/codegen/ABIFunctions.h @@ -87,6 +87,23 @@ public: std::pair<std::string, std::set<std::string>> requestedFunctions(); private: + struct EncodingOptions + { + /// Pad/signextend value types and bytes/string to multiples of 32 bytes. + bool padded = true; + /// Store arrays and structs in place without "data pointer" and do not store the length. + bool dynamicInplace = false; + /// Only for external function types: The value is a pair of address / function id instead + /// of a memory pointer to the compression representation. + bool encodeFunctionFromStack = false; + /// Encode storage pointers as storage pointers (we are targeting a library call). + bool encodeAsLibraryTypes = false; + + /// @returns a string to uniquely identify the encoding options for the encoding + /// function name. Skips everything that has its default value. + std::string toFunctionNameSuffix() const; + }; + /// @returns the name of the cleanup function for the given type and /// adds its implementation to the requested functions. /// @param _revertOnFailure if true, causes revert on invalid data, @@ -115,40 +132,39 @@ private: std::string abiEncodingFunction( Type const& _givenType, Type const& _targetType, - bool _encodeAsLibraryTypes, - bool _fromStack + EncodingOptions const& _options ); /// Part of @a abiEncodingFunction for array target type and given calldata array. std::string abiEncodingFunctionCalldataArray( Type const& _givenType, Type const& _targetType, - bool _encodeAsLibraryTypes + EncodingOptions const& _options ); /// Part of @a abiEncodingFunction for array target type and given memory array or /// a given storage array with one item per slot. std::string abiEncodingFunctionSimpleArray( ArrayType const& _givenType, ArrayType const& _targetType, - bool _encodeAsLibraryTypes + EncodingOptions const& _options ); std::string abiEncodingFunctionMemoryByteArray( ArrayType const& _givenType, ArrayType const& _targetType, - bool _encodeAsLibraryTypes + EncodingOptions const& _options ); /// Part of @a abiEncodingFunction for array target type and given storage array /// where multiple items are packed into the same storage slot. std::string abiEncodingFunctionCompactStorageArray( ArrayType const& _givenType, ArrayType const& _targetType, - bool _encodeAsLibraryTypes + EncodingOptions const& _options ); /// Part of @a abiEncodingFunction for struct types. std::string abiEncodingFunctionStruct( StructType const& _givenType, StructType const& _targetType, - bool _encodeAsLibraryTypes + EncodingOptions const& _options ); // @returns the name of the ABI encoding function with the given type @@ -157,14 +173,13 @@ private: std::string abiEncodingFunctionStringLiteral( Type const& _givenType, Type const& _targetType, - bool _encodeAsLibraryTypes + EncodingOptions const& _options ); std::string abiEncodingFunctionFunctionType( FunctionType const& _from, Type const& _to, - bool _encodeAsLibraryTypes, - bool _fromStack + EncodingOptions const& _options ); /// @returns the name of the ABI decoding function for the given type diff --git a/libsolidity/codegen/Compiler.cpp b/libsolidity/codegen/Compiler.cpp index a22e6e9d..72efed33 100644 --- a/libsolidity/codegen/Compiler.cpp +++ b/libsolidity/codegen/Compiler.cpp @@ -31,22 +31,28 @@ using namespace dev::solidity; void Compiler::compileContract( ContractDefinition const& _contract, - std::map<const ContractDefinition*, eth::Assembly const*> const& _contracts, + std::map<ContractDefinition const*, shared_ptr<Compiler const>> const& _otherCompilers, bytes const& _metadata ) { ContractCompiler runtimeCompiler(nullptr, m_runtimeContext, m_optimize, m_optimizeRuns); - runtimeCompiler.compileContract(_contract, _contracts); + runtimeCompiler.compileContract(_contract, _otherCompilers); m_runtimeContext.appendAuxiliaryData(_metadata); // This might modify m_runtimeContext because it can access runtime functions at // creation time. ContractCompiler creationCompiler(&runtimeCompiler, m_context, m_optimize, 1); - m_runtimeSub = creationCompiler.compileConstructor(_contract, _contracts); + m_runtimeSub = creationCompiler.compileConstructor(_contract, _otherCompilers); m_context.optimise(m_optimize, m_optimizeRuns); } +std::shared_ptr<eth::Assembly> Compiler::runtimeAssemblyPtr() const +{ + solAssert(m_context.runtimeContext(), ""); + return m_context.runtimeContext()->assemblyPtr(); +} + eth::AssemblyItem Compiler::functionEntryLabel(FunctionDefinition const& _function) const { return m_runtimeContext.functionEntryLabelIfExists(_function); diff --git a/libsolidity/codegen/Compiler.h b/libsolidity/codegen/Compiler.h index 784d7f8c..c21de96d 100644 --- a/libsolidity/codegen/Compiler.h +++ b/libsolidity/codegen/Compiler.h @@ -45,11 +45,15 @@ public: /// @arg _metadata contains the to be injected metadata CBOR void compileContract( ContractDefinition const& _contract, - std::map<ContractDefinition const*, eth::Assembly const*> const& _contracts, + std::map<ContractDefinition const*, std::shared_ptr<Compiler const>> const& _otherCompilers, bytes const& _metadata ); /// @returns Entire assembly. eth::Assembly const& assembly() const { return m_context.assembly(); } + /// @returns Entire assembly as a shared pointer to non-const. + std::shared_ptr<eth::Assembly> assemblyPtr() const { return m_context.assemblyPtr(); } + /// @returns Runtime assembly. + std::shared_ptr<eth::Assembly> runtimeAssemblyPtr() const; /// @returns The entire assembled object (with constructor). eth::LinkerObject assembledObject() const { return m_context.assembledObject(); } /// @returns Only the runtime object (without constructor). diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index be681b2e..861b1c98 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -167,11 +167,18 @@ unsigned CompilerContext::numberOfLocalVariables() const return m_localVariables.size(); } -eth::Assembly const& CompilerContext::compiledContract(const ContractDefinition& _contract) const +shared_ptr<eth::Assembly> CompilerContext::compiledContract(ContractDefinition const& _contract) const { - auto ret = m_compiledContracts.find(&_contract); - solAssert(ret != m_compiledContracts.end(), "Compiled contract not found."); - return *ret->second; + auto ret = m_otherCompilers.find(&_contract); + solAssert(ret != m_otherCompilers.end(), "Compiled contract not found."); + return ret->second->assemblyPtr(); +} + +shared_ptr<eth::Assembly> CompilerContext::compiledContractRuntime(ContractDefinition const& _contract) const +{ + auto ret = m_otherCompilers.find(&_contract); + solAssert(ret != m_otherCompilers.end(), "Compiled contract not found."); + return ret->second->runtimeAssemblyPtr(); } bool CompilerContext::isLocalVariable(Declaration const* _declaration) const diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h index dedcd95f..e5ddfbc5 100644 --- a/libsolidity/codegen/CompilerContext.h +++ b/libsolidity/codegen/CompilerContext.h @@ -41,6 +41,7 @@ namespace dev { namespace solidity { +class Compiler; /** * Context to be shared by all units that compile the same contract. @@ -74,8 +75,9 @@ public: /// Returns the number of currently allocated local variables. unsigned numberOfLocalVariables() const; - void setCompiledContracts(std::map<ContractDefinition const*, eth::Assembly const*> const& _contracts) { m_compiledContracts = _contracts; } - eth::Assembly const& compiledContract(ContractDefinition const& _contract) const; + void setOtherCompilers(std::map<ContractDefinition const*, std::shared_ptr<Compiler const>> const& _otherCompilers) { m_otherCompilers = _otherCompilers; } + std::shared_ptr<eth::Assembly> compiledContract(ContractDefinition const& _contract) const; + std::shared_ptr<eth::Assembly> compiledContractRuntime(ContractDefinition const& _contract) const; void setStackOffset(int _offset) { m_asm->setDeposit(_offset); } void adjustStackOffset(int _adjustment) { m_asm->adjustDeposit(_adjustment); } @@ -222,15 +224,15 @@ public: void optimise(bool _fullOptimsation, unsigned _runs = 200) { m_asm->optimise(_fullOptimsation, m_evmVersion, true, _runs); } /// @returns the runtime context if in creation mode and runtime context is set, nullptr otherwise. - CompilerContext* runtimeContext() { return m_runtimeContext; } + CompilerContext* runtimeContext() const { return m_runtimeContext; } /// @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; } + /// @returns a shared pointer to the assembly. + /// Should be avoided except when adding sub-assemblies. + std::shared_ptr<eth::Assembly> assemblyPtr() const { return m_asm; } /// @arg _sourceCodes is the map of input files to source code strings std::string assemblyString(StringMap const& _sourceCodes = StringMap()) const @@ -307,7 +309,7 @@ private: /// Activated experimental features. std::set<ExperimentalFeature> m_experimentalFeatures; /// Other already compiled contracts to be used in contract creation calls. - std::map<ContractDefinition const*, eth::Assembly const*> m_compiledContracts; + std::map<ContractDefinition const*, std::shared_ptr<Compiler const>> m_otherCompilers; /// Storage offsets of state variables std::map<Declaration const*, std::pair<u256, unsigned>> m_stateVariables; /// Offsets of local variables on the stack (relative to stack base). diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index bbc703c7..6cfb0777 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -1199,6 +1199,29 @@ void CompilerUtils::computeHashStatic() m_context << u256(32) << u256(0) << Instruction::KECCAK256; } +void CompilerUtils::copyContractCodeToMemory(ContractDefinition const& contract, bool _creation) +{ + string which = _creation ? "Creation" : "Runtime"; + m_context.callLowLevelFunction( + "$copyContract" + which + "CodeToMemory_" + contract.type()->identifier(), + 1, + 1, + [&contract, _creation](CompilerContext& _context) + { + // copy the contract's code into memory + shared_ptr<eth::Assembly> assembly = + _creation ? + _context.compiledContract(contract) : + _context.compiledContractRuntime(contract); + // pushes size + auto subroutine = _context.addSubroutine(assembly); + _context << Instruction::DUP1 << subroutine; + _context << Instruction::DUP4 << Instruction::CODECOPY; + _context << Instruction::ADD; + } + ); +} + void CompilerUtils::storeStringData(bytesConstRef _data) { //@todo provide both alternatives to the optimiser diff --git a/libsolidity/codegen/CompilerUtils.h b/libsolidity/codegen/CompilerUtils.h index 7e4f47ba..6bde2e8b 100644 --- a/libsolidity/codegen/CompilerUtils.h +++ b/libsolidity/codegen/CompilerUtils.h @@ -263,6 +263,13 @@ public: /// Appends code that computes the Keccak-256 hash of the topmost stack element of 32 byte type. void computeHashStatic(); + /// Apppends code that copies the code of the given contract to memory. + /// Stack pre: Memory position + /// Stack post: Updated memory position + /// @param creation if true, copies creation code, if false copies runtime code. + /// @note the contract has to be compiled already, so beware of cyclic dependencies! + void copyContractCodeToMemory(ContractDefinition const& contract, bool _creationCode); + /// Bytes we need to the start of call data. /// - The size in bytes of the function (hash) identifier. static const unsigned dataStartOffset; diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index b051d260..f843e07a 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -60,7 +60,7 @@ private: void ContractCompiler::compileContract( ContractDefinition const& _contract, - std::map<const ContractDefinition*, eth::Assembly const*> const& _contracts + map<ContractDefinition const*, shared_ptr<Compiler const>> const& _otherCompilers ) { CompilerContext::LocationSetter locationSetter(m_context, _contract); @@ -70,7 +70,7 @@ void ContractCompiler::compileContract( // This has to be the first code in the contract. appendDelegatecallCheck(); - initializeContext(_contract, _contracts); + initializeContext(_contract, _otherCompilers); // This generates the dispatch function for externally visible functions // and adds the function to the compilation queue. Additionally internal functions, // which are referenced directly or indirectly will be added. @@ -81,7 +81,7 @@ void ContractCompiler::compileContract( size_t ContractCompiler::compileConstructor( ContractDefinition const& _contract, - std::map<const ContractDefinition*, eth::Assembly const*> const& _contracts + std::map<ContractDefinition const*, shared_ptr<Compiler const>> const& _otherCompilers ) { CompilerContext::LocationSetter locationSetter(m_context, _contract); @@ -89,18 +89,18 @@ size_t ContractCompiler::compileConstructor( return deployLibrary(_contract); else { - initializeContext(_contract, _contracts); + initializeContext(_contract, _otherCompilers); return packIntoContractCreator(_contract); } } void ContractCompiler::initializeContext( ContractDefinition const& _contract, - map<ContractDefinition const*, eth::Assembly const*> const& _compiledContracts + map<ContractDefinition const*, shared_ptr<Compiler const>> const& _otherCompilers ) { m_context.setExperimentalFeatures(_contract.sourceUnit().annotation().experimentalFeatures); - m_context.setCompiledContracts(_compiledContracts); + m_context.setOtherCompilers(_otherCompilers); m_context.setInheritanceHierarchy(_contract.annotation().linearizedBaseContracts); CompilerUtils(m_context).initialiseFreeMemoryPointer(); registerStateVariables(_contract); @@ -716,7 +716,7 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly) CodeGenerator::assemble( _inlineAssembly.operations(), *_inlineAssembly.annotation().analysisInfo, - m_context.nonConstAssembly(), + *m_context.assemblyPtr(), identifierAccess ); m_context.setStackOffset(startStackHeight); diff --git a/libsolidity/codegen/ContractCompiler.h b/libsolidity/codegen/ContractCompiler.h index 40871f0d..9ab006f6 100644 --- a/libsolidity/codegen/ContractCompiler.h +++ b/libsolidity/codegen/ContractCompiler.h @@ -49,13 +49,13 @@ public: void compileContract( ContractDefinition const& _contract, - std::map<ContractDefinition const*, eth::Assembly const*> const& _contracts + std::map<ContractDefinition const*, std::shared_ptr<Compiler const>> const& _otherCompilers ); /// Compiles the constructor part of the contract. /// @returns the identifier of the runtime sub-assembly. size_t compileConstructor( ContractDefinition const& _contract, - std::map<ContractDefinition const*, eth::Assembly const*> const& _contracts + std::map<ContractDefinition const*, std::shared_ptr<Compiler const>> const& _otherCompilers ); private: @@ -63,7 +63,7 @@ private: /// information about the contract like the AST annotations. void initializeContext( ContractDefinition const& _contract, - std::map<ContractDefinition const*, eth::Assembly const*> const& _compiledContracts + std::map<ContractDefinition const*, std::shared_ptr<Compiler const>> const& _otherCompilers ); /// Adds the code that is run at creation time. Should be run after exchanging the run-time context /// with a new and initialized context. Adds the constructor code. diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index be2709ae..e6bb163d 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -594,22 +594,8 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) } ContractDefinition const* contract = &dynamic_cast<ContractType const&>(*function.returnParameterTypes().front()).contractDefinition(); - m_context.callLowLevelFunction( - "$copyContractCreationCodeToMemory_" + contract->type()->identifier(), - 0, - 1, - [contract](CompilerContext& _context) - { - // copy the contract's code into memory - eth::Assembly const& assembly = _context.compiledContract(*contract); - CompilerUtils(_context).fetchFreeMemoryPointer(); - // pushes size - auto subroutine = _context.addSubroutine(make_shared<eth::Assembly>(assembly)); - _context << Instruction::DUP1 << subroutine; - _context << Instruction::DUP4 << Instruction::CODECOPY; - _context << Instruction::ADD; - } - ); + utils().fetchFreeMemoryPointer(); + utils().copyContractCodeToMemory(*contract, true); utils().abiEncode(argumentTypes, function.parameterTypes()); // now on stack: memory_end_ptr // need: size, offset, endowment @@ -1107,6 +1093,9 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) case FunctionType::Kind::GasLeft: m_context << Instruction::GAS; break; + case FunctionType::Kind::MetaType: + // No code to generate. + break; } } return false; @@ -1348,6 +1337,23 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) solAssert(false, "Gas has been removed."); else if (member == "blockhash") solAssert(false, "Blockhash has been removed."); + else if (member == "creationCode" || member == "runtimeCode") + { + TypePointer arg = dynamic_cast<MagicType const&>(*_memberAccess.expression().annotation().type).typeArgument(); + ContractDefinition const& contract = dynamic_cast<ContractType const&>(*arg).contractDefinition(); + utils().fetchFreeMemoryPointer(); + m_context << Instruction::DUP1 << u256(32) << Instruction::ADD; + utils().copyContractCodeToMemory(contract, member == "creationCode"); + // Stack: start end + m_context.appendInlineAssembly( + Whiskers(R"({ + mstore(start, sub(end, add(start, 0x20))) + mstore(<free>, end) + })")("free", to_string(CompilerUtils::freeMemoryPointer)).render(), + {"start", "end"} + ); + m_context << Instruction::POP; + } else solAssert(false, "Unknown magic member."); break; |