mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Move ABI encoder into its own function.
This commit is contained in:
parent
73771f5bb2
commit
80ce3ca66f
@ -3,6 +3,7 @@
|
||||
Features:
|
||||
* 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: Keep a single copy of encoding functions when using the experimental "ABIEncoderV2".
|
||||
* Optimizer: Add new optimization step to remove unused ``JUMPDEST``s.
|
||||
* Syntax Checker: Warn if no visibility is specified on contract functions.
|
||||
* Type Checker: Display helpful warning for unused function arguments/return parameters.
|
||||
|
@ -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()
|
||||
|
@ -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:
|
||||
|
@ -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>
|
||||
@ -121,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).
|
||||
@ -302,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;
|
||||
};
|
||||
|
@ -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");
|
||||
auto ret = m_context.pushNewTag();
|
||||
moveIntoStack(sizeOnStack(_givenTypes) + 1);
|
||||
|
||||
ABIFunctions funs;
|
||||
string routine = funs.tupleEncoder(_givenTypes, _targetTypes, _encodeAsLibraryTypes);
|
||||
routine += funs.requestedFunctions();
|
||||
m_context.appendInlineAssembly("{" + routine + "}", variables, true);
|
||||
// Remove everyhing except for "value0" / the final memory pointer.
|
||||
popStackSlots(numValues);
|
||||
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)
|
||||
|
@ -122,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)
|
||||
@ -174,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");
|
||||
@ -892,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()
|
||||
|
Loading…
Reference in New Issue
Block a user