mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
commit
f676325d60
@ -5,6 +5,7 @@ Features:
|
|||||||
* 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".
|
* 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.
|
||||||
|
* Code Generator: Support passing ``structs`` as arguments and return parameters (requires ``pragma experimental ABIEncoderV2`` for now).
|
||||||
* 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.
|
||||||
* Type Checker: Do not show the same error multiple times for events.
|
* Type Checker: Do not show the same error multiple times for events.
|
||||||
|
@ -68,13 +68,15 @@ The following non-fixed-size types exist:
|
|||||||
|
|
||||||
- ``<type>[]``: a variable-length array of the given fixed-length type.
|
- ``<type>[]``: a variable-length array of the given fixed-length type.
|
||||||
|
|
||||||
Types can be combined to anonymous structs by enclosing a finite non-negative number
|
Types can be combined to a tuple by enclosing a finite non-negative number
|
||||||
of them inside parentheses, separated by commas:
|
of them inside parentheses, separated by commas:
|
||||||
|
|
||||||
- ``(T1,T2,...,Tn)``: anonymous struct (ordered tuple) consisting of the types ``T1``, ..., ``Tn``, ``n >= 0``
|
- ``(T1,T2,...,Tn)``: tuple consisting of the types ``T1``, ..., ``Tn``, ``n >= 0``
|
||||||
|
|
||||||
It is possible to form structs of structs, arrays of structs and so on.
|
It is possible to form tuples of tuples, arrays of tuples and so on.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
Solidity supports all the types presented above with the same names with the exception of tuples. The ABI tuple type is utilised for encoding Solidity ``structs``.
|
||||||
|
|
||||||
Formal Specification of the Encoding
|
Formal Specification of the Encoding
|
||||||
====================================
|
====================================
|
||||||
@ -133,7 +135,7 @@ on the type of ``X`` being
|
|||||||
|
|
||||||
``enc(X) = enc((X[0], ..., X[k-1]))``
|
``enc(X) = enc((X[0], ..., X[k-1]))``
|
||||||
|
|
||||||
i.e. it is encoded as if it were an anonymous struct with ``k`` elements
|
i.e. it is encoded as if it were a tuple with ``k`` elements
|
||||||
of the same type.
|
of the same type.
|
||||||
|
|
||||||
- ``T[]`` where ``X`` has ``k`` elements (``k`` is assumed to be of type ``uint256``):
|
- ``T[]`` where ``X`` has ``k`` elements (``k`` is assumed to be of type ``uint256``):
|
||||||
@ -176,7 +178,7 @@ and the return values ``v_1, ..., v_k`` of ``f`` are encoded as
|
|||||||
|
|
||||||
``enc((v_1, ..., v_k))``
|
``enc((v_1, ..., v_k))``
|
||||||
|
|
||||||
i.e. the values are combined into an anonymous struct and encoded.
|
i.e. the values are combined into a tuple and encoded.
|
||||||
|
|
||||||
Examples
|
Examples
|
||||||
========
|
========
|
||||||
@ -289,14 +291,16 @@ In effect, a log entry using this ABI is described as:
|
|||||||
JSON
|
JSON
|
||||||
====
|
====
|
||||||
|
|
||||||
The JSON format for a contract's interface is given by an array of function and/or event descriptions. A function description is a JSON object with the fields:
|
The JSON format for a contract's interface is given by an array of function and/or event descriptions.
|
||||||
|
A function description is a JSON object with the fields:
|
||||||
|
|
||||||
- ``type``: ``"function"``, ``"constructor"``, or ``"fallback"`` (the :ref:`unnamed "default" function <fallback-function>`);
|
- ``type``: ``"function"``, ``"constructor"``, or ``"fallback"`` (the :ref:`unnamed "default" function <fallback-function>`);
|
||||||
- ``name``: the name of the function;
|
- ``name``: the name of the function;
|
||||||
- ``inputs``: an array of objects, each of which contains:
|
- ``inputs``: an array of objects, each of which contains:
|
||||||
|
|
||||||
* ``name``: the name of the parameter;
|
* ``name``: the name of the parameter;
|
||||||
* ``type``: the canonical type of the parameter.
|
* ``type``: the canonical type of the parameter (more below).
|
||||||
|
* ``components``: used for tuple types (more below).
|
||||||
|
|
||||||
- ``outputs``: an array of objects similar to ``inputs``, can be omitted if function doesn't return anything;
|
- ``outputs``: an array of objects similar to ``inputs``, can be omitted if function doesn't return anything;
|
||||||
- ``payable``: ``true`` if function accepts ether, defaults to ``false``;
|
- ``payable``: ``true`` if function accepts ether, defaults to ``false``;
|
||||||
@ -316,7 +320,8 @@ An event description is a JSON object with fairly similar fields:
|
|||||||
- ``inputs``: an array of objects, each of which contains:
|
- ``inputs``: an array of objects, each of which contains:
|
||||||
|
|
||||||
* ``name``: the name of the parameter;
|
* ``name``: the name of the parameter;
|
||||||
* ``type``: the canonical type of the parameter.
|
* ``type``: the canonical type of the parameter (more below).
|
||||||
|
* ``components``: used for tuple types (more below).
|
||||||
* ``indexed``: ``true`` if the field is part of the log's topics, ``false`` if it one of the log's data segment.
|
* ``indexed``: ``true`` if the field is part of the log's topics, ``false`` if it one of the log's data segment.
|
||||||
|
|
||||||
- ``anonymous``: ``true`` if the event was declared as ``anonymous``.
|
- ``anonymous``: ``true`` if the event was declared as ``anonymous``.
|
||||||
@ -353,3 +358,87 @@ would result in the JSON:
|
|||||||
"name":"foo",
|
"name":"foo",
|
||||||
"outputs": []
|
"outputs": []
|
||||||
}]
|
}]
|
||||||
|
|
||||||
|
Handling tuple types
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
Despite that names are intentionally not part of the ABI encoding they do make a lot of sense to be included
|
||||||
|
in the JSON to enable displaying it to the end user. The structure is nested in the following way:
|
||||||
|
|
||||||
|
An object with members ``name``, ``type`` and potentially ``components`` describes a typed variable.
|
||||||
|
The canonical type is determined until a tuple type is reached and the string description up
|
||||||
|
to that point is stored in ``type`` prefix with the word ``tuple``, i.e. it will be ``tuple`` followed by
|
||||||
|
a sequence of ``[]`` and ``[k]`` with
|
||||||
|
integers ``k``. The components of the tuple are then stored in the member ``components``,
|
||||||
|
which is of array type and has the same structure as the top-level object except that
|
||||||
|
``indexed`` is not allowed there.
|
||||||
|
|
||||||
|
As an example, the code
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
contract Test {
|
||||||
|
struct S { uint a; uint[] b; T[] c; }
|
||||||
|
struct T { uint x; uint y; }
|
||||||
|
function f(S s, T t, uint a) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
would result in the JSON:
|
||||||
|
|
||||||
|
.. code:: json
|
||||||
|
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "f",
|
||||||
|
"type": "function",
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "s",
|
||||||
|
"type": "tuple",
|
||||||
|
"components": [
|
||||||
|
{
|
||||||
|
"name": "a",
|
||||||
|
"type": "uint256"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "b",
|
||||||
|
"type": "uint256[]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "c",
|
||||||
|
"type": "tuple[]",
|
||||||
|
"components": [
|
||||||
|
{
|
||||||
|
"name": "x",
|
||||||
|
"type": "uint256"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "y",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "t",
|
||||||
|
"type": "tuple",
|
||||||
|
"components": [
|
||||||
|
{
|
||||||
|
"name": "x",
|
||||||
|
"type": "uint256"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "y",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "a",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
@ -546,7 +546,7 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
|
|||||||
if (!type(*var)->canLiveOutsideStorage())
|
if (!type(*var)->canLiveOutsideStorage())
|
||||||
m_errorReporter.typeError(var->location(), "Type is required to live outside storage.");
|
m_errorReporter.typeError(var->location(), "Type is required to live outside storage.");
|
||||||
if (_function.visibility() >= FunctionDefinition::Visibility::Public && !(type(*var)->interfaceType(isLibraryFunction)))
|
if (_function.visibility() >= FunctionDefinition::Visibility::Public && !(type(*var)->interfaceType(isLibraryFunction)))
|
||||||
m_errorReporter.fatalTypeError(var->location(), "Internal type is not allowed for public or external functions.");
|
m_errorReporter.fatalTypeError(var->location(), "Internal or recursive type is not allowed for public or external functions.");
|
||||||
|
|
||||||
var->accept(*this);
|
var->accept(*this);
|
||||||
}
|
}
|
||||||
@ -641,7 +641,7 @@ bool TypeChecker::visit(VariableDeclaration const& _variable)
|
|||||||
_variable.visibility() >= VariableDeclaration::Visibility::Public &&
|
_variable.visibility() >= VariableDeclaration::Visibility::Public &&
|
||||||
!FunctionType(_variable).interfaceFunctionType()
|
!FunctionType(_variable).interfaceFunctionType()
|
||||||
)
|
)
|
||||||
m_errorReporter.typeError(_variable.location(), "Internal type is not allowed for public state variables.");
|
m_errorReporter.typeError(_variable.location(), "Internal or recursive type is not allowed for public state variables.");
|
||||||
|
|
||||||
if (varType->category() == Type::Category::Array)
|
if (varType->category() == Type::Category::Array)
|
||||||
if (auto arrayType = dynamic_cast<ArrayType const*>(varType.get()))
|
if (auto arrayType = dynamic_cast<ArrayType const*>(varType.get()))
|
||||||
@ -728,7 +728,7 @@ bool TypeChecker::visit(EventDefinition const& _eventDef)
|
|||||||
if (!type(*var)->canLiveOutsideStorage())
|
if (!type(*var)->canLiveOutsideStorage())
|
||||||
m_errorReporter.typeError(var->location(), "Type is required to live outside storage.");
|
m_errorReporter.typeError(var->location(), "Type is required to live outside storage.");
|
||||||
if (!type(*var)->interfaceType(false))
|
if (!type(*var)->interfaceType(false))
|
||||||
m_errorReporter.typeError(var->location(), "Internal type is not allowed as event parameter type.");
|
m_errorReporter.typeError(var->location(), "Internal or recursive type is not allowed as event parameter type.");
|
||||||
}
|
}
|
||||||
if (_eventDef.isAnonymous() && numIndexed > 4)
|
if (_eventDef.isAnonymous() && numIndexed > 4)
|
||||||
m_errorReporter.typeError(_eventDef.location(), "More than 4 indexed arguments for anonymous event.");
|
m_errorReporter.typeError(_eventDef.location(), "More than 4 indexed arguments for anonymous event.");
|
||||||
|
@ -33,6 +33,7 @@
|
|||||||
#include <boost/algorithm/string/replace.hpp>
|
#include <boost/algorithm/string/replace.hpp>
|
||||||
#include <boost/algorithm/string/predicate.hpp>
|
#include <boost/algorithm/string/predicate.hpp>
|
||||||
#include <boost/range/adaptor/reversed.hpp>
|
#include <boost/range/adaptor/reversed.hpp>
|
||||||
|
#include <boost/range/algorithm/copy.hpp>
|
||||||
#include <boost/range/adaptor/sliced.hpp>
|
#include <boost/range/adaptor/sliced.hpp>
|
||||||
#include <boost/range/adaptor/transformed.hpp>
|
#include <boost/range/adaptor/transformed.hpp>
|
||||||
|
|
||||||
@ -1470,7 +1471,7 @@ string ArrayType::toString(bool _short) const
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
string ArrayType::canonicalName(bool _addDataLocation) const
|
string ArrayType::canonicalName() const
|
||||||
{
|
{
|
||||||
string ret;
|
string ret;
|
||||||
if (isString())
|
if (isString())
|
||||||
@ -1479,16 +1480,29 @@ string ArrayType::canonicalName(bool _addDataLocation) const
|
|||||||
ret = "bytes";
|
ret = "bytes";
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ret = baseType()->canonicalName(false) + "[";
|
ret = baseType()->canonicalName() + "[";
|
||||||
if (!isDynamicallySized())
|
if (!isDynamicallySized())
|
||||||
ret += length().str();
|
ret += length().str();
|
||||||
ret += "]";
|
ret += "]";
|
||||||
}
|
}
|
||||||
if (_addDataLocation && location() == DataLocation::Storage)
|
|
||||||
ret += " storage";
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string ArrayType::signatureInExternalFunction(bool _structsByName) const
|
||||||
|
{
|
||||||
|
if (isByteArray())
|
||||||
|
return canonicalName();
|
||||||
|
else
|
||||||
|
{
|
||||||
|
solAssert(baseType(), "");
|
||||||
|
return
|
||||||
|
baseType()->signatureInExternalFunction(_structsByName) +
|
||||||
|
"[" +
|
||||||
|
(isDynamicallySized() ? "" : length().str()) +
|
||||||
|
"]";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
MemberList::MemberMap ArrayType::nativeMembers(ContractDefinition const*) const
|
MemberList::MemberMap ArrayType::nativeMembers(ContractDefinition const*) const
|
||||||
{
|
{
|
||||||
MemberList::MemberMap members;
|
MemberList::MemberMap members;
|
||||||
@ -1597,7 +1611,7 @@ string ContractType::toString(bool) const
|
|||||||
m_contract.name();
|
m_contract.name();
|
||||||
}
|
}
|
||||||
|
|
||||||
string ContractType::canonicalName(bool) const
|
string ContractType::canonicalName() const
|
||||||
{
|
{
|
||||||
return m_contract.annotation().canonicalName;
|
return m_contract.annotation().canonicalName;
|
||||||
}
|
}
|
||||||
@ -1721,15 +1735,22 @@ unsigned StructType::calldataEncodedSize(bool _padded) const
|
|||||||
|
|
||||||
bool StructType::isDynamicallyEncoded() const
|
bool StructType::isDynamicallyEncoded() const
|
||||||
{
|
{
|
||||||
solAssert(false, "Structs are not yet supported in the ABI.");
|
solAssert(!recursive(), "");
|
||||||
|
for (auto t: memoryMemberTypes())
|
||||||
|
{
|
||||||
|
solAssert(t, "Parameter should have external type.");
|
||||||
|
t = t->interfaceType(false);
|
||||||
|
if (t->isDynamicallyEncoded())
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
u256 StructType::memorySize() const
|
u256 StructType::memorySize() const
|
||||||
{
|
{
|
||||||
u256 size;
|
u256 size;
|
||||||
for (auto const& member: members(nullptr))
|
for (auto const& t: memoryMemberTypes())
|
||||||
if (member.type->canLiveOutsideStorage())
|
size += t->memoryHeadSize();
|
||||||
size += member.type->memoryHeadSize();
|
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1767,10 +1788,33 @@ MemberList::MemberMap StructType::nativeMembers(ContractDefinition const*) const
|
|||||||
|
|
||||||
TypePointer StructType::interfaceType(bool _inLibrary) const
|
TypePointer StructType::interfaceType(bool _inLibrary) const
|
||||||
{
|
{
|
||||||
|
if (!canBeUsedExternally(_inLibrary))
|
||||||
|
return TypePointer();
|
||||||
|
|
||||||
|
// Has to fulfill canBeUsedExternally(_inLibrary) == !!interfaceType(_inLibrary)
|
||||||
if (_inLibrary && location() == DataLocation::Storage)
|
if (_inLibrary && location() == DataLocation::Storage)
|
||||||
return shared_from_this();
|
return shared_from_this();
|
||||||
else
|
else
|
||||||
return TypePointer();
|
return copyForLocation(DataLocation::Memory, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool StructType::canBeUsedExternally(bool _inLibrary) const
|
||||||
|
{
|
||||||
|
if (_inLibrary && location() == DataLocation::Storage)
|
||||||
|
return true;
|
||||||
|
else if (recursive())
|
||||||
|
return false;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Check that all members have interface types.
|
||||||
|
// We pass "false" to canBeUsedExternally (_inLibrary), because this struct will be
|
||||||
|
// passed by value and thus the encoding does not differ, but it will disallow
|
||||||
|
// mappings.
|
||||||
|
for (auto const& var: m_struct.members())
|
||||||
|
if (!var->annotation().type->canBeUsedExternally(false))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
TypePointer StructType::copyForLocation(DataLocation _location, bool _isPointer) const
|
TypePointer StructType::copyForLocation(DataLocation _location, bool _isPointer) const
|
||||||
@ -1780,12 +1824,27 @@ TypePointer StructType::copyForLocation(DataLocation _location, bool _isPointer)
|
|||||||
return copy;
|
return copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
string StructType::canonicalName(bool _addDataLocation) const
|
string StructType::signatureInExternalFunction(bool _structsByName) const
|
||||||
{
|
{
|
||||||
string ret = m_struct.annotation().canonicalName;
|
if (_structsByName)
|
||||||
if (_addDataLocation && location() == DataLocation::Storage)
|
return canonicalName();
|
||||||
ret += " storage";
|
else
|
||||||
return ret;
|
{
|
||||||
|
TypePointers memberTypes = memoryMemberTypes();
|
||||||
|
auto memberTypeStrings = memberTypes | boost::adaptors::transformed([&](TypePointer _t) -> string
|
||||||
|
{
|
||||||
|
solAssert(_t, "Parameter should have external type.");
|
||||||
|
auto t = _t->interfaceType(_structsByName);
|
||||||
|
solAssert(t, "");
|
||||||
|
return t->signatureInExternalFunction(_structsByName);
|
||||||
|
});
|
||||||
|
return "(" + boost::algorithm::join(memberTypeStrings, ",") + ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
string StructType::canonicalName() const
|
||||||
|
{
|
||||||
|
return m_struct.annotation().canonicalName;
|
||||||
}
|
}
|
||||||
|
|
||||||
FunctionTypePointer StructType::constructorType() const
|
FunctionTypePointer StructType::constructorType() const
|
||||||
@ -1827,6 +1886,15 @@ u256 StructType::memoryOffsetOfMember(string const& _name) const
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TypePointers StructType::memoryMemberTypes() const
|
||||||
|
{
|
||||||
|
TypePointers types;
|
||||||
|
for (ASTPointer<VariableDeclaration> const& variable: m_struct.members())
|
||||||
|
if (variable->annotation().type->canLiveOutsideStorage())
|
||||||
|
types.push_back(variable->annotation().type);
|
||||||
|
return types;
|
||||||
|
}
|
||||||
|
|
||||||
set<string> StructType::membersMissingInMemory() const
|
set<string> StructType::membersMissingInMemory() const
|
||||||
{
|
{
|
||||||
set<string> missing;
|
set<string> missing;
|
||||||
@ -1836,6 +1904,33 @@ set<string> StructType::membersMissingInMemory() const
|
|||||||
return missing;
|
return missing;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool StructType::recursive() const
|
||||||
|
{
|
||||||
|
if (!m_recursive.is_initialized())
|
||||||
|
{
|
||||||
|
set<StructDefinition const*> structsSeen;
|
||||||
|
function<bool(StructType const*)> check = [&](StructType const* t) -> bool
|
||||||
|
{
|
||||||
|
StructDefinition const* str = &t->structDefinition();
|
||||||
|
if (structsSeen.count(str))
|
||||||
|
return true;
|
||||||
|
structsSeen.insert(str);
|
||||||
|
for (ASTPointer<VariableDeclaration> const& variable: str->members())
|
||||||
|
{
|
||||||
|
Type const* memberType = variable->annotation().type.get();
|
||||||
|
while (dynamic_cast<ArrayType const*>(memberType))
|
||||||
|
memberType = dynamic_cast<ArrayType const*>(memberType)->baseType().get();
|
||||||
|
if (StructType const* innerStruct = dynamic_cast<StructType const*>(memberType))
|
||||||
|
if (check(innerStruct))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
m_recursive = check(this);
|
||||||
|
}
|
||||||
|
return *m_recursive;
|
||||||
|
}
|
||||||
|
|
||||||
TypePointer EnumType::unaryOperatorResult(Token::Value _operator) const
|
TypePointer EnumType::unaryOperatorResult(Token::Value _operator) const
|
||||||
{
|
{
|
||||||
return _operator == Token::Delete ? make_shared<TupleType>() : TypePointer();
|
return _operator == Token::Delete ? make_shared<TupleType>() : TypePointer();
|
||||||
@ -1868,7 +1963,7 @@ string EnumType::toString(bool) const
|
|||||||
return string("enum ") + m_enum.annotation().canonicalName;
|
return string("enum ") + m_enum.annotation().canonicalName;
|
||||||
}
|
}
|
||||||
|
|
||||||
string EnumType::canonicalName(bool) const
|
string EnumType::canonicalName() const
|
||||||
{
|
{
|
||||||
return m_enum.annotation().canonicalName;
|
return m_enum.annotation().canonicalName;
|
||||||
}
|
}
|
||||||
@ -2295,7 +2390,7 @@ TypePointer FunctionType::binaryOperatorResult(Token::Value _operator, TypePoint
|
|||||||
return TypePointer();
|
return TypePointer();
|
||||||
}
|
}
|
||||||
|
|
||||||
string FunctionType::canonicalName(bool) const
|
string FunctionType::canonicalName() const
|
||||||
{
|
{
|
||||||
solAssert(m_kind == Kind::External, "");
|
solAssert(m_kind == Kind::External, "");
|
||||||
return "function";
|
return "function";
|
||||||
@ -2555,20 +2650,19 @@ string FunctionType::externalSignature() const
|
|||||||
solAssert(m_declaration != nullptr, "External signature of function needs declaration");
|
solAssert(m_declaration != nullptr, "External signature of function needs declaration");
|
||||||
solAssert(!m_declaration->name().empty(), "Fallback function has no signature.");
|
solAssert(!m_declaration->name().empty(), "Fallback function has no signature.");
|
||||||
|
|
||||||
bool _inLibrary = dynamic_cast<ContractDefinition const&>(*m_declaration->scope()).isLibrary();
|
bool const inLibrary = dynamic_cast<ContractDefinition const&>(*m_declaration->scope()).isLibrary();
|
||||||
|
|
||||||
string ret = m_declaration->name() + "(";
|
|
||||||
|
|
||||||
FunctionTypePointer external = interfaceFunctionType();
|
FunctionTypePointer external = interfaceFunctionType();
|
||||||
solAssert(!!external, "External function type requested.");
|
solAssert(!!external, "External function type requested.");
|
||||||
TypePointers externalParameterTypes = external->parameterTypes();
|
auto parameterTypes = external->parameterTypes();
|
||||||
for (auto it = externalParameterTypes.cbegin(); it != externalParameterTypes.cend(); ++it)
|
auto typeStrings = parameterTypes | boost::adaptors::transformed([&](TypePointer _t) -> string
|
||||||
{
|
{
|
||||||
solAssert(!!(*it), "Parameter should have external type");
|
solAssert(_t, "Parameter should have external type.");
|
||||||
ret += (*it)->canonicalName(_inLibrary) + (it + 1 == externalParameterTypes.cend() ? "" : ",");
|
string typeName = _t->signatureInExternalFunction(inLibrary);
|
||||||
}
|
if (inLibrary && _t->dataStoredIn(DataLocation::Storage))
|
||||||
|
typeName += " storage";
|
||||||
return ret + ")";
|
return typeName;
|
||||||
|
});
|
||||||
|
return m_declaration->name() + "(" + boost::algorithm::join(typeStrings, ",") + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
u256 FunctionType::externalIdentifier() const
|
u256 FunctionType::externalIdentifier() const
|
||||||
@ -2699,9 +2793,9 @@ string MappingType::toString(bool _short) const
|
|||||||
return "mapping(" + keyType()->toString(_short) + " => " + valueType()->toString(_short) + ")";
|
return "mapping(" + keyType()->toString(_short) + " => " + valueType()->toString(_short) + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
string MappingType::canonicalName(bool) const
|
string MappingType::canonicalName() const
|
||||||
{
|
{
|
||||||
return "mapping(" + keyType()->canonicalName(false) + " => " + valueType()->canonicalName(false) + ")";
|
return "mapping(" + keyType()->canonicalName() + " => " + valueType()->canonicalName() + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
string TypeType::identifier() const
|
string TypeType::identifier() const
|
||||||
|
@ -32,6 +32,7 @@
|
|||||||
|
|
||||||
#include <boost/noncopyable.hpp>
|
#include <boost/noncopyable.hpp>
|
||||||
#include <boost/rational.hpp>
|
#include <boost/rational.hpp>
|
||||||
|
#include <boost/optional.hpp>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
@ -245,9 +246,15 @@ public:
|
|||||||
|
|
||||||
virtual std::string toString(bool _short) const = 0;
|
virtual std::string toString(bool _short) const = 0;
|
||||||
std::string toString() const { return toString(false); }
|
std::string toString() const { return toString(false); }
|
||||||
/// @returns the canonical name of this type for use in function signatures.
|
/// @returns the canonical name of this type for use in library function signatures.
|
||||||
/// @param _addDataLocation if true, includes data location for reference types if it is "storage".
|
virtual std::string canonicalName() const { return toString(true); }
|
||||||
virtual std::string canonicalName(bool /*_addDataLocation*/) const { return toString(true); }
|
/// @returns the signature of this type in external functions, i.e. `uint256` for integers
|
||||||
|
/// or `(uint256,bytes8)[2]` for an array of structs. If @a _structsByName,
|
||||||
|
/// structs are given by canonical name like `ContractName.StructName[2]`.
|
||||||
|
virtual std::string signatureInExternalFunction(bool /*_structsByName*/) const
|
||||||
|
{
|
||||||
|
return canonicalName();
|
||||||
|
}
|
||||||
virtual u256 literalValue(Literal const*) const
|
virtual u256 literalValue(Literal const*) const
|
||||||
{
|
{
|
||||||
solAssert(false, "Literal value requested for type without literals.");
|
solAssert(false, "Literal value requested for type without literals.");
|
||||||
@ -619,7 +626,8 @@ public:
|
|||||||
virtual bool canLiveOutsideStorage() const override { return m_baseType->canLiveOutsideStorage(); }
|
virtual bool canLiveOutsideStorage() const override { return m_baseType->canLiveOutsideStorage(); }
|
||||||
virtual unsigned sizeOnStack() const override;
|
virtual unsigned sizeOnStack() const override;
|
||||||
virtual std::string toString(bool _short) const override;
|
virtual std::string toString(bool _short) const override;
|
||||||
virtual std::string canonicalName(bool _addDataLocation) const override;
|
virtual std::string canonicalName() const override;
|
||||||
|
virtual std::string signatureInExternalFunction(bool _structsByName) const override;
|
||||||
virtual MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override;
|
virtual MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override;
|
||||||
virtual TypePointer encodingType() const override;
|
virtual TypePointer encodingType() const override;
|
||||||
virtual TypePointer decodingType() const override;
|
virtual TypePointer decodingType() const override;
|
||||||
@ -677,7 +685,7 @@ public:
|
|||||||
virtual unsigned sizeOnStack() const override { return m_super ? 0 : 1; }
|
virtual unsigned sizeOnStack() const override { return m_super ? 0 : 1; }
|
||||||
virtual bool isValueType() const override { return true; }
|
virtual bool isValueType() const override { return true; }
|
||||||
virtual std::string toString(bool _short) const override;
|
virtual std::string toString(bool _short) const override;
|
||||||
virtual std::string canonicalName(bool _addDataLocation) const override;
|
virtual std::string canonicalName() const override;
|
||||||
|
|
||||||
virtual MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override;
|
virtual MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override;
|
||||||
virtual TypePointer encodingType() const override
|
virtual TypePointer encodingType() const override
|
||||||
@ -738,13 +746,15 @@ public:
|
|||||||
virtual MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override;
|
virtual MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override;
|
||||||
virtual TypePointer encodingType() const override
|
virtual TypePointer encodingType() const override
|
||||||
{
|
{
|
||||||
return location() == DataLocation::Storage ? std::make_shared<IntegerType>(256) : TypePointer();
|
return location() == DataLocation::Storage ? std::make_shared<IntegerType>(256) : shared_from_this();
|
||||||
}
|
}
|
||||||
virtual TypePointer interfaceType(bool _inLibrary) const override;
|
virtual TypePointer interfaceType(bool _inLibrary) const override;
|
||||||
|
virtual bool canBeUsedExternally(bool _inLibrary) const override;
|
||||||
|
|
||||||
TypePointer copyForLocation(DataLocation _location, bool _isPointer) const override;
|
TypePointer copyForLocation(DataLocation _location, bool _isPointer) const override;
|
||||||
|
|
||||||
virtual std::string canonicalName(bool _addDataLocation) const override;
|
virtual std::string canonicalName() const override;
|
||||||
|
virtual std::string signatureInExternalFunction(bool _structsByName) const override;
|
||||||
|
|
||||||
/// @returns a function that peforms the type conversion between a list of struct members
|
/// @returns a function that peforms the type conversion between a list of struct members
|
||||||
/// and a memory struct of this type.
|
/// and a memory struct of this type.
|
||||||
@ -755,11 +765,19 @@ public:
|
|||||||
|
|
||||||
StructDefinition const& structDefinition() const { return m_struct; }
|
StructDefinition const& structDefinition() const { return m_struct; }
|
||||||
|
|
||||||
|
/// @returns the vector of types of members available in memory.
|
||||||
|
TypePointers memoryMemberTypes() const;
|
||||||
/// @returns the set of all members that are removed in the memory version (typically mappings).
|
/// @returns the set of all members that are removed in the memory version (typically mappings).
|
||||||
std::set<std::string> membersMissingInMemory() const;
|
std::set<std::string> membersMissingInMemory() const;
|
||||||
|
|
||||||
|
/// @returns true if the same struct is used recursively in one of its members. Only
|
||||||
|
/// analyses the "memory" representation, i.e. mappings are ignored in all structs.
|
||||||
|
bool recursive() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
StructDefinition const& m_struct;
|
StructDefinition const& m_struct;
|
||||||
|
/// Cache for the recursive() function.
|
||||||
|
mutable boost::optional<bool> m_recursive;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -780,7 +798,7 @@ public:
|
|||||||
virtual unsigned storageBytes() const override;
|
virtual unsigned storageBytes() const override;
|
||||||
virtual bool canLiveOutsideStorage() const override { return true; }
|
virtual bool canLiveOutsideStorage() const override { return true; }
|
||||||
virtual std::string toString(bool _short) const override;
|
virtual std::string toString(bool _short) const override;
|
||||||
virtual std::string canonicalName(bool _addDataLocation) const override;
|
virtual std::string canonicalName() const override;
|
||||||
virtual bool isValueType() const override { return true; }
|
virtual bool isValueType() const override { return true; }
|
||||||
|
|
||||||
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||||
@ -951,7 +969,7 @@ public:
|
|||||||
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||||
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
|
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
|
||||||
virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override;
|
virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override;
|
||||||
virtual std::string canonicalName(bool /*_addDataLocation*/) const override;
|
virtual std::string canonicalName() const override;
|
||||||
virtual std::string toString(bool _short) const override;
|
virtual std::string toString(bool _short) const override;
|
||||||
virtual unsigned calldataEncodedSize(bool _padded) const override;
|
virtual unsigned calldataEncodedSize(bool _padded) const override;
|
||||||
virtual bool canBeStored() const override { return m_kind == Kind::Internal || m_kind == Kind::External; }
|
virtual bool canBeStored() const override { return m_kind == Kind::Internal || m_kind == Kind::External; }
|
||||||
@ -1053,7 +1071,7 @@ public:
|
|||||||
virtual std::string identifier() const override;
|
virtual std::string identifier() const override;
|
||||||
virtual bool operator==(Type const& _other) const override;
|
virtual bool operator==(Type const& _other) const override;
|
||||||
virtual std::string toString(bool _short) const override;
|
virtual std::string toString(bool _short) const override;
|
||||||
virtual std::string canonicalName(bool _addDataLocation) const override;
|
virtual std::string canonicalName() const override;
|
||||||
virtual bool canLiveOutsideStorage() const override { return false; }
|
virtual bool canLiveOutsideStorage() const override { return false; }
|
||||||
virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); }
|
virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); }
|
||||||
virtual TypePointer encodingType() const override
|
virtual TypePointer encodingType() const override
|
||||||
|
@ -404,9 +404,11 @@ string ABIFunctions::abiEncodingFunction(
|
|||||||
else
|
else
|
||||||
solAssert(false, "");
|
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)
|
else if (_from.category() == Type::Category::Function)
|
||||||
return abiEncodingFunctionFunctionType(
|
return abiEncodingFunctionFunctionType(
|
||||||
@ -534,7 +536,7 @@ string ABIFunctions::abiEncodingFunctionSimpleArray(
|
|||||||
for { let i := 0 } lt(i, length) { i := add(i, 1) }
|
for { let i := 0 } lt(i, length) { i := add(i, 1) }
|
||||||
{
|
{
|
||||||
mstore(pos, sub(tail, headStart))
|
mstore(pos, sub(tail, headStart))
|
||||||
tail := <encodeToMemoryFun>(<arrayElementAccess>(srcPtr), tail)
|
tail := <encodeToMemoryFun>(<arrayElementAccess>, tail)
|
||||||
srcPtr := <nextArrayElement>(srcPtr)
|
srcPtr := <nextArrayElement>(srcPtr)
|
||||||
pos := add(pos, <elementEncodedSize>)
|
pos := add(pos, <elementEncodedSize>)
|
||||||
}
|
}
|
||||||
@ -549,7 +551,7 @@ string ABIFunctions::abiEncodingFunctionSimpleArray(
|
|||||||
let srcPtr := <dataAreaFun>(value)
|
let srcPtr := <dataAreaFun>(value)
|
||||||
for { let i := 0 } lt(i, length) { i := add(i, 1) }
|
for { let i := 0 } lt(i, length) { i := add(i, 1) }
|
||||||
{
|
{
|
||||||
<encodeToMemoryFun>(<arrayElementAccess>(srcPtr), pos)
|
<encodeToMemoryFun>(<arrayElementAccess>, pos)
|
||||||
srcPtr := <nextArrayElement>(srcPtr)
|
srcPtr := <nextArrayElement>(srcPtr)
|
||||||
pos := add(pos, <elementEncodedSize>)
|
pos := add(pos, <elementEncodedSize>)
|
||||||
}
|
}
|
||||||
@ -573,7 +575,7 @@ string ABIFunctions::abiEncodingFunctionSimpleArray(
|
|||||||
_encodeAsLibraryTypes,
|
_encodeAsLibraryTypes,
|
||||||
true
|
true
|
||||||
));
|
));
|
||||||
templ("arrayElementAccess", inMemory ? "mload" : "sload");
|
templ("arrayElementAccess", inMemory ? "mload(srcPtr)" : _from.baseType()->isValueType() ? "sload(srcPtr)" : "srcPtr" );
|
||||||
templ("nextArrayElement", nextArrayElementFunction(_from));
|
templ("nextArrayElement", nextArrayElementFunction(_from));
|
||||||
return templ.render();
|
return templ.render();
|
||||||
});
|
});
|
||||||
@ -726,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(
|
string ABIFunctions::abiEncodingFunctionStringLiteral(
|
||||||
Type const& _from,
|
Type const& _from,
|
||||||
Type const& _to,
|
Type const& _to,
|
||||||
|
@ -123,6 +123,13 @@ private:
|
|||||||
bool _encodeAsLibraryTypes
|
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
|
// @returns the name of the ABI encoding function with the given type
|
||||||
// and queues the generation of the function to the requested functions.
|
// and queues the generation of the function to the requested functions.
|
||||||
// Case for _givenType being a string literal
|
// Case for _givenType being a string literal
|
||||||
|
@ -121,7 +121,7 @@ void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBound
|
|||||||
{
|
{
|
||||||
if (auto ref = dynamic_cast<ReferenceType const*>(&_type))
|
if (auto ref = dynamic_cast<ReferenceType const*>(&_type))
|
||||||
{
|
{
|
||||||
solAssert(ref->location() == DataLocation::Memory, "");
|
solUnimplementedAssert(ref->location() == DataLocation::Memory, "");
|
||||||
storeInMemoryDynamic(IntegerType(256), _padToWordBoundaries);
|
storeInMemoryDynamic(IntegerType(256), _padToWordBoundaries);
|
||||||
}
|
}
|
||||||
else if (auto str = dynamic_cast<StringLiteralType const*>(&_type))
|
else if (auto str = dynamic_cast<StringLiteralType const*>(&_type))
|
||||||
|
@ -333,7 +333,7 @@ void ContractCompiler::appendCalldataUnpacker(TypePointers const& _typeParameter
|
|||||||
{
|
{
|
||||||
// stack: v1 v2 ... v(k-1) base_offset current_offset
|
// stack: v1 v2 ... v(k-1) base_offset current_offset
|
||||||
TypePointer type = parameterType->decodingType();
|
TypePointer type = parameterType->decodingType();
|
||||||
solAssert(type, "No decoding type found.");
|
solUnimplementedAssert(type, "No decoding type found.");
|
||||||
if (type->category() == Type::Category::Array)
|
if (type->category() == Type::Category::Array)
|
||||||
{
|
{
|
||||||
auto const& arrayType = dynamic_cast<ArrayType const&>(*type);
|
auto const& arrayType = dynamic_cast<ArrayType const&>(*type);
|
||||||
|
@ -86,12 +86,12 @@ Json::Value ABI::generate(ContractDefinition const& _contractDef)
|
|||||||
Json::Value params(Json::arrayValue);
|
Json::Value params(Json::arrayValue);
|
||||||
for (auto const& p: it->parameters())
|
for (auto const& p: it->parameters())
|
||||||
{
|
{
|
||||||
solAssert(!!p->annotation().type->interfaceType(false), "");
|
auto type = p->annotation().type->interfaceType(false);
|
||||||
|
solAssert(type, "");
|
||||||
Json::Value input;
|
Json::Value input;
|
||||||
input["name"] = p->name();
|
auto param = formatType(p->name(), *type, false);
|
||||||
input["type"] = p->annotation().type->interfaceType(false)->canonicalName(false);
|
param["indexed"] = p->isIndexed();
|
||||||
input["indexed"] = p->isIndexed();
|
params.append(param);
|
||||||
params.append(input);
|
|
||||||
}
|
}
|
||||||
event["inputs"] = params;
|
event["inputs"] = params;
|
||||||
abi.append(event);
|
abi.append(event);
|
||||||
@ -111,10 +111,53 @@ Json::Value ABI::formatTypeList(
|
|||||||
for (unsigned i = 0; i < _names.size(); ++i)
|
for (unsigned i = 0; i < _names.size(); ++i)
|
||||||
{
|
{
|
||||||
solAssert(_types[i], "");
|
solAssert(_types[i], "");
|
||||||
Json::Value param;
|
params.append(formatType(_names[i], *_types[i], _forLibrary));
|
||||||
param["name"] = _names[i];
|
|
||||||
param["type"] = _types[i]->canonicalName(_forLibrary);
|
|
||||||
params.append(param);
|
|
||||||
}
|
}
|
||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Json::Value ABI::formatType(string const& _name, Type const& _type, bool _forLibrary)
|
||||||
|
{
|
||||||
|
Json::Value ret;
|
||||||
|
ret["name"] = _name;
|
||||||
|
string suffix = (_forLibrary && _type.dataStoredIn(DataLocation::Storage)) ? " storage" : "";
|
||||||
|
if (_type.isValueType() || (_forLibrary && _type.dataStoredIn(DataLocation::Storage)))
|
||||||
|
ret["type"] = _type.canonicalName() + suffix;
|
||||||
|
else if (ArrayType const* arrayType = dynamic_cast<ArrayType const*>(&_type))
|
||||||
|
{
|
||||||
|
if (arrayType->isByteArray())
|
||||||
|
ret["type"] = _type.canonicalName() + suffix;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
string suffix;
|
||||||
|
if (arrayType->isDynamicallySized())
|
||||||
|
suffix = "[]";
|
||||||
|
else
|
||||||
|
suffix = string("[") + arrayType->length().str() + "]";
|
||||||
|
solAssert(arrayType->baseType(), "");
|
||||||
|
Json::Value subtype = formatType("", *arrayType->baseType(), _forLibrary);
|
||||||
|
if (subtype.isMember("components"))
|
||||||
|
{
|
||||||
|
ret["type"] = subtype["type"].asString() + suffix;
|
||||||
|
ret["components"] = subtype["components"];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ret["type"] = subtype["type"].asString() + suffix;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (StructType const* structType = dynamic_cast<StructType const*>(&_type))
|
||||||
|
{
|
||||||
|
ret["type"] = "tuple";
|
||||||
|
ret["components"] = Json::arrayValue;
|
||||||
|
for (auto const& member: structType->members(nullptr))
|
||||||
|
{
|
||||||
|
solAssert(member.type, "");
|
||||||
|
auto t = member.type->interfaceType(_forLibrary);
|
||||||
|
solAssert(t, "");
|
||||||
|
ret["components"].append(formatType(member.name, *t, _forLibrary));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
solAssert(false, "Invalid type.");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
@ -50,6 +50,10 @@ private:
|
|||||||
std::vector<TypePointer> const& _types,
|
std::vector<TypePointer> const& _types,
|
||||||
bool _forLibrary
|
bool _forLibrary
|
||||||
);
|
);
|
||||||
|
/// @returns a Json object with "name", "type" and potentially "components" keys, according
|
||||||
|
/// to the ABI specification.
|
||||||
|
/// If it is possible to express the type as a single string, it is allowed to return a single string.
|
||||||
|
static Json::Value formatType(std::string const& _name, Type const& _type, bool _forLibrary);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -424,7 +424,43 @@ BOOST_AUTO_TEST_CASE(function_name_collision)
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(structs)
|
||||||
|
{
|
||||||
|
string sourceCode = R"(
|
||||||
|
contract C {
|
||||||
|
struct S { uint16 a; uint16 b; T[] sub; uint16 c; }
|
||||||
|
struct T { uint64[2] x; }
|
||||||
|
S s;
|
||||||
|
event e(uint16, S);
|
||||||
|
function f() returns (uint, S) {
|
||||||
|
uint16 x = 7;
|
||||||
|
s.a = 8;
|
||||||
|
s.b = 9;
|
||||||
|
s.c = 10;
|
||||||
|
s.sub.length = 3;
|
||||||
|
s.sub[0].x[0] = 11;
|
||||||
|
s.sub[1].x[0] = 12;
|
||||||
|
s.sub[2].x[1] = 13;
|
||||||
|
e(x, s);
|
||||||
|
return (x, s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
NEW_ENCODER(
|
||||||
|
compileAndRun(sourceCode, 0, "C");
|
||||||
|
bytes encoded = encodeArgs(
|
||||||
|
u256(7), 0x40,
|
||||||
|
8, 9, 0x80, 10,
|
||||||
|
3,
|
||||||
|
11, 0,
|
||||||
|
12, 0,
|
||||||
|
0, 13
|
||||||
|
);
|
||||||
|
BOOST_CHECK(callContractFunction("f()") == encoded);
|
||||||
|
REQUIRE_LOG_DATA(encoded);
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE_END()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ public:
|
|||||||
|
|
||||||
Json::Value generatedInterface = m_compilerStack.contractABI("");
|
Json::Value generatedInterface = m_compilerStack.contractABI("");
|
||||||
Json::Value expectedInterface;
|
Json::Value expectedInterface;
|
||||||
m_reader.parse(_expectedInterfaceString, expectedInterface);
|
BOOST_REQUIRE(m_reader.parse(_expectedInterfaceString, expectedInterface));
|
||||||
BOOST_CHECK_MESSAGE(
|
BOOST_CHECK_MESSAGE(
|
||||||
expectedInterface == generatedInterface,
|
expectedInterface == generatedInterface,
|
||||||
"Expected:\n" << expectedInterface.toStyledString() <<
|
"Expected:\n" << expectedInterface.toStyledString() <<
|
||||||
@ -939,6 +939,217 @@ BOOST_AUTO_TEST_CASE(function_type)
|
|||||||
checkInterface(sourceCode, interface);
|
checkInterface(sourceCode, interface);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(return_structs)
|
||||||
|
{
|
||||||
|
char const* text = R"(
|
||||||
|
contract C {
|
||||||
|
struct S { uint a; T[] sub; }
|
||||||
|
struct T { uint[2] x; }
|
||||||
|
function f() returns (uint x, S s) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
char const* interface = R"(
|
||||||
|
[{
|
||||||
|
"constant" : false,
|
||||||
|
"inputs" : [],
|
||||||
|
"name" : "f",
|
||||||
|
"outputs" : [
|
||||||
|
{
|
||||||
|
"name" : "x",
|
||||||
|
"type" : "uint256"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"components" : [
|
||||||
|
{
|
||||||
|
"name" : "a",
|
||||||
|
"type" : "uint256"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"components" : [
|
||||||
|
{
|
||||||
|
"name" : "x",
|
||||||
|
"type" : "uint256[2]"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name" : "sub",
|
||||||
|
"type" : "tuple[]"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name" : "s",
|
||||||
|
"type" : "tuple"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"payable" : false,
|
||||||
|
"stateMutability" : "nonpayable",
|
||||||
|
"type" : "function"
|
||||||
|
}]
|
||||||
|
)";
|
||||||
|
checkInterface(text, interface);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(return_structs_with_contracts)
|
||||||
|
{
|
||||||
|
char const* text = R"(
|
||||||
|
contract C {
|
||||||
|
struct S { C[] x; C y; }
|
||||||
|
function f() returns (S s, C c) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
char const* interface = R"(
|
||||||
|
[{
|
||||||
|
"constant": false,
|
||||||
|
"inputs": [],
|
||||||
|
"name": "f",
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"components": [
|
||||||
|
{
|
||||||
|
"name": "x",
|
||||||
|
"type": "address[]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "y",
|
||||||
|
"type": "address"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "s",
|
||||||
|
"type": "tuple"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "c",
|
||||||
|
"type": "address"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"payable": false,
|
||||||
|
"stateMutability" : "nonpayable",
|
||||||
|
"type": "function"
|
||||||
|
}]
|
||||||
|
)";
|
||||||
|
checkInterface(text, interface);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(event_structs)
|
||||||
|
{
|
||||||
|
char const* text = R"(
|
||||||
|
contract C {
|
||||||
|
struct S { uint a; T[] sub; bytes b; }
|
||||||
|
struct T { uint[2] x; }
|
||||||
|
event E(T t, S s);
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
char const *interface = R"(
|
||||||
|
[{
|
||||||
|
"anonymous": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"components": [
|
||||||
|
{
|
||||||
|
"name": "x",
|
||||||
|
"type": "uint256[2]"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"indexed": false,
|
||||||
|
"name": "t",
|
||||||
|
"type": "tuple"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"components": [
|
||||||
|
{
|
||||||
|
"name": "a",
|
||||||
|
"type": "uint256"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"components": [
|
||||||
|
{
|
||||||
|
"name": "x",
|
||||||
|
"type": "uint256[2]"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "sub",
|
||||||
|
"type": "tuple[]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "b",
|
||||||
|
"type": "bytes"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"indexed": false,
|
||||||
|
"name": "s",
|
||||||
|
"type": "tuple"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "E",
|
||||||
|
"type": "event"
|
||||||
|
}]
|
||||||
|
)";
|
||||||
|
checkInterface(text, interface);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(structs_in_libraries)
|
||||||
|
{
|
||||||
|
char const* text = R"(
|
||||||
|
library L {
|
||||||
|
struct S { uint a; T[] sub; bytes b; }
|
||||||
|
struct T { uint[2] x; }
|
||||||
|
function f(L.S storage s) {}
|
||||||
|
function g(L.S s) {}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
char const* interface = R"(
|
||||||
|
[{
|
||||||
|
"constant": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"components": [
|
||||||
|
{
|
||||||
|
"name": "a",
|
||||||
|
"type": "uint256"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"components": [
|
||||||
|
{
|
||||||
|
"name": "x",
|
||||||
|
"type": "uint256[2]"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "sub",
|
||||||
|
"type": "tuple[]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "b",
|
||||||
|
"type": "bytes"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "s",
|
||||||
|
"type": "tuple"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "g",
|
||||||
|
"outputs": [],
|
||||||
|
"payable": false,
|
||||||
|
"stateMutability": "nonpayable",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "s",
|
||||||
|
"type": "L.S storage"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "f",
|
||||||
|
"outputs": [],
|
||||||
|
"payable": false,
|
||||||
|
"stateMutability": "nonpayable",
|
||||||
|
"type": "function"
|
||||||
|
}]
|
||||||
|
)";
|
||||||
|
checkInterface(text, interface);
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE_END()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -9682,6 +9682,7 @@ BOOST_AUTO_TEST_CASE(contracts_separated_with_comment)
|
|||||||
compileAndRun(sourceCode, 0, "C2");
|
compileAndRun(sourceCode, 0, "C2");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(include_creation_bytecode_only_once)
|
BOOST_AUTO_TEST_CASE(include_creation_bytecode_only_once)
|
||||||
{
|
{
|
||||||
char const* sourceCode = R"(
|
char const* sourceCode = R"(
|
||||||
|
@ -599,6 +599,149 @@ BOOST_AUTO_TEST_CASE(enum_external_type)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(external_structs)
|
||||||
|
{
|
||||||
|
char const* text = R"(
|
||||||
|
contract Test {
|
||||||
|
enum ActionChoices { GoLeft, GoRight, GoStraight, Sit }
|
||||||
|
struct Empty {}
|
||||||
|
struct Nested { X[2][] a; uint y; }
|
||||||
|
struct X { bytes32 x; Test t; Empty[] e; }
|
||||||
|
function f(ActionChoices, uint, Empty) external {}
|
||||||
|
function g(Test, Nested) external {}
|
||||||
|
function h(function(Nested) external returns (uint)[]) external {}
|
||||||
|
function i(Nested[]) external {}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
SourceUnit const* sourceUnit = parseAndAnalyse(text);
|
||||||
|
for (ASTPointer<ASTNode> const& node: sourceUnit->nodes())
|
||||||
|
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
|
||||||
|
{
|
||||||
|
auto functions = contract->definedFunctions();
|
||||||
|
BOOST_REQUIRE(!functions.empty());
|
||||||
|
BOOST_CHECK_EQUAL("f(uint8,uint256,())", functions[0]->externalSignature());
|
||||||
|
BOOST_CHECK_EQUAL("g(address,((bytes32,address,()[])[2][],uint256))", functions[1]->externalSignature());
|
||||||
|
BOOST_CHECK_EQUAL("h(function[])", functions[2]->externalSignature());
|
||||||
|
BOOST_CHECK_EQUAL("i(((bytes32,address,()[])[2][],uint256)[])", functions[3]->externalSignature());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(external_structs_in_libraries)
|
||||||
|
{
|
||||||
|
char const* text = R"(
|
||||||
|
library Test {
|
||||||
|
enum ActionChoices { GoLeft, GoRight, GoStraight, Sit }
|
||||||
|
struct Empty {}
|
||||||
|
struct Nested { X[2][] a; uint y; }
|
||||||
|
struct X { bytes32 x; Test t; Empty[] e; }
|
||||||
|
function f(ActionChoices, uint, Empty) external {}
|
||||||
|
function g(Test, Nested) external {}
|
||||||
|
function h(function(Nested) external returns (uint)[]) external {}
|
||||||
|
function i(Nested[]) external {}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
SourceUnit const* sourceUnit = parseAndAnalyse(text);
|
||||||
|
for (ASTPointer<ASTNode> const& node: sourceUnit->nodes())
|
||||||
|
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
|
||||||
|
{
|
||||||
|
auto functions = contract->definedFunctions();
|
||||||
|
BOOST_REQUIRE(!functions.empty());
|
||||||
|
BOOST_CHECK_EQUAL("f(Test.ActionChoices,uint256,Test.Empty)", functions[0]->externalSignature());
|
||||||
|
BOOST_CHECK_EQUAL("g(Test,Test.Nested)", functions[1]->externalSignature());
|
||||||
|
BOOST_CHECK_EQUAL("h(function[])", functions[2]->externalSignature());
|
||||||
|
BOOST_CHECK_EQUAL("i(Test.Nested[])", functions[3]->externalSignature());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(struct_with_mapping_in_library)
|
||||||
|
{
|
||||||
|
char const* text = R"(
|
||||||
|
library Test {
|
||||||
|
struct Nested { mapping(uint => uint)[2][] a; uint y; }
|
||||||
|
struct X { Nested n; }
|
||||||
|
function f(X storage x) external {}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
SourceUnit const* sourceUnit = parseAndAnalyse(text);
|
||||||
|
for (ASTPointer<ASTNode> const& node: sourceUnit->nodes())
|
||||||
|
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
|
||||||
|
{
|
||||||
|
auto functions = contract->definedFunctions();
|
||||||
|
BOOST_REQUIRE(!functions.empty());
|
||||||
|
BOOST_CHECK_EQUAL("f(Test.X storage)", functions[0]->externalSignature());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(functions_with_identical_structs_in_interface)
|
||||||
|
{
|
||||||
|
char const* text = R"(
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
struct S1 { }
|
||||||
|
struct S2 { }
|
||||||
|
function f(S1) pure {}
|
||||||
|
function f(S2) pure {}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
CHECK_ERROR(text, TypeError, "Function overload clash during conversion to external types for arguments");
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(functions_with_different_structs_in_interface)
|
||||||
|
{
|
||||||
|
char const* text = R"(
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
struct S1 { function() external a; }
|
||||||
|
struct S2 { bytes24 a; }
|
||||||
|
function f(S1) pure {}
|
||||||
|
function f(S2) pure {}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
CHECK_SUCCESS(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(functions_with_stucts_of_non_external_types_in_interface)
|
||||||
|
{
|
||||||
|
char const* text = R"(
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
struct S { function() internal a; }
|
||||||
|
function f(S) {}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
CHECK_ERROR(text, TypeError, "Internal or recursive type is not allowed for public or external functions.");
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(functions_with_stucts_of_non_external_types_in_interface_2)
|
||||||
|
{
|
||||||
|
char const* text = R"(
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
struct S { mapping(uint => uint) a; }
|
||||||
|
function f(S) {}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
CHECK_ERROR(text, TypeError, "Internal or recursive type is not allowed for public or external functions.");
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(functions_with_stucts_of_non_external_types_in_interface_nested)
|
||||||
|
{
|
||||||
|
char const* text = R"(
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
struct T { mapping(uint => uint) a; }
|
||||||
|
struct S { T[][2] b; }
|
||||||
|
function f(S) {}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
CHECK_ERROR(text, TypeError, "Internal or recursive type is not allowed for public or external functions.");
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(function_external_call_allowed_conversion)
|
BOOST_AUTO_TEST_CASE(function_external_call_allowed_conversion)
|
||||||
{
|
{
|
||||||
char const* text = R"(
|
char const* text = R"(
|
||||||
@ -980,24 +1123,24 @@ BOOST_AUTO_TEST_CASE(state_variable_accessors)
|
|||||||
FunctionTypePointer function = retrieveFunctionBySignature(*contract, "foo()");
|
FunctionTypePointer function = retrieveFunctionBySignature(*contract, "foo()");
|
||||||
BOOST_REQUIRE(function && function->hasDeclaration());
|
BOOST_REQUIRE(function && function->hasDeclaration());
|
||||||
auto returnParams = function->returnParameterTypes();
|
auto returnParams = function->returnParameterTypes();
|
||||||
BOOST_CHECK_EQUAL(returnParams.at(0)->canonicalName(false), "uint256");
|
BOOST_CHECK_EQUAL(returnParams.at(0)->canonicalName(), "uint256");
|
||||||
BOOST_CHECK(function->stateMutability() == StateMutability::View);
|
BOOST_CHECK(function->stateMutability() == StateMutability::View);
|
||||||
|
|
||||||
function = retrieveFunctionBySignature(*contract, "map(uint256)");
|
function = retrieveFunctionBySignature(*contract, "map(uint256)");
|
||||||
BOOST_REQUIRE(function && function->hasDeclaration());
|
BOOST_REQUIRE(function && function->hasDeclaration());
|
||||||
auto params = function->parameterTypes();
|
auto params = function->parameterTypes();
|
||||||
BOOST_CHECK_EQUAL(params.at(0)->canonicalName(false), "uint256");
|
BOOST_CHECK_EQUAL(params.at(0)->canonicalName(), "uint256");
|
||||||
returnParams = function->returnParameterTypes();
|
returnParams = function->returnParameterTypes();
|
||||||
BOOST_CHECK_EQUAL(returnParams.at(0)->canonicalName(false), "bytes4");
|
BOOST_CHECK_EQUAL(returnParams.at(0)->canonicalName(), "bytes4");
|
||||||
BOOST_CHECK(function->stateMutability() == StateMutability::View);
|
BOOST_CHECK(function->stateMutability() == StateMutability::View);
|
||||||
|
|
||||||
function = retrieveFunctionBySignature(*contract, "multiple_map(uint256,uint256)");
|
function = retrieveFunctionBySignature(*contract, "multiple_map(uint256,uint256)");
|
||||||
BOOST_REQUIRE(function && function->hasDeclaration());
|
BOOST_REQUIRE(function && function->hasDeclaration());
|
||||||
params = function->parameterTypes();
|
params = function->parameterTypes();
|
||||||
BOOST_CHECK_EQUAL(params.at(0)->canonicalName(false), "uint256");
|
BOOST_CHECK_EQUAL(params.at(0)->canonicalName(), "uint256");
|
||||||
BOOST_CHECK_EQUAL(params.at(1)->canonicalName(false), "uint256");
|
BOOST_CHECK_EQUAL(params.at(1)->canonicalName(), "uint256");
|
||||||
returnParams = function->returnParameterTypes();
|
returnParams = function->returnParameterTypes();
|
||||||
BOOST_CHECK_EQUAL(returnParams.at(0)->canonicalName(false), "bytes4");
|
BOOST_CHECK_EQUAL(returnParams.at(0)->canonicalName(), "bytes4");
|
||||||
BOOST_CHECK(function->stateMutability() == StateMutability::View);
|
BOOST_CHECK(function->stateMutability() == StateMutability::View);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1072,7 +1215,7 @@ BOOST_AUTO_TEST_CASE(struct_accessor_one_array_only)
|
|||||||
Data public data;
|
Data public data;
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
CHECK_ERROR(sourceCode, TypeError, "Internal type is not allowed for public state variables.");
|
CHECK_ERROR(sourceCode, TypeError, "Internal or recursive type is not allowed for public state variables.");
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(base_class_state_variable_internal_member)
|
BOOST_AUTO_TEST_CASE(base_class_state_variable_internal_member)
|
||||||
@ -3283,7 +3426,7 @@ BOOST_AUTO_TEST_CASE(library_memory_struct)
|
|||||||
function f() public returns (S ) {}
|
function f() public returns (S ) {}
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
CHECK_ERROR(text, TypeError, "Internal type is not allowed for public or external functions.");
|
CHECK_SUCCESS(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(using_for_arbitrary_mismatch)
|
BOOST_AUTO_TEST_CASE(using_for_arbitrary_mismatch)
|
||||||
@ -4874,7 +5017,7 @@ BOOST_AUTO_TEST_CASE(internal_function_as_external_parameter)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
CHECK_ERROR(text, TypeError, "Internal type is not allowed for public or external functions.");
|
CHECK_ERROR(text, TypeError, "Internal or recursive type is not allowed for public or external functions.");
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(internal_function_returned_from_public_function)
|
BOOST_AUTO_TEST_CASE(internal_function_returned_from_public_function)
|
||||||
@ -4886,7 +5029,7 @@ BOOST_AUTO_TEST_CASE(internal_function_returned_from_public_function)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
CHECK_ERROR(text, TypeError, "Internal type is not allowed for public or external functions.");
|
CHECK_ERROR(text, TypeError, "Internal or recursive type is not allowed for public or external functions.");
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(internal_function_as_external_parameter_in_library_internal)
|
BOOST_AUTO_TEST_CASE(internal_function_as_external_parameter_in_library_internal)
|
||||||
@ -4908,7 +5051,7 @@ BOOST_AUTO_TEST_CASE(internal_function_as_external_parameter_in_library_external
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
CHECK_ERROR(text, TypeError, "Internal type is not allowed for public or external functions.");
|
CHECK_ERROR(text, TypeError, "Internal or recursive type is not allowed for public or external functions.");
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(function_type_arrays)
|
BOOST_AUTO_TEST_CASE(function_type_arrays)
|
||||||
@ -5396,6 +5539,56 @@ BOOST_AUTO_TEST_CASE(constructible_internal_constructor)
|
|||||||
success(text);
|
success(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(return_structs)
|
||||||
|
{
|
||||||
|
char const* text = R"(
|
||||||
|
contract C {
|
||||||
|
struct S { uint a; T[] sub; }
|
||||||
|
struct T { uint[] x; }
|
||||||
|
function f() returns (uint, S) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
success(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(return_recursive_structs)
|
||||||
|
{
|
||||||
|
char const* text = R"(
|
||||||
|
contract C {
|
||||||
|
struct S { uint a; S[] sub; }
|
||||||
|
function f() returns (uint, S) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
CHECK_ERROR(text, TypeError, "Internal or recursive type is not allowed for public or external functions.");
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(return_recursive_structs2)
|
||||||
|
{
|
||||||
|
char const* text = R"(
|
||||||
|
contract C {
|
||||||
|
struct S { uint a; S[2][] sub; }
|
||||||
|
function f() returns (uint, S) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
CHECK_ERROR(text, TypeError, "Internal or recursive type is not allowed for public or external functions.");
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(return_recursive_structs3)
|
||||||
|
{
|
||||||
|
char const* text = R"(
|
||||||
|
contract C {
|
||||||
|
struct S { uint a; S[][][] sub; }
|
||||||
|
struct T { S s; }
|
||||||
|
function f() returns (uint x, T t) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
CHECK_ERROR(text, TypeError, "Internal or recursive type is not allowed for public or external functions.");
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(address_checksum_type_deduction)
|
BOOST_AUTO_TEST_CASE(address_checksum_type_deduction)
|
||||||
{
|
{
|
||||||
char const* text = R"(
|
char const* text = R"(
|
||||||
|
Loading…
Reference in New Issue
Block a user