mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #2823 from ethereum/iuliaCompressor
Combine all ABI functions into a single assembly.
This commit is contained in:
commit
a0d171722a
@ -3,6 +3,7 @@
|
|||||||
Features:
|
Features:
|
||||||
* Support ``pragma experimental v0.5.0;`` to turn on upcoming breaking changes.
|
* Support ``pragma experimental v0.5.0;`` to turn on upcoming breaking changes.
|
||||||
* Code Generator: Added ``.selector`` member on external function types to retrieve their signature.
|
* Code Generator: Added ``.selector`` member on external function types to retrieve their signature.
|
||||||
|
* Code Generator: Keep a single copy of encoding functions when using the experimental "ABIEncoderV2".
|
||||||
* Optimizer: Add new optimization step to remove unused ``JUMPDEST``s.
|
* Optimizer: Add new optimization step to remove unused ``JUMPDEST``s.
|
||||||
* Syntax Checker: Warn if no visibility is specified on contract functions.
|
* Syntax Checker: Warn if no visibility is specified on contract functions.
|
||||||
* Type Checker: Display helpful warning for unused function arguments/return parameters.
|
* Type Checker: Display helpful warning for unused function arguments/return parameters.
|
||||||
|
@ -49,6 +49,8 @@ void Assembly::append(Assembly const& _a)
|
|||||||
}
|
}
|
||||||
m_deposit = newDeposit;
|
m_deposit = newDeposit;
|
||||||
m_usedTags += _a.m_usedTags;
|
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)
|
for (auto const& i: _a.m_data)
|
||||||
m_data.insert(i);
|
m_data.insert(i);
|
||||||
for (auto const& i: _a.m_strings)
|
for (auto const& i: _a.m_strings)
|
||||||
@ -326,6 +328,14 @@ AssemblyItem const& Assembly::append(AssemblyItem const& _i)
|
|||||||
return back();
|
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)
|
AssemblyItem Assembly::newPushLibraryAddress(string const& _identifier)
|
||||||
{
|
{
|
||||||
h256 h(dev::keccak256(_identifier));
|
h256 h(dev::keccak256(_identifier));
|
||||||
@ -581,6 +591,7 @@ LinkerObject const& Assembly::assemble() const
|
|||||||
assertThrow(i.data() != 0, AssemblyException, "");
|
assertThrow(i.data() != 0, AssemblyException, "");
|
||||||
assertThrow(i.splitForeignPushTag().first == size_t(-1), AssemblyException, "Foreign tag.");
|
assertThrow(i.splitForeignPushTag().first == size_t(-1), AssemblyException, "Foreign tag.");
|
||||||
assertThrow(ret.bytecode.size() < 0xffffffffL, AssemblyException, "Tag too large.");
|
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();
|
m_tagPositionsInBytecode[size_t(i.data())] = ret.bytecode.size();
|
||||||
ret.bytecode.push_back((byte)Instruction::JUMPDEST);
|
ret.bytecode.push_back((byte)Instruction::JUMPDEST);
|
||||||
break;
|
break;
|
||||||
|
@ -47,6 +47,8 @@ public:
|
|||||||
|
|
||||||
AssemblyItem newTag() { return AssemblyItem(Tag, m_usedTags++); }
|
AssemblyItem newTag() { return AssemblyItem(Tag, m_usedTags++); }
|
||||||
AssemblyItem newPushTag() { return AssemblyItem(PushTag, 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 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); }
|
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); }
|
Assembly const& sub(size_t _sub) const { return *m_subs.at(_sub); }
|
||||||
@ -150,6 +152,7 @@ private:
|
|||||||
protected:
|
protected:
|
||||||
/// 0 is reserved for exception
|
/// 0 is reserved for exception
|
||||||
unsigned m_usedTags = 1;
|
unsigned m_usedTags = 1;
|
||||||
|
std::map<std::string, size_t> m_namedTags;
|
||||||
AssemblyItems m_items;
|
AssemblyItems m_items;
|
||||||
std::map<h256, bytes> m_data;
|
std::map<h256, bytes> m_data;
|
||||||
/// Data that is appended to the very end of the contract.
|
/// Data that is appended to the very end of the contract.
|
||||||
|
@ -66,6 +66,8 @@ public:
|
|||||||
virtual void appendLabelReference(LabelID _labelId) = 0;
|
virtual void appendLabelReference(LabelID _labelId) = 0;
|
||||||
/// Generate a new unique label.
|
/// Generate a new unique label.
|
||||||
virtual LabelID newLabelId() = 0;
|
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.
|
/// Append a reference to a to-be-linked symobl.
|
||||||
/// Currently, we assume that the value is always a 20 byte number.
|
/// Currently, we assume that the value is always a 20 byte number.
|
||||||
virtual void appendLinkerSymbol(std::string const& _name) = 0;
|
virtual void appendLinkerSymbol(std::string const& _name) = 0;
|
||||||
|
@ -77,6 +77,14 @@ EVMAssembly::LabelID EVMAssembly::newLabelId()
|
|||||||
return m_nextLabelId++;
|
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&)
|
void EVMAssembly::appendLinkerSymbol(string const&)
|
||||||
{
|
{
|
||||||
solAssert(false, "Linker symbols not yet implemented.");
|
solAssert(false, "Linker symbols not yet implemented.");
|
||||||
|
@ -52,6 +52,8 @@ public:
|
|||||||
virtual void appendLabelReference(LabelID _labelId) override;
|
virtual void appendLabelReference(LabelID _labelId) override;
|
||||||
/// Generate a new unique label.
|
/// Generate a new unique label.
|
||||||
virtual LabelID newLabelId() override;
|
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.
|
/// Append a reference to a to-be-linked symobl.
|
||||||
/// Currently, we assume that the value is always a 20 byte number.
|
/// Currently, we assume that the value is always a 20 byte number.
|
||||||
virtual void appendLinkerSymbol(std::string const& _name) override;
|
virtual void appendLinkerSymbol(std::string const& _name) override;
|
||||||
@ -85,6 +87,7 @@ private:
|
|||||||
LabelID m_nextLabelId = 0;
|
LabelID m_nextLabelId = 0;
|
||||||
int m_stackHeight = 0;
|
int m_stackHeight = 0;
|
||||||
bytes m_bytecode;
|
bytes m_bytecode;
|
||||||
|
std::map<std::string, LabelID> m_namedLabels;
|
||||||
std::map<LabelID, size_t> m_labelPositions;
|
std::map<LabelID, size_t> m_labelPositions;
|
||||||
std::map<size_t, LabelID> m_labelReferences;
|
std::map<size_t, LabelID> m_labelReferences;
|
||||||
std::vector<size_t> m_assemblySizePositions;
|
std::vector<size_t> m_assemblySizePositions;
|
||||||
|
@ -108,10 +108,10 @@ void CodeTransform::operator()(FunctionCall const& _call)
|
|||||||
visitExpression(arg);
|
visitExpression(arg);
|
||||||
m_assembly.setSourceLocation(_call.location);
|
m_assembly.setSourceLocation(_call.location);
|
||||||
if (m_evm15)
|
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
|
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_assembly.appendLabel(returnLabel);
|
||||||
m_stackAdjustment--;
|
m_stackAdjustment--;
|
||||||
}
|
}
|
||||||
@ -286,12 +286,12 @@ void CodeTransform::operator()(FunctionDefinition const& _function)
|
|||||||
if (m_evm15)
|
if (m_evm15)
|
||||||
{
|
{
|
||||||
m_assembly.appendJumpTo(afterFunction, -stackHeightBefore);
|
m_assembly.appendJumpTo(afterFunction, -stackHeightBefore);
|
||||||
m_assembly.appendBeginsub(functionEntryID(function), _function.arguments.size());
|
m_assembly.appendBeginsub(functionEntryID(_function.name, function), _function.arguments.size());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_assembly.appendJumpTo(afterFunction, -stackHeightBefore + height);
|
m_assembly.appendJumpTo(afterFunction, -stackHeightBefore + height);
|
||||||
m_assembly.appendLabel(functionEntryID(function));
|
m_assembly.appendLabel(functionEntryID(_function.name, function));
|
||||||
}
|
}
|
||||||
m_stackAdjustment += localStackAdjustment;
|
m_stackAdjustment += localStackAdjustment;
|
||||||
|
|
||||||
@ -303,8 +303,16 @@ void CodeTransform::operator()(FunctionDefinition const& _function)
|
|||||||
m_assembly.appendConstant(u256(0));
|
m_assembly.appendConstant(u256(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
CodeTransform(m_assembly, m_info, m_julia, m_evm15, m_identifierAccess, localStackAdjustment, m_context)
|
CodeTransform(
|
||||||
(_function.body);
|
m_assembly,
|
||||||
|
m_info,
|
||||||
|
m_julia,
|
||||||
|
m_evm15,
|
||||||
|
m_identifierAccess,
|
||||||
|
m_useNamedLabelsForFunctions,
|
||||||
|
localStackAdjustment,
|
||||||
|
m_context
|
||||||
|
)(_function.body);
|
||||||
|
|
||||||
{
|
{
|
||||||
// The stack layout here is:
|
// The stack layout here is:
|
||||||
@ -421,10 +429,16 @@ AbstractAssembly::LabelID CodeTransform::labelID(Scope::Label const& _label)
|
|||||||
return m_context->labelIDs[&_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))
|
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];
|
return m_context->functionEntryIDs[&_function];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,13 +50,15 @@ public:
|
|||||||
solidity::assembly::AsmAnalysisInfo& _analysisInfo,
|
solidity::assembly::AsmAnalysisInfo& _analysisInfo,
|
||||||
bool _julia = false,
|
bool _julia = false,
|
||||||
bool _evm15 = false,
|
bool _evm15 = false,
|
||||||
ExternalIdentifierAccess const& _identifierAccess = ExternalIdentifierAccess()
|
ExternalIdentifierAccess const& _identifierAccess = ExternalIdentifierAccess(),
|
||||||
|
bool _useNamedLabelsForFunctions = false
|
||||||
): CodeTransform(
|
): CodeTransform(
|
||||||
_assembly,
|
_assembly,
|
||||||
_analysisInfo,
|
_analysisInfo,
|
||||||
_julia,
|
_julia,
|
||||||
_evm15,
|
_evm15,
|
||||||
_identifierAccess,
|
_identifierAccess,
|
||||||
|
_useNamedLabelsForFunctions,
|
||||||
_assembly.stackHeight(),
|
_assembly.stackHeight(),
|
||||||
std::make_shared<Context>()
|
std::make_shared<Context>()
|
||||||
)
|
)
|
||||||
@ -78,6 +80,7 @@ protected:
|
|||||||
bool _julia,
|
bool _julia,
|
||||||
bool _evm15,
|
bool _evm15,
|
||||||
ExternalIdentifierAccess const& _identifierAccess,
|
ExternalIdentifierAccess const& _identifierAccess,
|
||||||
|
bool _useNamedLabelsForFunctions,
|
||||||
int _stackAdjustment,
|
int _stackAdjustment,
|
||||||
std::shared_ptr<Context> _context
|
std::shared_ptr<Context> _context
|
||||||
):
|
):
|
||||||
@ -85,6 +88,7 @@ protected:
|
|||||||
m_info(_analysisInfo),
|
m_info(_analysisInfo),
|
||||||
m_julia(_julia),
|
m_julia(_julia),
|
||||||
m_evm15(_evm15),
|
m_evm15(_evm15),
|
||||||
|
m_useNamedLabelsForFunctions(_useNamedLabelsForFunctions),
|
||||||
m_identifierAccess(_identifierAccess),
|
m_identifierAccess(_identifierAccess),
|
||||||
m_stackAdjustment(_stackAdjustment),
|
m_stackAdjustment(_stackAdjustment),
|
||||||
m_context(_context)
|
m_context(_context)
|
||||||
@ -110,7 +114,7 @@ private:
|
|||||||
/// @returns the label ID corresponding to the given label, allocating a new one if
|
/// @returns the label ID corresponding to the given label, allocating a new one if
|
||||||
/// necessary.
|
/// necessary.
|
||||||
AbstractAssembly::LabelID labelID(solidity::assembly::Scope::Label const& _label);
|
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.
|
/// Generates code for an expression that is supposed to return a single value.
|
||||||
void visitExpression(solidity::assembly::Statement const& _expression);
|
void visitExpression(solidity::assembly::Statement const& _expression);
|
||||||
|
|
||||||
@ -136,6 +140,7 @@ private:
|
|||||||
solidity::assembly::Scope* m_scope = nullptr;
|
solidity::assembly::Scope* m_scope = nullptr;
|
||||||
bool m_julia = false;
|
bool m_julia = false;
|
||||||
bool m_evm15 = false;
|
bool m_evm15 = false;
|
||||||
|
bool m_useNamedLabelsForFunctions = false;
|
||||||
ExternalIdentifierAccess m_identifierAccess;
|
ExternalIdentifierAccess m_identifierAccess;
|
||||||
/// Adjustment between the stack height as determined during the analysis phase
|
/// 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
|
/// and the stack height in the assembly. This is caused by an initial stack being present
|
||||||
|
@ -30,32 +30,35 @@ using namespace std;
|
|||||||
using namespace dev;
|
using namespace dev;
|
||||||
using namespace dev::solidity;
|
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(
|
string ABIFunctions::tupleEncoder(
|
||||||
TypePointers const& _givenTypes,
|
TypePointers const& _givenTypes,
|
||||||
TypePointers const& _targetTypes,
|
TypePointers const& _targetTypes,
|
||||||
bool _encodeAsLibraryTypes
|
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";
|
||||||
|
|
||||||
|
return createFunction(functionName, [&]() {
|
||||||
solAssert(!_givenTypes.empty(), "");
|
solAssert(!_givenTypes.empty(), "");
|
||||||
size_t const headSize_ = headSize(_targetTypes);
|
|
||||||
|
|
||||||
Whiskers encoder(R"(
|
// Note that the values are in reverse due to the difference in calling semantics.
|
||||||
{
|
Whiskers templ(R"(
|
||||||
let tail := add($headStart, <headSize>)
|
function <functionName>(headStart <valueParams>) -> tail {
|
||||||
|
tail := add(headStart, <headSize>)
|
||||||
<encodeElements>
|
<encodeElements>
|
||||||
<deepestStackElement> := tail
|
|
||||||
}
|
}
|
||||||
)");
|
)");
|
||||||
encoder("headSize", to_string(headSize_));
|
templ("functionName", functionName);
|
||||||
|
size_t const headSize_ = headSize(_targetTypes);
|
||||||
|
templ("headSize", to_string(headSize_));
|
||||||
|
string valueParams;
|
||||||
string encodeElements;
|
string encodeElements;
|
||||||
size_t headPos = 0;
|
size_t headPos = 0;
|
||||||
size_t stackPos = 0;
|
size_t stackPos = 0;
|
||||||
@ -66,16 +69,20 @@ string ABIFunctions::tupleEncoder(
|
|||||||
size_t sizeOnStack = _givenTypes[i]->sizeOnStack();
|
size_t sizeOnStack = _givenTypes[i]->sizeOnStack();
|
||||||
string valueNames = "";
|
string valueNames = "";
|
||||||
for (size_t j = 0; j < sizeOnStack; j++)
|
for (size_t j = 0; j < sizeOnStack; j++)
|
||||||
valueNames += "$value" + to_string(stackPos++) + ", ";
|
{
|
||||||
|
valueNames += "value" + to_string(stackPos) + ", ";
|
||||||
|
valueParams = ", value" + to_string(stackPos) + valueParams;
|
||||||
|
stackPos++;
|
||||||
|
}
|
||||||
bool dynamic = _targetTypes[i]->isDynamicallyEncoded();
|
bool dynamic = _targetTypes[i]->isDynamicallyEncoded();
|
||||||
Whiskers elementTempl(
|
Whiskers elementTempl(
|
||||||
dynamic ?
|
dynamic ?
|
||||||
string(R"(
|
string(R"(
|
||||||
mstore(add($headStart, <pos>), sub(tail, $headStart))
|
mstore(add(headStart, <pos>), sub(tail, headStart))
|
||||||
tail := <abiEncode>(<values> tail)
|
tail := <abiEncode>(<values> tail)
|
||||||
)") :
|
)") :
|
||||||
string(R"(
|
string(R"(
|
||||||
<abiEncode>(<values> add($headStart, <pos>))
|
<abiEncode>(<values> add(headStart, <pos>))
|
||||||
)")
|
)")
|
||||||
);
|
);
|
||||||
elementTempl("values", valueNames);
|
elementTempl("values", valueNames);
|
||||||
@ -85,10 +92,11 @@ string ABIFunctions::tupleEncoder(
|
|||||||
headPos += dynamic ? 0x20 : _targetTypes[i]->calldataEncodedSize();
|
headPos += dynamic ? 0x20 : _targetTypes[i]->calldataEncodedSize();
|
||||||
}
|
}
|
||||||
solAssert(headPos == headSize_, "");
|
solAssert(headPos == headSize_, "");
|
||||||
encoder("encodeElements", encodeElements);
|
templ("valueParams", valueParams);
|
||||||
encoder("deepestStackElement", stackPos > 0 ? "$value0" : "$headStart");
|
templ("encodeElements", encodeElements);
|
||||||
|
|
||||||
return encoder.render();
|
return templ.render();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
string ABIFunctions::requestedFunctions()
|
string ABIFunctions::requestedFunctions()
|
||||||
|
@ -44,15 +44,18 @@ using TypePointers = std::vector<TypePointer>;
|
|||||||
/// multiple times.
|
/// multiple times.
|
||||||
///
|
///
|
||||||
/// Make sure to include the result of ``requestedFunctions()`` to a block that
|
/// 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
|
class ABIFunctions
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
~ABIFunctions();
|
/// @returns name of an assembly function to ABI-encode values of @a _givenTypes
|
||||||
|
|
||||||
/// @returns assembly code block to ABI-encode values of @a _givenTypes residing on the stack
|
|
||||||
/// into memory, converting the types to @a _targetTypes on the fly.
|
/// 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
|
/// Does not allocate memory (does not change the memory head pointer), but writes
|
||||||
/// to memory starting at $headStart and an unrestricted amount after that.
|
/// 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)
|
/// Assigns the end of encoded memory either to $value0 or (if that is not present)
|
||||||
@ -63,7 +66,7 @@ public:
|
|||||||
bool _encodeAsLibraryTypes = false
|
bool _encodeAsLibraryTypes = false
|
||||||
);
|
);
|
||||||
|
|
||||||
/// @returns auxiliary functions referenced from the block generated in @a tupleEncoder
|
/// @returns concatenation of all generated functions.
|
||||||
std::string requestedFunctions();
|
std::string requestedFunctions();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -266,19 +266,9 @@ void CompilerContext::resetVisitedNodes(ASTNode const* _node)
|
|||||||
void CompilerContext::appendInlineAssembly(
|
void CompilerContext::appendInlineAssembly(
|
||||||
string const& _assembly,
|
string const& _assembly,
|
||||||
vector<string> const& _localVariables,
|
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();
|
int startStackHeight = stackHeight();
|
||||||
|
|
||||||
julia::ExternalIdentifierAccess identifierAccess;
|
julia::ExternalIdentifierAccess identifierAccess;
|
||||||
@ -320,7 +310,7 @@ void CompilerContext::appendInlineAssembly(
|
|||||||
|
|
||||||
ErrorList errors;
|
ErrorList errors;
|
||||||
ErrorReporter errorReporter(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);
|
auto parserResult = assembly::Parser(errorReporter).parse(scanner);
|
||||||
solAssert(parserResult, "Failed to parse inline assembly block.");
|
solAssert(parserResult, "Failed to parse inline assembly block.");
|
||||||
solAssert(errorReporter.errors().empty(), "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);
|
assembly::AsmAnalyzer analyzer(analysisInfo, errorReporter, false, identifierAccess.resolve);
|
||||||
solAssert(analyzer.analyze(*parserResult), "Failed to analyze inline assembly block.");
|
solAssert(analyzer.analyze(*parserResult), "Failed to analyze inline assembly block.");
|
||||||
solAssert(errorReporter.errors().empty(), "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(
|
FunctionDefinition const& CompilerContext::resolveVirtualFunction(
|
||||||
|
@ -22,6 +22,8 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <libsolidity/codegen/ABIFunctions.h>
|
||||||
|
|
||||||
#include <libsolidity/ast/ASTForward.h>
|
#include <libsolidity/ast/ASTForward.h>
|
||||||
#include <libsolidity/ast/Types.h>
|
#include <libsolidity/ast/Types.h>
|
||||||
#include <libsolidity/ast/ASTAnnotations.h>
|
#include <libsolidity/ast/ASTAnnotations.h>
|
||||||
@ -121,6 +123,7 @@ public:
|
|||||||
);
|
);
|
||||||
/// Generates the code for missing low-level functions, i.e. calls the generators passed above.
|
/// Generates the code for missing low-level functions, i.e. calls the generators passed above.
|
||||||
void appendMissingLowLevelFunctions();
|
void appendMissingLowLevelFunctions();
|
||||||
|
ABIFunctions& abiFunctions() { return m_abiFunctions; }
|
||||||
|
|
||||||
ModifierDefinition const& functionModifier(std::string const& _name) const;
|
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).
|
/// Returns the distance of the given local variable from the bottom of the stack (of the current function).
|
||||||
@ -156,6 +159,8 @@ public:
|
|||||||
eth::AssemblyItem pushNewTag() { return m_asm->append(m_asm->newPushTag()).tag(); }
|
eth::AssemblyItem pushNewTag() { return m_asm->append(m_asm->newPushTag()).tag(); }
|
||||||
/// @returns a new tag without pushing any opcodes or data
|
/// @returns a new tag without pushing any opcodes or data
|
||||||
eth::AssemblyItem newTag() { return m_asm->newTag(); }
|
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)
|
/// 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.
|
/// on the stack. @returns the pushsub assembly item.
|
||||||
eth::AssemblyItem addSubroutine(eth::AssemblyPointer const& _assembly) { return m_asm->appendSubroutine(_assembly); }
|
eth::AssemblyItem addSubroutine(eth::AssemblyPointer const& _assembly) { return m_asm->appendSubroutine(_assembly); }
|
||||||
@ -185,10 +190,11 @@ public:
|
|||||||
/// Appends inline assembly. @a _replacements are string-matching replacements that are performed
|
/// Appends inline assembly. @a _replacements are string-matching replacements that are performed
|
||||||
/// prior to parsing the inline assembly.
|
/// prior to parsing the inline assembly.
|
||||||
/// @param _localVariables assigns stack positions to variables with the last one being the stack top
|
/// @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(
|
void appendInlineAssembly(
|
||||||
std::string const& _assembly,
|
std::string const& _assembly,
|
||||||
std::vector<std::string> const& _localVariables = std::vector<std::string>(),
|
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.
|
/// Appends arbitrary data to the end of the bytecode.
|
||||||
@ -299,6 +305,8 @@ private:
|
|||||||
size_t m_runtimeSub = -1;
|
size_t m_runtimeSub = -1;
|
||||||
/// An index of low-level function labels by name.
|
/// An index of low-level function labels by name.
|
||||||
std::map<std::string, eth::AssemblyItem> m_lowLevelFunctions;
|
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.
|
/// The queue of low-level functions to generate.
|
||||||
std::queue<std::tuple<std::string, unsigned, unsigned, std::function<void(CompilerContext&)>>> m_lowLevelFunctionGenerationQueue;
|
std::queue<std::tuple<std::string, unsigned, unsigned, std::function<void(CompilerContext&)>>> m_lowLevelFunctionGenerationQueue;
|
||||||
};
|
};
|
||||||
|
@ -310,18 +310,13 @@ void CompilerUtils::abiEncode(
|
|||||||
{
|
{
|
||||||
// stack: <$value0> <$value1> ... <$value(n-1)> <$headStart>
|
// stack: <$value0> <$value1> ... <$value(n-1)> <$headStart>
|
||||||
|
|
||||||
vector<string> variables;
|
auto ret = m_context.pushNewTag();
|
||||||
size_t numValues = sizeOnStack(_givenTypes);
|
moveIntoStack(sizeOnStack(_givenTypes) + 1);
|
||||||
for (size_t i = 0; i < numValues; ++i)
|
|
||||||
variables.push_back("$value" + to_string(i));
|
|
||||||
variables.push_back("$headStart");
|
|
||||||
|
|
||||||
ABIFunctions funs;
|
string encoderName = m_context.abiFunctions().tupleEncoder(_givenTypes, _targetTypes, _encodeAsLibraryTypes);
|
||||||
string routine = funs.tupleEncoder(_givenTypes, _targetTypes, _encodeAsLibraryTypes);
|
m_context.appendJumpTo(m_context.namedTag(encoderName));
|
||||||
routine += funs.requestedFunctions();
|
m_context.adjustStackOffset(-int(sizeOnStack(_givenTypes)) - 1);
|
||||||
m_context.appendInlineAssembly("{" + routine + "}", variables);
|
m_context << ret.tag();
|
||||||
// Remove everyhing except for "value0" / the final memory pointer.
|
|
||||||
popStackSlots(numValues);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CompilerUtils::zeroInitialiseMemoryArray(ArrayType const& _type)
|
void CompilerUtils::zeroInitialiseMemoryArray(ArrayType const& _type)
|
||||||
|
@ -122,6 +122,7 @@ void ContractCompiler::appendCallValueCheck()
|
|||||||
|
|
||||||
void ContractCompiler::appendInitAndConstructorCode(ContractDefinition const& _contract)
|
void ContractCompiler::appendInitAndConstructorCode(ContractDefinition const& _contract)
|
||||||
{
|
{
|
||||||
|
CompilerContext::LocationSetter locationSetter(m_context, _contract);
|
||||||
// Determine the arguments that are used for the base constructors.
|
// Determine the arguments that are used for the base constructors.
|
||||||
std::vector<ContractDefinition const*> const& bases = _contract.annotation().linearizedBaseContracts;
|
std::vector<ContractDefinition const*> const& bases = _contract.annotation().linearizedBaseContracts;
|
||||||
for (ContractDefinition const* contract: bases)
|
for (ContractDefinition const* contract: bases)
|
||||||
@ -174,6 +175,7 @@ size_t ContractCompiler::packIntoContractCreator(ContractDefinition const& _cont
|
|||||||
appendMissingFunctions();
|
appendMissingFunctions();
|
||||||
m_runtimeCompiler->appendMissingFunctions();
|
m_runtimeCompiler->appendMissingFunctions();
|
||||||
|
|
||||||
|
CompilerContext::LocationSetter locationSetter(m_context, _contract);
|
||||||
m_context << deployRoutine;
|
m_context << deployRoutine;
|
||||||
|
|
||||||
solAssert(m_context.runtimeSub() != size_t(-1), "Runtime sub not registered");
|
solAssert(m_context.runtimeSub() != size_t(-1), "Runtime sub not registered");
|
||||||
@ -892,6 +894,9 @@ void ContractCompiler::appendMissingFunctions()
|
|||||||
solAssert(m_context.nextFunctionToCompile() != function, "Compiled the wrong function?");
|
solAssert(m_context.nextFunctionToCompile() != function, "Compiled the wrong function?");
|
||||||
}
|
}
|
||||||
m_context.appendMissingLowLevelFunctions();
|
m_context.appendMissingLowLevelFunctions();
|
||||||
|
string abiFunctions = m_context.abiFunctions().requestedFunctions();
|
||||||
|
if (!abiFunctions.empty())
|
||||||
|
m_context.appendInlineAssembly("{" + move(abiFunctions) + "}", {}, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ContractCompiler::appendModifierOrFunctionCode()
|
void ContractCompiler::appendModifierOrFunctionCode()
|
||||||
|
@ -83,6 +83,10 @@ public:
|
|||||||
{
|
{
|
||||||
return assemblyTagToIdentifier(m_assembly.newTag());
|
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
|
virtual void appendLinkerSymbol(std::string const& _linkerSymbol) override
|
||||||
{
|
{
|
||||||
m_assembly.appendLibraryAddress(_linkerSymbol);
|
m_assembly.appendLibraryAddress(_linkerSymbol);
|
||||||
@ -141,9 +145,17 @@ void assembly::CodeGenerator::assemble(
|
|||||||
Block const& _parsedData,
|
Block const& _parsedData,
|
||||||
AsmAnalysisInfo& _analysisInfo,
|
AsmAnalysisInfo& _analysisInfo,
|
||||||
eth::Assembly& _assembly,
|
eth::Assembly& _assembly,
|
||||||
julia::ExternalIdentifierAccess const& _identifierAccess
|
julia::ExternalIdentifierAccess const& _identifierAccess,
|
||||||
|
bool _useNamedLabelsForFunctions
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
EthAssemblyAdapter assemblyAdapter(_assembly);
|
EthAssemblyAdapter assemblyAdapter(_assembly);
|
||||||
julia::CodeTransform(assemblyAdapter, _analysisInfo, false, false, _identifierAccess)(_parsedData);
|
julia::CodeTransform(
|
||||||
|
assemblyAdapter,
|
||||||
|
_analysisInfo,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
_identifierAccess,
|
||||||
|
_useNamedLabelsForFunctions
|
||||||
|
)(_parsedData);
|
||||||
}
|
}
|
||||||
|
@ -46,7 +46,8 @@ public:
|
|||||||
Block const& _parsedData,
|
Block const& _parsedData,
|
||||||
AsmAnalysisInfo& _analysisInfo,
|
AsmAnalysisInfo& _analysisInfo,
|
||||||
eth::Assembly& _assembly,
|
eth::Assembly& _assembly,
|
||||||
julia::ExternalIdentifierAccess const& _identifierAccess = julia::ExternalIdentifierAccess()
|
julia::ExternalIdentifierAccess const& _identifierAccess = julia::ExternalIdentifierAccess(),
|
||||||
|
bool _useNamedLabelsForFunctions = false
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user