diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp index 6b4bb52b4..31857c09a 100644 --- a/libevmasm/Assembly.cpp +++ b/libevmasm/Assembly.cpp @@ -49,6 +49,8 @@ void Assembly::append(Assembly const& _a) } m_deposit = newDeposit; m_usedTags += _a.m_usedTags; + // This does not transfer the names of named tags on purpose. The tags themselves are + // transferred, but their names are only available inside the assembly. for (auto const& i: _a.m_data) m_data.insert(i); for (auto const& i: _a.m_strings) @@ -326,6 +328,14 @@ AssemblyItem const& Assembly::append(AssemblyItem const& _i) return back(); } +AssemblyItem Assembly::namedTag(string const& _name) +{ + assertThrow(!_name.empty(), AssemblyException, ""); + if (!m_namedTags.count(_name)) + m_namedTags[_name] = size_t(newTag().data()); + return AssemblyItem(Tag, m_namedTags.at(_name)); +} + AssemblyItem Assembly::newPushLibraryAddress(string const& _identifier) { h256 h(dev::keccak256(_identifier)); @@ -581,6 +591,7 @@ LinkerObject const& Assembly::assemble() const assertThrow(i.data() != 0, AssemblyException, ""); assertThrow(i.splitForeignPushTag().first == size_t(-1), AssemblyException, "Foreign tag."); assertThrow(ret.bytecode.size() < 0xffffffffL, AssemblyException, "Tag too large."); + assertThrow(m_tagPositionsInBytecode[size_t(i.data())] == size_t(-1), AssemblyException, "Duplicate tag position."); m_tagPositionsInBytecode[size_t(i.data())] = ret.bytecode.size(); ret.bytecode.push_back((byte)Instruction::JUMPDEST); break; diff --git a/libevmasm/Assembly.h b/libevmasm/Assembly.h index cbdd71bcf..885192e49 100644 --- a/libevmasm/Assembly.h +++ b/libevmasm/Assembly.h @@ -47,6 +47,8 @@ public: AssemblyItem newTag() { return AssemblyItem(Tag, m_usedTags++); } AssemblyItem newPushTag() { return AssemblyItem(PushTag, m_usedTags++); } + /// Returns a tag identified by the given name. Creates it if it does not yet exist. + AssemblyItem namedTag(std::string const& _name); AssemblyItem newData(bytes const& _data) { h256 h(dev::keccak256(asString(_data))); m_data[h] = _data; return AssemblyItem(PushData, h); } AssemblyItem newSub(AssemblyPointer const& _sub) { m_subs.push_back(_sub); return AssemblyItem(PushSub, m_subs.size() - 1); } Assembly const& sub(size_t _sub) const { return *m_subs.at(_sub); } @@ -150,6 +152,7 @@ private: protected: /// 0 is reserved for exception unsigned m_usedTags = 1; + std::map m_namedTags; AssemblyItems m_items; std::map m_data; /// Data that is appended to the very end of the contract. diff --git a/libjulia/backends/evm/AbstractAssembly.h b/libjulia/backends/evm/AbstractAssembly.h index cfc9b8a55..8e90a912b 100644 --- a/libjulia/backends/evm/AbstractAssembly.h +++ b/libjulia/backends/evm/AbstractAssembly.h @@ -66,6 +66,8 @@ public: virtual void appendLabelReference(LabelID _labelId) = 0; /// Generate a new unique label. virtual LabelID newLabelId() = 0; + /// Returns a label identified by the given name. Creates it if it does not yet exist. + virtual LabelID namedLabel(std::string const& _name) = 0; /// Append a reference to a to-be-linked symobl. /// Currently, we assume that the value is always a 20 byte number. virtual void appendLinkerSymbol(std::string const& _name) = 0; diff --git a/libjulia/backends/evm/EVMAssembly.cpp b/libjulia/backends/evm/EVMAssembly.cpp index 173d5e934..1d499b200 100644 --- a/libjulia/backends/evm/EVMAssembly.cpp +++ b/libjulia/backends/evm/EVMAssembly.cpp @@ -77,6 +77,14 @@ EVMAssembly::LabelID EVMAssembly::newLabelId() return m_nextLabelId++; } +AbstractAssembly::LabelID EVMAssembly::namedLabel(string const& _name) +{ + solAssert(!_name.empty(), ""); + if (!m_namedLabels.count(_name)) + m_namedLabels[_name] = newLabelId(); + return m_namedLabels[_name]; +} + void EVMAssembly::appendLinkerSymbol(string const&) { solAssert(false, "Linker symbols not yet implemented."); diff --git a/libjulia/backends/evm/EVMAssembly.h b/libjulia/backends/evm/EVMAssembly.h index 695858226..593cee6a2 100644 --- a/libjulia/backends/evm/EVMAssembly.h +++ b/libjulia/backends/evm/EVMAssembly.h @@ -52,6 +52,8 @@ public: virtual void appendLabelReference(LabelID _labelId) override; /// Generate a new unique label. virtual LabelID newLabelId() override; + /// Returns a label identified by the given name. Creates it if it does not yet exist. + virtual LabelID namedLabel(std::string const& _name) override; /// Append a reference to a to-be-linked symobl. /// Currently, we assume that the value is always a 20 byte number. virtual void appendLinkerSymbol(std::string const& _name) override; @@ -85,6 +87,7 @@ private: LabelID m_nextLabelId = 0; int m_stackHeight = 0; bytes m_bytecode; + std::map m_namedLabels; std::map m_labelPositions; std::map m_labelReferences; std::vector m_assemblySizePositions; diff --git a/libjulia/backends/evm/EVMCodeTransform.cpp b/libjulia/backends/evm/EVMCodeTransform.cpp index 704aa3c10..e0b11cf33 100644 --- a/libjulia/backends/evm/EVMCodeTransform.cpp +++ b/libjulia/backends/evm/EVMCodeTransform.cpp @@ -108,10 +108,10 @@ void CodeTransform::operator()(FunctionCall const& _call) visitExpression(arg); m_assembly.setSourceLocation(_call.location); if (m_evm15) - m_assembly.appendJumpsub(functionEntryID(*function), function->arguments.size(), function->returns.size()); + m_assembly.appendJumpsub(functionEntryID(_call.functionName.name, *function), function->arguments.size(), function->returns.size()); else { - m_assembly.appendJumpTo(functionEntryID(*function), function->returns.size() - function->arguments.size() - 1); + m_assembly.appendJumpTo(functionEntryID(_call.functionName.name, *function), function->returns.size() - function->arguments.size() - 1); m_assembly.appendLabel(returnLabel); m_stackAdjustment--; } @@ -286,12 +286,12 @@ void CodeTransform::operator()(FunctionDefinition const& _function) if (m_evm15) { m_assembly.appendJumpTo(afterFunction, -stackHeightBefore); - m_assembly.appendBeginsub(functionEntryID(function), _function.arguments.size()); + m_assembly.appendBeginsub(functionEntryID(_function.name, function), _function.arguments.size()); } else { m_assembly.appendJumpTo(afterFunction, -stackHeightBefore + height); - m_assembly.appendLabel(functionEntryID(function)); + m_assembly.appendLabel(functionEntryID(_function.name, function)); } m_stackAdjustment += localStackAdjustment; @@ -303,8 +303,16 @@ void CodeTransform::operator()(FunctionDefinition const& _function) m_assembly.appendConstant(u256(0)); } - CodeTransform(m_assembly, m_info, m_julia, m_evm15, m_identifierAccess, localStackAdjustment, m_context) - (_function.body); + CodeTransform( + m_assembly, + m_info, + m_julia, + m_evm15, + m_identifierAccess, + m_useNamedLabelsForFunctions, + localStackAdjustment, + m_context + )(_function.body); { // The stack layout here is: @@ -421,10 +429,16 @@ AbstractAssembly::LabelID CodeTransform::labelID(Scope::Label const& _label) return m_context->labelIDs[&_label]; } -AbstractAssembly::LabelID CodeTransform::functionEntryID(Scope::Function const& _function) +AbstractAssembly::LabelID CodeTransform::functionEntryID(string const& _name, Scope::Function const& _function) { if (!m_context->functionEntryIDs.count(&_function)) - m_context->functionEntryIDs[&_function] = m_assembly.newLabelId(); + { + AbstractAssembly::LabelID id = + m_useNamedLabelsForFunctions ? + m_assembly.namedLabel(_name) : + m_assembly.newLabelId(); + m_context->functionEntryIDs[&_function] = id; + } return m_context->functionEntryIDs[&_function]; } diff --git a/libjulia/backends/evm/EVMCodeTransform.h b/libjulia/backends/evm/EVMCodeTransform.h index cd452c5b4..2c0fd10cc 100644 --- a/libjulia/backends/evm/EVMCodeTransform.h +++ b/libjulia/backends/evm/EVMCodeTransform.h @@ -50,13 +50,15 @@ public: solidity::assembly::AsmAnalysisInfo& _analysisInfo, bool _julia = false, bool _evm15 = false, - ExternalIdentifierAccess const& _identifierAccess = ExternalIdentifierAccess() + ExternalIdentifierAccess const& _identifierAccess = ExternalIdentifierAccess(), + bool _useNamedLabelsForFunctions = false ): CodeTransform( _assembly, _analysisInfo, _julia, _evm15, _identifierAccess, + _useNamedLabelsForFunctions, _assembly.stackHeight(), std::make_shared() ) @@ -78,6 +80,7 @@ protected: bool _julia, bool _evm15, ExternalIdentifierAccess const& _identifierAccess, + bool _useNamedLabelsForFunctions, int _stackAdjustment, std::shared_ptr _context ): @@ -85,6 +88,7 @@ protected: m_info(_analysisInfo), m_julia(_julia), m_evm15(_evm15), + m_useNamedLabelsForFunctions(_useNamedLabelsForFunctions), m_identifierAccess(_identifierAccess), m_stackAdjustment(_stackAdjustment), m_context(_context) @@ -110,7 +114,7 @@ private: /// @returns the label ID corresponding to the given label, allocating a new one if /// necessary. AbstractAssembly::LabelID labelID(solidity::assembly::Scope::Label const& _label); - AbstractAssembly::LabelID functionEntryID(solidity::assembly::Scope::Function const& _function); + AbstractAssembly::LabelID functionEntryID(std::string const& _name, solidity::assembly::Scope::Function const& _function); /// Generates code for an expression that is supposed to return a single value. void visitExpression(solidity::assembly::Statement const& _expression); @@ -136,6 +140,7 @@ private: solidity::assembly::Scope* m_scope = nullptr; bool m_julia = false; bool m_evm15 = false; + bool m_useNamedLabelsForFunctions = false; ExternalIdentifierAccess m_identifierAccess; /// Adjustment between the stack height as determined during the analysis phase /// and the stack height in the assembly. This is caused by an initial stack being present diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index ed780d0bd..5a77162ee 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 const& _localVariables, - map 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(CharStream(*assembly), "--CODEGEN--"); + auto scanner = make_shared(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 5116585e7..dd36bba08 100644 --- a/libsolidity/codegen/CompilerContext.h +++ b/libsolidity/codegen/CompilerContext.h @@ -156,6 +156,8 @@ 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); } @@ -185,10 +187,11 @@ 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 const& _localVariables = std::vector(), - std::map const& _replacements = std::map{} + bool _system = false ); /// Appends arbitrary data to the end of the bytecode. diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index 146472f92..3662478db 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -319,7 +319,7 @@ void CompilerUtils::abiEncode( ABIFunctions funs; string routine = funs.tupleEncoder(_givenTypes, _targetTypes, _encodeAsLibraryTypes); routine += funs.requestedFunctions(); - m_context.appendInlineAssembly("{" + routine + "}", variables); + m_context.appendInlineAssembly("{" + routine + "}", variables, true); // Remove everyhing except for "value0" / the final memory pointer. popStackSlots(numValues); } diff --git a/libsolidity/inlineasm/AsmCodeGen.cpp b/libsolidity/inlineasm/AsmCodeGen.cpp index 6d0c02558..dded9f768 100644 --- a/libsolidity/inlineasm/AsmCodeGen.cpp +++ b/libsolidity/inlineasm/AsmCodeGen.cpp @@ -83,6 +83,10 @@ public: { return assemblyTagToIdentifier(m_assembly.newTag()); } + virtual size_t namedLabel(std::string const& _name) override + { + return assemblyTagToIdentifier(m_assembly.namedTag(_name)); + } virtual void appendLinkerSymbol(std::string const& _linkerSymbol) override { m_assembly.appendLibraryAddress(_linkerSymbol); @@ -141,9 +145,17 @@ void assembly::CodeGenerator::assemble( Block const& _parsedData, AsmAnalysisInfo& _analysisInfo, eth::Assembly& _assembly, - julia::ExternalIdentifierAccess const& _identifierAccess + julia::ExternalIdentifierAccess const& _identifierAccess, + bool _useNamedLabelsForFunctions ) { EthAssemblyAdapter assemblyAdapter(_assembly); - julia::CodeTransform(assemblyAdapter, _analysisInfo, false, false, _identifierAccess)(_parsedData); + julia::CodeTransform( + assemblyAdapter, + _analysisInfo, + false, + false, + _identifierAccess, + _useNamedLabelsForFunctions + )(_parsedData); } diff --git a/libsolidity/inlineasm/AsmCodeGen.h b/libsolidity/inlineasm/AsmCodeGen.h index 2a36a590e..a7d7ead1a 100644 --- a/libsolidity/inlineasm/AsmCodeGen.h +++ b/libsolidity/inlineasm/AsmCodeGen.h @@ -46,7 +46,8 @@ public: Block const& _parsedData, AsmAnalysisInfo& _analysisInfo, eth::Assembly& _assembly, - julia::ExternalIdentifierAccess const& _identifierAccess = julia::ExternalIdentifierAccess() + julia::ExternalIdentifierAccess const& _identifierAccess = julia::ExternalIdentifierAccess(), + bool _useNamedLabelsForFunctions = false ); }; diff --git a/test/libsolidity/ABIEncoderTests.cpp b/test/libsolidity/ABIEncoderTests.cpp index 297c4ef08..4ddf17ce7 100644 --- a/test/libsolidity/ABIEncoderTests.cpp +++ b/test/libsolidity/ABIEncoderTests.cpp @@ -398,6 +398,34 @@ BOOST_AUTO_TEST_CASE(calldata) ) } +BOOST_AUTO_TEST_CASE(function_name_collision) +{ + // This tests a collision between a function name used by inline assembly + // and by the ABI encoder + string sourceCode = R"( + contract C { + function f(uint x) returns (uint) { + assembly { + function abi_encode_t_uint256_to_t_uint256() { + mstore(0, 7) + return(0, 0x20) + } + switch x + case 0 { abi_encode_t_uint256_to_t_uint256() } + } + return 1; + } + } + )"; + BOTH_ENCODERS( + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("f(uint256)", encodeArgs(0)) == encodeArgs(7)); + BOOST_CHECK(callContractFunction("f(uint256)", encodeArgs(1)) == encodeArgs(1)); + ) +} + + + BOOST_AUTO_TEST_SUITE_END() }