aboutsummaryrefslogtreecommitdiffstats
path: root/libsolidity/codegen/ContractCompiler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libsolidity/codegen/ContractCompiler.cpp')
-rw-r--r--libsolidity/codegen/ContractCompiler.cpp71
1 files changed, 68 insertions, 3 deletions
diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp
index a81ba518..f463db94 100644
--- a/libsolidity/codegen/ContractCompiler.cpp
+++ b/libsolidity/codegen/ContractCompiler.cpp
@@ -64,6 +64,12 @@ void ContractCompiler::compileContract(
)
{
CompilerContext::LocationSetter locationSetter(m_context, _contract);
+
+ if (_contract.isLibrary())
+ // Check whether this is a call (true) or a delegatecall (false).
+ // This has to be the first code in the contract.
+ appendDelegatecallCheck();
+
initializeContext(_contract, _contracts);
appendFunctionSelector(_contract);
appendMissingFunctions();
@@ -75,8 +81,13 @@ size_t ContractCompiler::compileConstructor(
)
{
CompilerContext::LocationSetter locationSetter(m_context, _contract);
- initializeContext(_contract, _contracts);
- return packIntoContractCreator(_contract);
+ if (_contract.isLibrary())
+ return deployLibrary(_contract);
+ else
+ {
+ initializeContext(_contract, _contracts);
+ return packIntoContractCreator(_contract);
+ }
}
size_t ContractCompiler::compileClone(
@@ -122,6 +133,7 @@ void ContractCompiler::appendCallValueCheck()
void ContractCompiler::appendInitAndConstructorCode(ContractDefinition const& _contract)
{
+ solAssert(!_contract.isLibrary(), "Tried to initialize library.");
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;
@@ -163,6 +175,7 @@ void ContractCompiler::appendInitAndConstructorCode(ContractDefinition const& _c
size_t ContractCompiler::packIntoContractCreator(ContractDefinition const& _contract)
{
solAssert(!!m_runtimeCompiler, "");
+ solAssert(!_contract.isLibrary(), "Tried to use contract creator or library.");
appendInitAndConstructorCode(_contract);
@@ -188,6 +201,34 @@ size_t ContractCompiler::packIntoContractCreator(ContractDefinition const& _cont
return m_context.runtimeSub();
}
+size_t ContractCompiler::deployLibrary(ContractDefinition const& _contract)
+{
+ solAssert(!!m_runtimeCompiler, "");
+ solAssert(_contract.isLibrary(), "Tried to deploy contract as library.");
+
+ CompilerContext::LocationSetter locationSetter(m_context, _contract);
+
+ solAssert(m_context.runtimeSub() != size_t(-1), "Runtime sub not registered");
+ m_context.pushSubroutineSize(m_context.runtimeSub());
+ m_context.pushSubroutineOffset(m_context.runtimeSub());
+ m_context.appendInlineAssembly(R"(
+ {
+ // If code starts at 11, an mstore(0) writes to the full PUSH20 plus data
+ // without the need for a shift.
+ let codepos := 11
+ codecopy(codepos, subOffset, subSize)
+ // Check that the first opcode is a PUSH20
+ switch eq(0x73, byte(0, mload(codepos)))
+ case 0 { invalid() }
+ mstore(0, address())
+ mstore8(codepos, 0x73)
+ return(codepos, subSize)
+ }
+ )", {"subSize", "subOffset"});
+
+ return m_context.runtimeSub();
+}
+
void ContractCompiler::appendBaseConstructor(FunctionDefinition const& _constructor)
{
CompilerContext::LocationSetter locationSetter(m_context, _constructor);
@@ -244,11 +285,26 @@ void ContractCompiler::appendConstructor(FunctionDefinition const& _constructor)
_constructor.accept(*this);
}
+void ContractCompiler::appendDelegatecallCheck()
+{
+ // Special constant that will be replaced by the address at deploy time.
+ // At compilation time, this is just "PUSH20 00...000".
+ m_context.appendDeployTimeAddress();
+ m_context << Instruction::ADDRESS << Instruction::EQ;
+ // The result on the stack is
+ // "We have not been called via DELEGATECALL".
+}
+
void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contract)
{
map<FixedHash<4>, FunctionTypePointer> interfaceFunctions = _contract.interfaceFunctions();
map<FixedHash<4>, const eth::AssemblyItem> callDataUnpackerEntryPoints;
+ if (_contract.isLibrary())
+ {
+ solAssert(m_context.stackHeight() == 1, "CALL / DELEGATECALL flag expected.");
+ }
+
FunctionDefinition const* fallback = _contract.fallbackFunction();
eth::AssemblyItem notFound = m_context.newTag();
// directly jump to fallback if the data is too short to contain a function selector
@@ -260,7 +316,7 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac
if (!interfaceFunctions.empty())
CompilerUtils(m_context).loadFromMemory(0, IntegerType(CompilerUtils::dataStartOffset * 8), true);
- // stack now is: 1 0 <funhash>
+ // stack now is: <can-call-non-view-functions>? <funhash>
for (auto const& it: interfaceFunctions)
{
callDataUnpackerEntryPoints.insert(std::make_pair(it.first, m_context.newTag()));
@@ -272,6 +328,7 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac
m_context << notFound;
if (fallback)
{
+ solAssert(!_contract.isLibrary(), "");
if (!fallback->isPayable())
appendCallValueCheck();
@@ -291,6 +348,13 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac
CompilerContext::LocationSetter locationSetter(m_context, functionType->declaration());
m_context << callDataUnpackerEntryPoints.at(it.first);
+ if (_contract.isLibrary() && functionType->stateMutability() > StateMutability::View)
+ {
+ // If the function is not a view function and is called without DELEGATECALL,
+ // we revert.
+ m_context << dupInstruction(2);
+ m_context.appendConditionalRevert();
+ }
m_context.setStackOffset(0);
// We have to allow this for libraries, because value of the previous
// call is still visible in the delegatecall.
@@ -441,6 +505,7 @@ void ContractCompiler::registerStateVariables(ContractDefinition const& _contrac
void ContractCompiler::initializeStateVariables(ContractDefinition const& _contract)
{
+ solAssert(!_contract.isLibrary(), "Tried to initialize state variables of library.");
for (VariableDeclaration const* variable: _contract.stateVariables())
if (variable->value() && !variable->isConstant())
ExpressionCompiler(m_context, m_optimise).appendStateVariableInitialization(*variable);