aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorchriseth <chris@ethereum.org>2019-01-10 23:44:31 +0800
committerchriseth <chris@ethereum.org>2019-01-18 03:36:48 +0800
commite6fee257e68e7b145a47eee8c5937db7a7a99849 (patch)
treed4dce225357536d378d69f1ac87c4c5132d15613
parent2fcfb216b5dcb5cec2d70d2ee7647df47c8166ca (diff)
downloaddexon-solidity-e6fee257e68e7b145a47eee8c5937db7a7a99849.tar.gz
dexon-solidity-e6fee257e68e7b145a47eee8c5937db7a7a99849.tar.zst
dexon-solidity-e6fee257e68e7b145a47eee8c5937db7a7a99849.zip
Code generation for access to contract code.
-rw-r--r--libsolidity/ast/Types.cpp7
-rw-r--r--libsolidity/ast/Types.h2
-rw-r--r--libsolidity/codegen/Compiler.cpp6
-rw-r--r--libsolidity/codegen/Compiler.h4
-rw-r--r--libsolidity/codegen/CompilerContext.cpp15
-rw-r--r--libsolidity/codegen/CompilerContext.h6
-rw-r--r--libsolidity/codegen/CompilerUtils.cpp23
-rw-r--r--libsolidity/codegen/CompilerUtils.h7
-rw-r--r--libsolidity/codegen/ContractCompiler.cpp12
-rw-r--r--libsolidity/codegen/ContractCompiler.h6
-rw-r--r--libsolidity/codegen/ExpressionCompiler.cpp35
-rw-r--r--libsolidity/interface/CompilerStack.cpp14
-rw-r--r--libsolidity/interface/CompilerStack.h6
-rw-r--r--test/libsolidity/Assembly.cpp3
14 files changed, 101 insertions, 45 deletions
diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp
index 20859d28..8f1fe491 100644
--- a/libsolidity/ast/Types.cpp
+++ b/libsolidity/ast/Types.cpp
@@ -3455,3 +3455,10 @@ string MagicType::toString(bool _short) const
solAssert(false, "Unknown kind of magic.");
return {};
}
+
+TypePointer MagicType::typeArgument() const
+{
+ solAssert(m_kind == Kind::MetaType, "");
+ solAssert(m_typeArgument, "");
+ return m_typeArgument;
+}
diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h
index 5427786a..53109de1 100644
--- a/libsolidity/ast/Types.h
+++ b/libsolidity/ast/Types.h
@@ -1335,6 +1335,8 @@ public:
Kind kind() const { return m_kind; }
+ TypePointer typeArgument() const;
+
private:
Kind m_kind;
/// Contract type used for contract metadata magic.
diff --git a/libsolidity/codegen/Compiler.cpp b/libsolidity/codegen/Compiler.cpp
index a22e6e9d..f0eaca4f 100644
--- a/libsolidity/codegen/Compiler.cpp
+++ b/libsolidity/codegen/Compiler.cpp
@@ -31,18 +31,18 @@ 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);
}
diff --git a/libsolidity/codegen/Compiler.h b/libsolidity/codegen/Compiler.h
index 784d7f8c..953267a4 100644
--- a/libsolidity/codegen/Compiler.h
+++ b/libsolidity/codegen/Compiler.h
@@ -45,11 +45,13 @@ 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 Runtime assembly.
+ eth::Assembly const& runtimeAssembly() const { return m_context.assembly().sub(m_runtimeSub); }
/// @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..20e1af7c 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
+eth::Assembly const& 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->assembly();
+}
+
+eth::Assembly const& CompilerContext::compiledContractRuntime(ContractDefinition const& _contract) const
+{
+ auto ret = m_otherCompilers.find(&_contract);
+ solAssert(ret != m_otherCompilers.end(), "Compiled contract not found.");
+ return ret->second->runtimeAssembly();
}
bool CompilerContext::isLocalVariable(Declaration const* _declaration) const
diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h
index dedcd95f..43e1ea77 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; }
+ void setOtherCompilers(std::map<ContractDefinition const*, std::shared_ptr<Compiler const>> const& _otherCompilers) { m_otherCompilers = _otherCompilers; }
eth::Assembly const& compiledContract(ContractDefinition const& _contract) const;
+ eth::Assembly const& compiledContractRuntime(ContractDefinition const& _contract) const;
void setStackOffset(int _offset) { m_asm->setDeposit(_offset); }
void adjustStackOffset(int _adjustment) { m_asm->adjustDeposit(_adjustment); }
@@ -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..9b7244ba 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
+ eth::Assembly const& assembly =
+ _creation ?
+ _context.compiledContract(contract) :
+ _context.compiledContractRuntime(contract);
+ // pushes size
+ auto subroutine = _context.addSubroutine(make_shared<eth::Assembly>(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..a718603b 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);
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 5c2fa6d0..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
@@ -1351,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;
diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp
index f9d889e7..fc33f755 100644
--- a/libsolidity/interface/CompilerStack.cpp
+++ b/libsolidity/interface/CompilerStack.cpp
@@ -343,12 +343,12 @@ bool CompilerStack::compile()
return false;
// Only compile contracts individually which have been requested.
- map<ContractDefinition const*, eth::Assembly const*> compiledContracts;
+ map<ContractDefinition const*, shared_ptr<Compiler const>> otherCompilers;
for (Source const* source: m_sourceOrder)
for (ASTPointer<ASTNode> const& node: source->ast->nodes())
if (auto contract = dynamic_cast<ContractDefinition const*>(node.get()))
if (isRequestedContract(*contract))
- compileContract(*contract, compiledContracts);
+ compileContract(*contract, otherCompilers);
m_stackState = CompilationSuccessful;
this->link();
return true;
@@ -795,19 +795,19 @@ bool onlySafeExperimentalFeaturesActivated(set<ExperimentalFeature> const& featu
void CompilerStack::compileContract(
ContractDefinition const& _contract,
- map<ContractDefinition const*, eth::Assembly const*>& _compiledContracts
+ map<ContractDefinition const*, shared_ptr<Compiler const>>& _otherCompilers
)
{
solAssert(m_stackState >= AnalysisSuccessful, "");
if (
- _compiledContracts.count(&_contract) ||
+ _otherCompilers.count(&_contract) ||
!_contract.annotation().unimplementedFunctions.empty() ||
!_contract.constructorIsPublic()
)
return;
for (auto const* dependency: _contract.annotation().contractDependencies)
- compileContract(*dependency, _compiledContracts);
+ compileContract(*dependency, _otherCompilers);
Contract& compiledContract = m_contracts.at(_contract.fullyQualifiedName());
@@ -825,7 +825,7 @@ void CompilerStack::compileContract(
try
{
// Run optimiser and compile the contract.
- compiler->compileContract(_contract, _compiledContracts, cborEncodedMetadata);
+ compiler->compileContract(_contract, _otherCompilers, cborEncodedMetadata);
}
catch(eth::OptimizerException const&)
{
@@ -852,7 +852,7 @@ void CompilerStack::compileContract(
solAssert(false, "Assembly exception for deployed bytecode");
}
- _compiledContracts[compiledContract.contract] = &compiler->assembly();
+ _otherCompilers[compiledContract.contract] = compiler;
}
CompilerStack::Contract const& CompilerStack::contract(string const& _contractName) const
diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h
index 81d5009f..79927850 100644
--- a/libsolidity/interface/CompilerStack.h
+++ b/libsolidity/interface/CompilerStack.h
@@ -293,10 +293,12 @@ private:
/// @returns true if the contract is requested to be compiled.
bool isRequestedContract(ContractDefinition const& _contract) const;
- /// Compile a single contract and put the result in @a _compiledContracts.
+ /// Compile a single contract.
+ /// @param _otherCompilers provides access to compilers of other contracts, to get
+ /// their bytecode if needed. Only filled after they have been compiled.
void compileContract(
ContractDefinition const& _contract,
- std::map<ContractDefinition const*, eth::Assembly const*>& _compiledContracts
+ std::map<ContractDefinition const*, std::shared_ptr<Compiler const>>& _otherCompilers
);
/// Links all the known library addresses in the available objects. Any unknown
diff --git a/test/libsolidity/Assembly.cpp b/test/libsolidity/Assembly.cpp
index baa9bff1..b5a1797b 100644
--- a/test/libsolidity/Assembly.cpp
+++ b/test/libsolidity/Assembly.cpp
@@ -46,6 +46,7 @@ namespace dev
{
namespace solidity
{
+class Contract;
namespace test
{
@@ -84,7 +85,7 @@ eth::AssemblyItems compileContract(std::shared_ptr<CharStream> _sourceCode)
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
{
Compiler compiler(dev::test::Options::get().evmVersion());
- compiler.compileContract(*contract, map<ContractDefinition const*, Assembly const*>{}, bytes());
+ compiler.compileContract(*contract, map<ContractDefinition const*, shared_ptr<Compiler const>>{}, bytes());
return compiler.runtimeAssemblyItems();
}