aboutsummaryrefslogtreecommitdiffstats
path: root/libsolidity/codegen
diff options
context:
space:
mode:
Diffstat (limited to 'libsolidity/codegen')
-rw-r--r--libsolidity/codegen/ABIFunctions.cpp232
-rw-r--r--libsolidity/codegen/ABIFunctions.h22
-rw-r--r--libsolidity/codegen/ArrayUtils.cpp6
-rw-r--r--libsolidity/codegen/Compiler.h15
-rw-r--r--libsolidity/codegen/CompilerContext.cpp16
-rw-r--r--libsolidity/codegen/CompilerContext.h30
-rw-r--r--libsolidity/codegen/CompilerUtils.cpp22
-rw-r--r--libsolidity/codegen/CompilerUtils.h12
-rw-r--r--libsolidity/codegen/ContractCompiler.cpp12
-rw-r--r--libsolidity/codegen/ContractCompiler.h4
-rw-r--r--libsolidity/codegen/ExpressionCompiler.cpp14
-rw-r--r--libsolidity/codegen/ExpressionCompiler.h2
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();