mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge remote-tracking branch 'origin/develop' into breaking
This commit is contained in:
commit
a0a02f2307
.circleci
Changelog.mddocs
libsolidity
libyul/backends/wasm
scripts
test
cmdlineTests
name_simplifier
standard_generatedSources
standard_optimizer_generatedSources
yul_unimplemented
libsolidity
gasTests
semanticTests
abiEncoderV1
abi_encode.solabi_encode_decode_simple.solabi_encode_rational.solcalldata_arrays_too_large.soldecode_slice.sol
abiEncoderV2
abi_encode_empty_string_v2.solabi_encode_rational_v2.solbool_out_of_bounds.solcalldata_array.solcalldata_array_dynamic_static_short_decode.sol
cleanup
enums.solstruct
accessor
arithmetics
array
calldata_array.solcalldata_array_dynamic_invalid.solcalldata_array_of_struct.solcalldata_slice_access.sol
copying
array_nested_calldata_to_memory.solarray_nested_calldata_to_storage.solarray_of_struct_calldata_to_memory.solarray_of_structs_containing_arrays_calldata_to_memory.solarray_of_structs_containing_arrays_calldata_to_storage.solarray_storage_multi_items_per_slot.solbytes_memory_to_storage.solbytes_storage_to_memory.solcalldata_array_of_struct_to_memory.solcalldata_array_static_to_memory.solcalldata_bytes_to_storage.solcalldata_dynamic_array_to_memory.solcopy_internal_function_array_to_storage.solstorage_memory_packed.sol
create_dynamic_array_with_zero_length.solcreate_memory_array_too_large.solcreate_multiple_dynamic_arrays.solevm_exceptions_out_of_band_access.solexternal_array_args.solfixed_array_cleanup.solfixed_arrays_in_storage.solfixed_bytes_length_access.solfixed_out_of_bounds_array_access.solfunction_memory_array.solindexAccess
inline_array_return.solinline_array_singleton.solinline_array_storage_to_memory_conversion_ints.solinline_array_storage_to_memory_conversion_strings.solinline_array_strings_from_document.solpop
array_pop_empty_exception.solarray_pop_isolated.solbyte_array_pop.solbyte_array_pop_empty_exception.solbyte_array_pop_isolated.solbyte_array_pop_storage_empty.sol
push
@ -332,6 +332,9 @@ jobs:
|
||||
- run:
|
||||
name: checking shell scripts
|
||||
command: ./scripts/chk_shellscripts/chk_shellscripts.sh
|
||||
- run:
|
||||
name: Check for broken symlinks
|
||||
command: ./scripts/check_symlinks.sh
|
||||
|
||||
chk_errorcodes:
|
||||
docker:
|
||||
|
@ -27,6 +27,9 @@ AST Changes:
|
||||
|
||||
### 0.7.6 (unreleased)
|
||||
|
||||
Compiler Features:
|
||||
* SMTChecker: Support named arguments in function calls.
|
||||
* SMTChecker: Support struct constructor.
|
||||
|
||||
### 0.7.5 (2020-11-18)
|
||||
|
||||
|
@ -33,6 +33,13 @@ that call them, similar to internal library functions.
|
||||
}
|
||||
}
|
||||
|
||||
.. note::
|
||||
Functions defined outside a contract are still always executed
|
||||
in the context of a contract. They still have access to the variable ``this``,
|
||||
can call other contracts, send them Ether and destroy the contract that called them,
|
||||
among other things. The main difference to functions defined inside a contract
|
||||
is that free functions do not have direct access to storage variables and functions
|
||||
not in their scope.
|
||||
|
||||
.. _function-parameters-return-variables:
|
||||
|
||||
|
@ -30,7 +30,7 @@ Team Calls
|
||||
If you have issues or pull requests to discuss, or are interested in hearing what
|
||||
the team and contributors are working on, you can join our public team calls:
|
||||
|
||||
- Mondays at 12pm CET/CEST.
|
||||
- Mondays at 3pm CET/CEST.
|
||||
- Wednesdays at 2pm CET/CEST.
|
||||
|
||||
Both calls take place on `Google Meet <https://meet.google.com/mrq-kbwv-edg>`_.
|
||||
|
@ -39,6 +39,7 @@ Getting Started
|
||||
|
||||
If you are new to the concept of smart contracts we recommend you to get started by digging
|
||||
into the "Introduction to Smart Contracts" section, which covers:
|
||||
|
||||
* :ref:`A simple example smart contract <simple-smart-contract>` written in Solidity.
|
||||
* :ref:`Blockchain Basics <blockchain-basics>`.
|
||||
* :ref:`The Ethereum Virtual Machine <the-ethereum-virtual-machine>`.
|
||||
|
@ -1,3 +1,6 @@
|
||||
|
||||
.. index: calldata layout
|
||||
|
||||
*******************
|
||||
Layout of Call Data
|
||||
*******************
|
||||
|
@ -36,4 +36,37 @@ elements.
|
||||
definitely zeroed out memory area, using such a pointer non-temporarily
|
||||
without updating the free memory pointer can have unexpected results.
|
||||
|
||||
.. index: calldata layout
|
||||
|
||||
Differences to Layout in Storage
|
||||
================================
|
||||
|
||||
As described above the layout in memory is different from the layout in
|
||||
:ref:`storage<storage-inplace-encoding>`. Below there are some examples.
|
||||
|
||||
Example for Difference in Arrays
|
||||
--------------------------------
|
||||
|
||||
The following array occupies 32 bytes (1 slot) in storage, but 128
|
||||
bytes (4 items with 32 bytes each) in memory.
|
||||
|
||||
::
|
||||
|
||||
uint8[4] a;
|
||||
|
||||
|
||||
|
||||
Example for Difference in Struct Layout
|
||||
---------------------------------------
|
||||
|
||||
The following struct occupies 96 bytes (3 slots of 32 bytes) in storage,
|
||||
but 128 bytes (4 items with 32 bytes each) in memory.
|
||||
|
||||
|
||||
::
|
||||
|
||||
struct S {
|
||||
uint a;
|
||||
uint b;
|
||||
uint8 c;
|
||||
uint8 d;
|
||||
}
|
||||
|
@ -198,11 +198,11 @@ vector<pair<util::FixedHash<4>, FunctionTypePointer>> const& ContractDefinition:
|
||||
});
|
||||
}
|
||||
|
||||
uint64_t ContractDefinition::interfaceId() const
|
||||
uint32_t ContractDefinition::interfaceId() const
|
||||
{
|
||||
uint64_t result{0};
|
||||
uint32_t result{0};
|
||||
for (auto const& function: interfaceFunctionList(false))
|
||||
result ^= util::fromBigEndian<uint64_t>(function.first.ref());
|
||||
result ^= util::fromBigEndian<uint32_t>(function.first.ref());
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -752,6 +752,44 @@ FunctionCallAnnotation& FunctionCall::annotation() const
|
||||
return initAnnotation<FunctionCallAnnotation>();
|
||||
}
|
||||
|
||||
vector<ASTPointer<Expression const>> FunctionCall::sortedArguments() const
|
||||
{
|
||||
// normal arguments
|
||||
if (m_names.empty())
|
||||
return arguments();
|
||||
|
||||
// named arguments
|
||||
FunctionTypePointer functionType;
|
||||
if (*annotation().kind == FunctionCallKind::StructConstructorCall)
|
||||
{
|
||||
auto const& type = dynamic_cast<TypeType const&>(*m_expression->annotation().type);
|
||||
auto const& structType = dynamic_cast<StructType const&>(*type.actualType());
|
||||
functionType = structType.constructorType();
|
||||
}
|
||||
else
|
||||
functionType = dynamic_cast<FunctionType const*>(m_expression->annotation().type);
|
||||
|
||||
vector<ASTPointer<Expression const>> sorted;
|
||||
for (auto const& parameterName: functionType->parameterNames())
|
||||
{
|
||||
bool found = false;
|
||||
for (size_t j = 0; j < m_names.size() && !found; j++)
|
||||
if ((found = (parameterName == *m_names.at(j))))
|
||||
// we found the actual parameter position
|
||||
sorted.push_back(m_arguments.at(j));
|
||||
solAssert(found, "");
|
||||
}
|
||||
|
||||
if (!functionType->takesArbitraryParameters())
|
||||
{
|
||||
solAssert(m_arguments.size() == functionType->parameterTypes().size(), "");
|
||||
solAssert(m_arguments.size() == m_names.size(), "");
|
||||
solAssert(m_arguments.size() == sorted.size(), "");
|
||||
}
|
||||
|
||||
return sorted;
|
||||
}
|
||||
|
||||
IdentifierAnnotation& Identifier::annotation() const
|
||||
{
|
||||
return initAnnotation<IdentifierAnnotation>();
|
||||
|
@ -515,7 +515,7 @@ public:
|
||||
std::map<util::FixedHash<4>, FunctionTypePointer> interfaceFunctions(bool _includeInheritedFunctions = true) const;
|
||||
std::vector<std::pair<util::FixedHash<4>, FunctionTypePointer>> const& interfaceFunctionList(bool _includeInheritedFunctions = true) const;
|
||||
/// @returns the EIP-165 compatible interface identifier. This will exclude inherited functions.
|
||||
uint64_t interfaceId() const;
|
||||
uint32_t interfaceId() const;
|
||||
|
||||
/// @returns a list of all declarations in this contract
|
||||
std::vector<Declaration const*> declarations() const { return filteredNodes<Declaration>(m_subNodes); }
|
||||
@ -1936,7 +1936,13 @@ public:
|
||||
void accept(ASTConstVisitor& _visitor) const override;
|
||||
|
||||
Expression const& expression() const { return *m_expression; }
|
||||
/// @returns the given arguments in the order they were written.
|
||||
std::vector<ASTPointer<Expression const>> arguments() const { return {m_arguments.begin(), m_arguments.end()}; }
|
||||
/// @returns the given arguments sorted by how the called function takes them.
|
||||
std::vector<ASTPointer<Expression const>> sortedArguments() const;
|
||||
/// @returns the list of given argument names if this is a named call,
|
||||
/// in the order they were written.
|
||||
/// If this is not a named call, this is empty.
|
||||
std::vector<ASTPointer<ASTString>> const& names() const { return m_names; }
|
||||
|
||||
FunctionCallAnnotation& annotation() const override;
|
||||
|
@ -3002,6 +3002,11 @@ TypePointers FunctionType::parameterTypes() const
|
||||
return TypePointers(m_parameterTypes.cbegin() + 1, m_parameterTypes.cend());
|
||||
}
|
||||
|
||||
TypePointers const& FunctionType::parameterTypesIncludingSelf() const
|
||||
{
|
||||
return m_parameterTypes;
|
||||
}
|
||||
|
||||
string FunctionType::richIdentifier() const
|
||||
{
|
||||
string id = "t_function_";
|
||||
@ -3247,8 +3252,7 @@ vector<tuple<string, TypePointer>> FunctionType::makeStackItems() const
|
||||
if (m_saltSet)
|
||||
slots.emplace_back("salt", TypeProvider::fixedBytes(32));
|
||||
if (bound())
|
||||
for (auto const& [boundName, boundType]: m_parameterTypes.front()->stackItems())
|
||||
slots.emplace_back("self_" + boundName, boundType);
|
||||
slots.emplace_back("self", m_parameterTypes.front());
|
||||
return slots;
|
||||
}
|
||||
|
||||
|
@ -1239,6 +1239,7 @@ public:
|
||||
static FunctionTypePointer newExpressionType(ContractDefinition const& _contract);
|
||||
|
||||
TypePointers parameterTypes() const;
|
||||
TypePointers const& parameterTypesIncludingSelf() const;
|
||||
std::vector<std::string> parameterNames() const;
|
||||
TypePointers const& returnParameterTypes() const { return m_returnParameterTypes; }
|
||||
/// @returns the list of return parameter types. All dynamically-sized types (this excludes
|
||||
|
@ -42,6 +42,7 @@ string ABIFunctions::tupleEncoder(
|
||||
bool _reversed
|
||||
)
|
||||
{
|
||||
solAssert(_givenTypes.size() == _targetTypes.size(), "");
|
||||
EncodingOptions options;
|
||||
options.encodeAsLibraryTypes = _encodeAsLibraryTypes;
|
||||
options.encodeFunctionFromStack = true;
|
||||
@ -1078,8 +1079,6 @@ string ABIFunctions::abiDecodingFunction(Type const& _type, bool _fromMemory, bo
|
||||
solAssert(!_fromMemory, "");
|
||||
return abiDecodingFunctionCalldataArray(*arrayType);
|
||||
}
|
||||
else if (arrayType->isByteArray())
|
||||
return abiDecodingFunctionByteArray(*arrayType, _fromMemory);
|
||||
else
|
||||
return abiDecodingFunctionArray(*arrayType, _fromMemory);
|
||||
}
|
||||
@ -1132,36 +1131,21 @@ string ABIFunctions::abiDecodingFunctionValueType(Type const& _type, bool _fromM
|
||||
string ABIFunctions::abiDecodingFunctionArray(ArrayType const& _type, bool _fromMemory)
|
||||
{
|
||||
solAssert(_type.dataStoredIn(DataLocation::Memory), "");
|
||||
solAssert(!_type.isByteArray(), "");
|
||||
|
||||
string functionName =
|
||||
"abi_decode_" +
|
||||
_type.identifier() +
|
||||
(_fromMemory ? "_fromMemory" : "");
|
||||
|
||||
solAssert(!_type.dataStoredIn(DataLocation::Storage), "");
|
||||
|
||||
return createFunction(functionName, [&]() {
|
||||
string load = _fromMemory ? "mload" : "calldataload";
|
||||
bool dynamicBase = _type.baseType()->isDynamicallyEncoded();
|
||||
Whiskers templ(
|
||||
R"(
|
||||
// <readableTypeName>
|
||||
function <functionName>(offset, end) -> array {
|
||||
if iszero(slt(add(offset, 0x1f), end)) { <revertString> }
|
||||
let length := <retrieveLength>
|
||||
array := <allocate>(<allocationSize>(length))
|
||||
let dst := array
|
||||
<storeLength> // might update offset and dst
|
||||
let src := offset
|
||||
<staticBoundsCheck>
|
||||
for { let i := 0 } lt(i, length) { i := add(i, 1) }
|
||||
{
|
||||
let elementPos := <retrieveElementPos>
|
||||
mstore(dst, <decodingFun>(elementPos, end))
|
||||
dst := add(dst, 0x20)
|
||||
src := add(src, <stride>)
|
||||
}
|
||||
array := <abiDecodeAvailableLen>(<offset>, length, end)
|
||||
}
|
||||
)"
|
||||
);
|
||||
@ -1169,18 +1153,56 @@ string ABIFunctions::abiDecodingFunctionArray(ArrayType const& _type, bool _from
|
||||
templ("revertString", revertReasonIfDebug("ABI decoding: invalid calldata array offset"));
|
||||
templ("functionName", functionName);
|
||||
templ("readableTypeName", _type.toString(true));
|
||||
templ("retrieveLength", !_type.isDynamicallySized() ? toCompactHexWithPrefix(_type.length()) : load + "(offset)");
|
||||
templ("retrieveLength", _type.isDynamicallySized() ? (load + "(offset)") : toCompactHexWithPrefix(_type.length()));
|
||||
templ("offset", _type.isDynamicallySized() ? "add(offset, 0x20)" : "offset");
|
||||
templ("abiDecodeAvailableLen", abiDecodingFunctionArrayAvailableLength(_type, _fromMemory));
|
||||
return templ.render();
|
||||
});
|
||||
}
|
||||
|
||||
string ABIFunctions::abiDecodingFunctionArrayAvailableLength(ArrayType const& _type, bool _fromMemory)
|
||||
{
|
||||
solAssert(_type.dataStoredIn(DataLocation::Memory), "");
|
||||
if (_type.isByteArray())
|
||||
return abiDecodingFunctionByteArrayAvailableLength(_type, _fromMemory);
|
||||
|
||||
string functionName =
|
||||
"abi_decode_available_length_" +
|
||||
_type.identifier() +
|
||||
(_fromMemory ? "_fromMemory" : "");
|
||||
|
||||
return createFunction(functionName, [&]() {
|
||||
Whiskers templ(R"(
|
||||
// <readableTypeName>
|
||||
function <functionName>(offset, length, end) -> array {
|
||||
array := <allocate>(<allocationSize>(length))
|
||||
let dst := array
|
||||
<storeLength>
|
||||
let src := offset
|
||||
<staticBoundsCheck>
|
||||
for { let i := 0 } lt(i, length) { i := add(i, 1) }
|
||||
{
|
||||
let elementPos := <retrieveElementPos>
|
||||
mstore(dst, <decodingFun>(elementPos, end))
|
||||
dst := add(dst, 0x20)
|
||||
src := add(src, <stride>)
|
||||
}
|
||||
}
|
||||
)");
|
||||
templ("functionName", functionName);
|
||||
templ("readableTypeName", _type.toString(true));
|
||||
templ("allocate", m_utils.allocationFunction());
|
||||
templ("allocationSize", m_utils.arrayAllocationSizeFunction(_type));
|
||||
string calldataStride = toCompactHexWithPrefix(_type.calldataStride());
|
||||
templ("stride", calldataStride);
|
||||
if (_type.isDynamicallySized())
|
||||
templ("storeLength", "mstore(array, length) offset := add(offset, 0x20) dst := add(dst, 0x20)");
|
||||
templ("storeLength", "mstore(array, length) dst := add(array, 0x20)");
|
||||
else
|
||||
templ("storeLength", "");
|
||||
if (dynamicBase)
|
||||
if (_type.baseType()->isDynamicallyEncoded())
|
||||
{
|
||||
templ("staticBoundsCheck", "");
|
||||
string load = _fromMemory ? "mload" : "calldataload";
|
||||
templ("retrieveElementPos", "add(offset, " + load + "(src))");
|
||||
}
|
||||
else
|
||||
@ -1244,36 +1266,28 @@ string ABIFunctions::abiDecodingFunctionCalldataArray(ArrayType const& _type)
|
||||
});
|
||||
}
|
||||
|
||||
string ABIFunctions::abiDecodingFunctionByteArray(ArrayType const& _type, bool _fromMemory)
|
||||
string ABIFunctions::abiDecodingFunctionByteArrayAvailableLength(ArrayType const& _type, bool _fromMemory)
|
||||
{
|
||||
solAssert(_type.dataStoredIn(DataLocation::Memory), "");
|
||||
solAssert(_type.isByteArray(), "");
|
||||
|
||||
string functionName =
|
||||
"abi_decode_" +
|
||||
"abi_decode_available_length_" +
|
||||
_type.identifier() +
|
||||
(_fromMemory ? "_fromMemory" : "");
|
||||
|
||||
return createFunction(functionName, [&]() {
|
||||
Whiskers templ(
|
||||
R"(
|
||||
function <functionName>(offset, end) -> array {
|
||||
if iszero(slt(add(offset, 0x1f), end)) { <revertStringOffset> }
|
||||
let length := <load>(offset)
|
||||
array := <allocate>(<allocationSize>(length))
|
||||
mstore(array, length)
|
||||
let src := add(offset, 0x20)
|
||||
let dst := add(array, 0x20)
|
||||
if gt(add(src, length), end) { <revertStringLength> }
|
||||
<copyToMemFun>(src, dst, length)
|
||||
}
|
||||
)"
|
||||
);
|
||||
// TODO add test
|
||||
templ("revertStringOffset", revertReasonIfDebug("ABI decoding: invalid byte array offset"));
|
||||
Whiskers templ(R"(
|
||||
function <functionName>(src, length, end) -> array {
|
||||
array := <allocate>(<allocationSize>(length))
|
||||
mstore(array, length)
|
||||
let dst := add(array, 0x20)
|
||||
if gt(add(src, length), end) { <revertStringLength> }
|
||||
<copyToMemFun>(src, dst, length)
|
||||
}
|
||||
)");
|
||||
templ("revertStringLength", revertReasonIfDebug("ABI decoding: invalid byte array length"));
|
||||
templ("functionName", functionName);
|
||||
templ("load", _fromMemory ? "mload" : "calldataload");
|
||||
templ("allocate", m_utils.allocationFunction());
|
||||
templ("allocationSize", m_utils.arrayAllocationSizeFunction(_type));
|
||||
templ("copyToMemFun", m_utils.copyToMemoryFunction(!_fromMemory));
|
||||
|
@ -165,6 +165,11 @@ public:
|
||||
EncodingOptions const& _options
|
||||
);
|
||||
|
||||
/// Decodes array in case of dynamic arrays with offset pointing to
|
||||
/// data and length already on stack
|
||||
/// signature: (dataOffset, length, dataEnd) -> decodedArray
|
||||
std::string abiDecodingFunctionArrayAvailableLength(ArrayType const& _type, bool _fromMemory);
|
||||
|
||||
private:
|
||||
/// Part of @a abiEncodingFunction for array target type and given calldata array.
|
||||
/// Uses calldatacopy and does not perform cleanup or validation and can therefore only
|
||||
@ -234,15 +239,14 @@ private:
|
||||
std::string abiDecodingFunctionArray(ArrayType const& _type, bool _fromMemory);
|
||||
/// Part of @a abiDecodingFunction for calldata array types.
|
||||
std::string abiDecodingFunctionCalldataArray(ArrayType const& _type);
|
||||
/// Part of @a abiDecodingFunction for byte array types.
|
||||
std::string abiDecodingFunctionByteArray(ArrayType const& _type, bool _fromMemory);
|
||||
/// Part of @a abiDecodingFunctionArrayWithAvailableLength
|
||||
std::string abiDecodingFunctionByteArrayAvailableLength(ArrayType const& _type, bool _fromMemory);
|
||||
/// Part of @a abiDecodingFunction for calldata struct types.
|
||||
std::string abiDecodingFunctionCalldataStruct(StructType const& _type);
|
||||
/// Part of @a abiDecodingFunction for struct types.
|
||||
std::string abiDecodingFunctionStruct(StructType const& _type, bool _fromMemory);
|
||||
/// Part of @a abiDecodingFunction for array types.
|
||||
std::string abiDecodingFunctionFunctionType(FunctionType const& _type, bool _fromMemory, bool _forUseOnStack);
|
||||
|
||||
/// Part of @a abiDecodingFunction for struct types.
|
||||
std::string abiDecodingFunctionStruct(StructType const& _type, bool _fromMemory);
|
||||
/// @returns the name of a function that retrieves an element from calldata.
|
||||
std::string calldataAccessFunction(Type const& _type);
|
||||
|
||||
|
@ -558,26 +558,8 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
||||
functionType = dynamic_cast<FunctionType const*>(_functionCall.expression().annotation().type);
|
||||
|
||||
TypePointers parameterTypes = functionType->parameterTypes();
|
||||
vector<ASTPointer<Expression const>> const& callArguments = _functionCall.arguments();
|
||||
vector<ASTPointer<ASTString>> const& callArgumentNames = _functionCall.names();
|
||||
if (!functionType->takesArbitraryParameters())
|
||||
solAssert(callArguments.size() == parameterTypes.size(), "");
|
||||
|
||||
vector<ASTPointer<Expression const>> arguments;
|
||||
if (callArgumentNames.empty())
|
||||
// normal arguments
|
||||
arguments = callArguments;
|
||||
else
|
||||
// named arguments
|
||||
for (auto const& parameterName: functionType->parameterNames())
|
||||
{
|
||||
bool found = false;
|
||||
for (size_t j = 0; j < callArgumentNames.size() && !found; j++)
|
||||
if ((found = (parameterName == *callArgumentNames[j])))
|
||||
// we found the actual parameter position
|
||||
arguments.push_back(callArguments[j]);
|
||||
solAssert(found, "");
|
||||
}
|
||||
vector<ASTPointer<Expression const>> const& arguments = _functionCall.sortedArguments();
|
||||
|
||||
if (functionCallKind == FunctionCallKind::StructConstructorCall)
|
||||
{
|
||||
|
@ -3170,6 +3170,12 @@ string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to)
|
||||
solAssert(false, "Invalid conversion from " + _from.canonicalName() + " to " + _to.canonicalName());
|
||||
break;
|
||||
}
|
||||
case Type::Category::Mapping:
|
||||
{
|
||||
solAssert(_from == _to, "");
|
||||
body = "converted := value";
|
||||
break;
|
||||
}
|
||||
default:
|
||||
solAssert(false, "Invalid conversion from " + _from.canonicalName() + " to " + _to.canonicalName());
|
||||
}
|
||||
@ -3182,7 +3188,8 @@ string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to)
|
||||
|
||||
string YulUtilFunctions::arrayConversionFunction(ArrayType const& _from, ArrayType const& _to)
|
||||
{
|
||||
solUnimplementedAssert(_to.location() != DataLocation::CallData, "Conversion of calldata types not yet implemented.");
|
||||
solAssert(_to.location() != DataLocation::CallData, "");
|
||||
|
||||
// Other cases are done explicitly in LValue::storeValue, and only possible by assignment.
|
||||
if (_to.location() == DataLocation::Storage)
|
||||
solAssert(
|
||||
@ -3190,12 +3197,6 @@ string YulUtilFunctions::arrayConversionFunction(ArrayType const& _from, ArrayTy
|
||||
_from.location() == DataLocation::Storage,
|
||||
"Invalid conversion to storage type."
|
||||
);
|
||||
if (_to.location() == DataLocation::Memory && _from.location() == DataLocation::CallData)
|
||||
{
|
||||
solUnimplementedAssert(_from.isDynamicallySized(), "");
|
||||
solUnimplementedAssert(!_from.baseType()->isDynamicallyEncoded(), "");
|
||||
solUnimplementedAssert(_from.isByteArray() && _to.isByteArray() && _to.isDynamicallySized(), "");
|
||||
}
|
||||
|
||||
string functionName =
|
||||
"convert_array_" +
|
||||
@ -3223,19 +3224,31 @@ string YulUtilFunctions::arrayConversionFunction(ArrayType const& _from, ArrayTy
|
||||
"body",
|
||||
Whiskers(R"(
|
||||
// Copy the array to a free position in memory
|
||||
converted :=
|
||||
<?fromStorage>
|
||||
converted := <arrayStorageToMem>(value)
|
||||
<arrayStorageToMem>(value)
|
||||
</fromStorage>
|
||||
<?fromCalldata>
|
||||
converted := <allocateMemoryArray>(length)
|
||||
<copyToMemory>(value, add(converted, 0x20), length)
|
||||
<abiDecode>(value, <length>, calldatasize())
|
||||
</fromCalldata>
|
||||
)")
|
||||
("fromStorage", _from.dataStoredIn(DataLocation::Storage))
|
||||
("fromCalldata", _from.dataStoredIn(DataLocation::CallData))
|
||||
("allocateMemoryArray", _from.dataStoredIn(DataLocation::CallData) ? allocateMemoryArrayFunction(_to) : "")
|
||||
("copyToMemory", _from.dataStoredIn(DataLocation::CallData) ? copyToMemoryFunction(true) : "")
|
||||
("arrayStorageToMem", _from.dataStoredIn(DataLocation::Storage) ? copyArrayFromStorageToMemoryFunction(_from, _to) : "")
|
||||
("length", _from.isDynamicallySized() ? "length" : _from.length().str())
|
||||
(
|
||||
"abiDecode",
|
||||
_from.dataStoredIn(DataLocation::CallData) ?
|
||||
ABIFunctions(
|
||||
m_evmVersion,
|
||||
m_revertStrings,
|
||||
m_functionCollector
|
||||
).abiDecodingFunctionArrayAvailableLength(_to, false) :
|
||||
""
|
||||
)
|
||||
(
|
||||
"arrayStorageToMem",
|
||||
_from.dataStoredIn(DataLocation::Storage) ? copyArrayFromStorageToMemoryFunction(_from, _to) : ""
|
||||
)
|
||||
.render()
|
||||
);
|
||||
else
|
||||
|
@ -28,7 +28,7 @@ using namespace solidity::frontend;
|
||||
YulArity YulArity::fromType(FunctionType const& _functionType)
|
||||
{
|
||||
return YulArity{
|
||||
TupleType(_functionType.parameterTypes()).sizeOnStack(),
|
||||
TupleType(_functionType.parameterTypesIncludingSelf()).sizeOnStack(),
|
||||
TupleType(_functionType.returnParameterTypes()).sizeOnStack()
|
||||
};
|
||||
}
|
||||
|
@ -241,6 +241,10 @@ void IRGeneratorForStatements::initializeStateVar(VariableDeclaration const& _va
|
||||
return;
|
||||
|
||||
_varDecl.value()->accept(*this);
|
||||
|
||||
Type const* rightIntermediateType = _varDecl.value()->annotation().type->closestTemporaryType(_varDecl.type());
|
||||
solAssert(rightIntermediateType, "");
|
||||
IRVariable value = convert(*_varDecl.value(), *rightIntermediateType);
|
||||
writeToLValue(
|
||||
_varDecl.immutable() ?
|
||||
IRLValue{*_varDecl.annotation().type, IRLValue::Immutable{&_varDecl}} :
|
||||
@ -248,7 +252,7 @@ void IRGeneratorForStatements::initializeStateVar(VariableDeclaration const& _va
|
||||
util::toCompactHexWithPrefix(m_context.storageLocationOfStateVariable(_varDecl).first),
|
||||
m_context.storageLocationOfStateVariable(_varDecl).second
|
||||
}},
|
||||
*_varDecl.value()
|
||||
value
|
||||
);
|
||||
}
|
||||
catch (langutil::UnimplementedFeatureError const& _error)
|
||||
@ -826,7 +830,6 @@ bool IRGeneratorForStatements::visit(FunctionCall const& _functionCall)
|
||||
if (
|
||||
functionType &&
|
||||
functionType->kind() == FunctionType::Kind::Internal &&
|
||||
!functionType->bound() &&
|
||||
IRHelpers::referencedFunctionDeclaration(_functionCall.expression())
|
||||
)
|
||||
m_context.internalFunctionCalledDirectly(_functionCall.expression());
|
||||
@ -861,26 +864,8 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
||||
functionType = dynamic_cast<FunctionType const*>(_functionCall.expression().annotation().type);
|
||||
|
||||
TypePointers parameterTypes = functionType->parameterTypes();
|
||||
vector<ASTPointer<Expression const>> const& callArguments = _functionCall.arguments();
|
||||
vector<ASTPointer<ASTString>> const& callArgumentNames = _functionCall.names();
|
||||
if (!functionType->takesArbitraryParameters())
|
||||
solAssert(callArguments.size() == parameterTypes.size(), "");
|
||||
|
||||
vector<ASTPointer<Expression const>> arguments;
|
||||
if (callArgumentNames.empty())
|
||||
// normal arguments
|
||||
arguments = callArguments;
|
||||
else
|
||||
// named arguments
|
||||
for (auto const& parameterName: functionType->parameterNames())
|
||||
{
|
||||
auto const it = std::find_if(callArgumentNames.cbegin(), callArgumentNames.cend(), [&](ASTPointer<ASTString> const& _argName) {
|
||||
return *_argName == parameterName;
|
||||
});
|
||||
|
||||
solAssert(it != callArgumentNames.cend(), "");
|
||||
arguments.push_back(callArguments[static_cast<size_t>(std::distance(callArgumentNames.begin(), it))]);
|
||||
}
|
||||
vector<ASTPointer<Expression const>> const& arguments = _functionCall.sortedArguments();
|
||||
|
||||
if (functionCallKind == FunctionCallKind::StructConstructorCall)
|
||||
{
|
||||
@ -921,8 +906,6 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
||||
solAssert(functionType->kind() == FunctionType::Kind::Internal || functionType->kind() == FunctionType::Kind::DelegateCall, "");
|
||||
}
|
||||
}
|
||||
else
|
||||
solAssert(!functionType->bound(), "");
|
||||
|
||||
switch (functionType->kind())
|
||||
{
|
||||
@ -957,19 +940,17 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
||||
}
|
||||
|
||||
solAssert(functionDef && functionDef->isImplemented(), "");
|
||||
solAssert(
|
||||
functionDef->parameters().size() == arguments.size() + (functionType->bound() ? 1 : 0),
|
||||
""
|
||||
);
|
||||
}
|
||||
|
||||
solAssert(!functionType->takesArbitraryParameters(), "");
|
||||
|
||||
vector<string> args;
|
||||
if (functionType->bound())
|
||||
{
|
||||
solAssert(memberAccess && functionDef, "");
|
||||
solAssert(functionDef->parameters().size() == arguments.size() + 1, "");
|
||||
args += convert(memberAccess->expression(), *functionDef->parameters()[0]->type()).stackSlots();
|
||||
}
|
||||
else
|
||||
solAssert(!functionDef || functionDef->parameters().size() == arguments.size(), "");
|
||||
args += IRVariable(_functionCall.expression()).part("self").stackSlots();
|
||||
|
||||
for (size_t i = 0; i < arguments.size(); ++i)
|
||||
args += convert(*arguments[i], *parameterTypes[i]).stackSlots();
|
||||
@ -1585,6 +1566,23 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
|
||||
Type::Category::Array,
|
||||
Type::Category::FixedBytes,
|
||||
}).count(objectCategory) > 0, "");
|
||||
|
||||
define(IRVariable(_memberAccess).part("self"), _memberAccess.expression());
|
||||
auto const& functionDefinition = dynamic_cast<FunctionDefinition const&>(memberFunctionType->declaration());
|
||||
solAssert(*_memberAccess.annotation().requiredLookup == VirtualLookup::Static, "");
|
||||
if (memberFunctionType->kind() == FunctionType::Kind::Internal)
|
||||
{
|
||||
define(IRVariable(_memberAccess).part("functionIdentifier")) << to_string(functionDefinition.id()) << "\n";
|
||||
m_context.internalFunctionAccessed(_memberAccess, functionDefinition);
|
||||
}
|
||||
else
|
||||
{
|
||||
solAssert(memberFunctionType->kind() == FunctionType::Kind::DelegateCall, "");
|
||||
auto contract = dynamic_cast<ContractDefinition const*>(functionDefinition.scope());
|
||||
solAssert(contract && contract->isLibrary(), "");
|
||||
define(IRVariable(_memberAccess).part("address")) << linkerSymbol(*contract) << "\n";
|
||||
define(IRVariable(_memberAccess).part("functionSelector")) << memberFunctionType->externalIdentifier();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@ -2294,15 +2292,21 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
|
||||
"Can only be used for regular external calls."
|
||||
);
|
||||
|
||||
solUnimplementedAssert(!funType.bound(), "");
|
||||
|
||||
bool const isDelegateCall = funKind == FunctionType::Kind::DelegateCall;
|
||||
bool const useStaticCall = funType.stateMutability() <= StateMutability::View && m_context.evmVersion().hasStaticCall();
|
||||
|
||||
ReturnInfo const returnInfo{m_context.evmVersion(), funType};
|
||||
|
||||
TypePointers parameterTypes = funType.parameterTypes();
|
||||
TypePointers argumentTypes;
|
||||
vector<string> argumentStrings;
|
||||
if (funType.bound())
|
||||
{
|
||||
parameterTypes.insert(parameterTypes.begin(), funType.selfType());
|
||||
argumentTypes.emplace_back(funType.selfType());
|
||||
argumentStrings += IRVariable(_functionCall.expression()).part("self").stackSlots();
|
||||
}
|
||||
|
||||
for (auto const& arg: _arguments)
|
||||
{
|
||||
argumentTypes.emplace_back(&type(*arg));
|
||||
@ -2381,7 +2385,7 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
|
||||
bool encodeForLibraryCall = funKind == FunctionType::Kind::DelegateCall;
|
||||
|
||||
solAssert(funType.padArguments(), "");
|
||||
templ("encodeArgs", m_context.abiFunctions().tupleEncoder(argumentTypes, funType.parameterTypes(), encodeForLibraryCall));
|
||||
templ("encodeArgs", m_context.abiFunctions().tupleEncoder(argumentTypes, parameterTypes, encodeForLibraryCall));
|
||||
templ("argumentString", joinHumanReadablePrefixed(argumentStrings));
|
||||
|
||||
solAssert(!isDelegateCall || !funType.valueSet(), "Value set for delegatecall");
|
||||
|
@ -622,11 +622,7 @@ void SMTEncoder::endVisit(FunctionCall const& _funCall)
|
||||
createExpr(_funCall);
|
||||
if (functionCallKind == FunctionCallKind::StructConstructorCall)
|
||||
{
|
||||
m_errorReporter.warning(
|
||||
4639_error,
|
||||
_funCall.location(),
|
||||
"Assertion checker does not yet implement this expression."
|
||||
);
|
||||
visitStructConstructorCall(_funCall);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -856,6 +852,9 @@ void SMTEncoder::endVisit(Identifier const& _identifier)
|
||||
defineExpr(_identifier, m_context.state().thisAddress());
|
||||
m_uninterpretedTerms.insert(&_identifier);
|
||||
}
|
||||
// Ignore type identifiers
|
||||
else if (dynamic_cast<TypeType const*>(_identifier.annotation().type))
|
||||
return;
|
||||
// Ignore the builtin abi, it is handled in FunctionCall.
|
||||
// TODO: ignore MagicType in general (abi, block, msg, tx, type)
|
||||
else if (auto magicType = dynamic_cast<MagicType const*>(_identifier.annotation().type); magicType && magicType->kind() == MagicType::Kind::ABI)
|
||||
@ -1013,6 +1012,13 @@ void SMTEncoder::visitFunctionIdentifier(Identifier const& _identifier)
|
||||
}
|
||||
}
|
||||
|
||||
void SMTEncoder::visitStructConstructorCall(FunctionCall const& _funCall)
|
||||
{
|
||||
solAssert(*_funCall.annotation().kind == FunctionCallKind::StructConstructorCall, "");
|
||||
auto& structSymbolicVar = dynamic_cast<smt::SymbolicStructVariable&>(*m_context.expression(_funCall));
|
||||
structSymbolicVar.assignAllMembers(applyMap(_funCall.sortedArguments(), [this](auto const& arg) { return expr(*arg); }));
|
||||
}
|
||||
|
||||
void SMTEncoder::endVisit(Literal const& _literal)
|
||||
{
|
||||
solAssert(_literal.annotation().type, "Expected type for AST node");
|
||||
@ -1073,10 +1079,10 @@ void SMTEncoder::endVisit(Return const& _return)
|
||||
solAssert(types.size() == returnParams.size(), "");
|
||||
|
||||
for (unsigned i = 0; i < returnParams.size(); ++i)
|
||||
m_context.addAssertion(symbTuple->component(i, types.at(i), returnParams.at(i)->type()) == m_context.newValue(*returnParams.at(i)));
|
||||
assignment(*returnParams.at(i), symbTuple->component(i, types.at(i), returnParams.at(i)->type()));
|
||||
}
|
||||
else if (returnParams.size() == 1)
|
||||
m_context.addAssertion(expr(*_return.expression(), returnParams.front()->type()) == m_context.newValue(*returnParams.front()));
|
||||
assignment(*returnParams.front(), expr(*_return.expression(), returnParams.front()->type()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1292,8 +1298,7 @@ void SMTEncoder::indexOrMemberAssignment(Expression const& _expr, smtutil::Expre
|
||||
if (var->hasReferenceOrMappingType())
|
||||
resetReferences(*var);
|
||||
|
||||
m_context.addAssertion(m_context.newValue(*var) == _rightHandSide);
|
||||
m_context.expression(_expr)->increaseIndex();
|
||||
assignment(*var, _rightHandSide);
|
||||
defineExpr(_expr, currentValue(*var));
|
||||
return;
|
||||
}
|
||||
@ -1318,7 +1323,6 @@ void SMTEncoder::indexOrMemberAssignment(Expression const& _expr, smtutil::Expre
|
||||
smtutil::Expression(make_shared<smtutil::SortSort>(smt::smtSort(*baseType)), baseType->toString(true)),
|
||||
{smtutil::Expression::store(symbArray->elements(), indexExpr, toStore), symbArray->length()}
|
||||
);
|
||||
m_context.expression(*indexAccess)->increaseIndex();
|
||||
defineExpr(*indexAccess, smtutil::Expression::select(
|
||||
symbArray->elements(),
|
||||
indexExpr
|
||||
@ -1359,8 +1363,7 @@ void SMTEncoder::indexOrMemberAssignment(Expression const& _expr, smtutil::Expre
|
||||
if (varDecl->hasReferenceOrMappingType())
|
||||
resetReferences(*varDecl);
|
||||
|
||||
m_context.addAssertion(m_context.newValue(*varDecl) == toStore);
|
||||
m_context.expression(*id)->increaseIndex();
|
||||
assignment(*varDecl, toStore);
|
||||
defineExpr(*id, currentValue(*varDecl));
|
||||
break;
|
||||
}
|
||||
@ -1373,8 +1376,7 @@ void SMTEncoder::indexOrMemberAssignment(Expression const& _expr, smtutil::Expre
|
||||
)
|
||||
resetReferences(type);
|
||||
|
||||
m_context.expression(*lastExpr)->increaseIndex();
|
||||
m_context.addAssertion(expr(*lastExpr) == toStore);
|
||||
assignment(*m_context.expression(*lastExpr), toStore);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -1953,7 +1955,12 @@ void SMTEncoder::assignment(VariableDeclaration const& _variable, smtutil::Expre
|
||||
TypePointer type = _variable.type();
|
||||
if (type->category() == Type::Category::Mapping)
|
||||
arrayAssignment();
|
||||
m_context.addAssertion(m_context.newValue(_variable) == _value);
|
||||
assignment(*m_context.variable(_variable), _value);
|
||||
}
|
||||
|
||||
void SMTEncoder::assignment(smt::SymbolicVariable& _symVar, smtutil::Expression const& _value)
|
||||
{
|
||||
m_context.addAssertion(_symVar.increaseIndex() == _value);
|
||||
}
|
||||
|
||||
SMTEncoder::VariableIndices SMTEncoder::visitBranch(ASTNode const* _statement, smtutil::Expression _condition)
|
||||
@ -2525,8 +2532,8 @@ vector<smtutil::Expression> SMTEncoder::symbolicArguments(FunctionCall const& _f
|
||||
auto const& funType = dynamic_cast<FunctionType const*>(calledExpr->annotation().type);
|
||||
solAssert(funType, "");
|
||||
|
||||
vector<ASTPointer<Expression const>> arguments = _funCall.sortedArguments();
|
||||
auto const& functionParams = function->parameters();
|
||||
auto const& arguments = _funCall.arguments();
|
||||
unsigned firstParam = 0;
|
||||
if (funType->bound())
|
||||
{
|
||||
|
@ -151,6 +151,7 @@ protected:
|
||||
virtual void visitAddMulMod(FunctionCall const& _funCall);
|
||||
void visitObjectCreation(FunctionCall const& _funCall);
|
||||
void visitTypeConversion(FunctionCall const& _funCall);
|
||||
void visitStructConstructorCall(FunctionCall const& _funCall);
|
||||
void visitFunctionIdentifier(Identifier const& _identifier);
|
||||
|
||||
/// Encodes a modifier or function body according to the modifier
|
||||
@ -194,6 +195,10 @@ protected:
|
||||
IntegerType const& _type
|
||||
);
|
||||
|
||||
/// Handles the actual assertion of the new value to the encoding context.
|
||||
/// Other assignment methods should use this one in the end.
|
||||
void assignment(smt::SymbolicVariable& _symVar, smtutil::Expression const& _value);
|
||||
|
||||
void assignment(VariableDeclaration const& _variable, Expression const& _value);
|
||||
/// Handles assignments to variables of different types.
|
||||
void assignment(VariableDeclaration const& _variable, smtutil::Expression const& _value);
|
||||
|
@ -360,7 +360,7 @@ SymbolicStructVariable::SymbolicStructVariable(
|
||||
}
|
||||
}
|
||||
|
||||
smtutil::Expression SymbolicStructVariable::member(string const& _member)
|
||||
smtutil::Expression SymbolicStructVariable::member(string const& _member) const
|
||||
{
|
||||
return smtutil::Expression::tuple_get(currentValue(), m_memberIndices.at(_member));
|
||||
}
|
||||
@ -385,3 +385,18 @@ smtutil::Expression SymbolicStructVariable::assignMember(string const& _member,
|
||||
|
||||
return currentValue();
|
||||
}
|
||||
|
||||
smtutil::Expression SymbolicStructVariable::assignAllMembers(vector<smtutil::Expression> const& _memberValues)
|
||||
{
|
||||
auto structType = dynamic_cast<StructType const*>(m_type);
|
||||
solAssert(structType, "");
|
||||
|
||||
auto const& structDef = structType->structDefinition();
|
||||
auto const& structMembers = structDef.members();
|
||||
solAssert(_memberValues.size() == structMembers.size(), "");
|
||||
increaseIndex();
|
||||
for (unsigned i = 0; i < _memberValues.size(); ++i)
|
||||
m_context.addAssertion(_memberValues[i] == member(structMembers[i]->name()));
|
||||
|
||||
return currentValue();
|
||||
}
|
@ -282,12 +282,16 @@ public:
|
||||
);
|
||||
|
||||
/// @returns the symbolic expression representing _member.
|
||||
smtutil::Expression member(std::string const& _member);
|
||||
smtutil::Expression member(std::string const& _member) const;
|
||||
|
||||
/// @returns the symbolic expression representing this struct
|
||||
/// with field _member updated.
|
||||
smtutil::Expression assignMember(std::string const& _member, smtutil::Expression const& _memberValue);
|
||||
|
||||
/// @returns the symbolic expression representing this struct
|
||||
/// with all fields updated with the given values.
|
||||
smtutil::Expression assignAllMembers(std::vector<smtutil::Expression> const& _memberValues);
|
||||
|
||||
private:
|
||||
std::map<std::string, unsigned> m_memberIndices;
|
||||
};
|
||||
|
@ -345,8 +345,9 @@ bytes BinaryTransform::operator()(Literal const& _literal)
|
||||
|
||||
bytes BinaryTransform::operator()(StringLiteral const&)
|
||||
{
|
||||
// TODO is this used?
|
||||
yulAssert(false, "String literals not yet implemented");
|
||||
// StringLiteral is a special AST element used for certain builtins.
|
||||
// It is not mapped to actual WebAssembly, and should be processed in visit(BuiltinCall).
|
||||
yulAssert(false, "");
|
||||
}
|
||||
|
||||
bytes BinaryTransform::operator()(LocalVariable const& _variable)
|
||||
|
@ -101,6 +101,8 @@ string TextTransform::operator()(wasm::Literal const& _literal)
|
||||
|
||||
string TextTransform::operator()(wasm::StringLiteral const& _literal)
|
||||
{
|
||||
// StringLiteral is a special AST element used for certain builtins.
|
||||
// The output of this will not be valid WebAssembly.
|
||||
string quoted = boost::replace_all_copy(_literal.value, "\\", "\\\\");
|
||||
boost::replace_all(quoted, "\"", "\\\"");
|
||||
return "\"" + quoted + "\"";
|
||||
|
@ -63,6 +63,7 @@ using Expression = std::variant<
|
||||
>;
|
||||
|
||||
struct Literal { std::variant<uint32_t, uint64_t> value; };
|
||||
// This is a special AST element used for certain builtins. It is not mapped to actual WebAssembly.
|
||||
struct StringLiteral { std::string value; };
|
||||
struct LocalVariable { std::string name; };
|
||||
struct GlobalVariable { std::string name; };
|
||||
|
@ -55,7 +55,7 @@ function callvalue() -> z1, z2, z3, z4 {
|
||||
}
|
||||
|
||||
function calldataload(x1, x2, x3, x4) -> z1, z2, z3, z4 {
|
||||
eth.callDataCopy(0:i32, u256_to_i32(x1, x2, x3, x4), 32:i32)
|
||||
calldatacopy(0, 0, 0, 0, x1, x2, x3, x4, 0, 0, 0, 32)
|
||||
z1, z2, z3, z4 := mload_internal(0:i32)
|
||||
}
|
||||
|
||||
@ -64,11 +64,31 @@ function calldatasize() -> z1, z2, z3, z4 {
|
||||
}
|
||||
|
||||
function calldatacopy(x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4) {
|
||||
eth.callDataCopy(
|
||||
to_internal_i32ptr(x1, x2, x3, x4),
|
||||
u256_to_i32(y1, y2, y3, y4),
|
||||
u256_to_i32(z1, z2, z3, z4)
|
||||
)
|
||||
let cds:i32 := eth.getCallDataSize()
|
||||
let destination:i32 := u256_to_i32(x1, x2, x3, x4)
|
||||
let offset:i32 := u256_to_i32(y1, y2, y3, y4)
|
||||
let requested_size:i32 := u256_to_i32(z1, z2, z3, z4)
|
||||
// overflow?
|
||||
if i32.gt_u(offset, i32.sub(0xffffffff:i32, requested_size)) {
|
||||
eth.revert(0:i32, 0:i32)
|
||||
}
|
||||
|
||||
let available_size:i32 := i32.sub(cds, offset)
|
||||
if i32.gt_u(offset, cds) {
|
||||
available_size := 0:i32
|
||||
}
|
||||
|
||||
if i32.gt_u(available_size, 0:i32) {
|
||||
eth.callDataCopy(
|
||||
destination,
|
||||
offset,
|
||||
available_size
|
||||
)
|
||||
}
|
||||
|
||||
if i32.gt_u(requested_size, available_size) {
|
||||
memset(i32.add(destination, available_size), 0:i32, i32.sub(requested_size, available_size))
|
||||
}
|
||||
}
|
||||
|
||||
// Needed?
|
||||
|
@ -60,3 +60,10 @@ function pop(x1, x2, x3, x4) {
|
||||
function memoryguard(x:i64) -> y1, y2, y3, y4 {
|
||||
y4 := x
|
||||
}
|
||||
|
||||
function memset(ptr:i32, value:i32, length:i32) {
|
||||
for { let i:i32 := 0:i32 } i32.lt_u(i, length) { i := i32.add(i, 1:i32) }
|
||||
{
|
||||
i32.store8(i32.add(ptr, i), value)
|
||||
}
|
||||
}
|
@ -8,13 +8,10 @@ import json
|
||||
SOLC_BIN = sys.argv[1]
|
||||
REPORT_FILE = open("report.txt", mode="w", encoding='utf8', newline='\n')
|
||||
|
||||
def removeSMT(source):
|
||||
return source.replace('pragma experimental SMTChecker;', '')
|
||||
|
||||
for optimize in [False, True]:
|
||||
for f in sorted(glob.glob("*.sol")):
|
||||
sources = {}
|
||||
sources[f] = {'content': removeSMT(open(f, mode='r', encoding='utf8').read())}
|
||||
sources[f] = {'content': open(f, mode='r', encoding='utf8').read()}
|
||||
input_json = {
|
||||
'language': 'Solidity',
|
||||
'sources': sources,
|
||||
@ -23,6 +20,9 @@ for optimize in [False, True]:
|
||||
'enabled': optimize
|
||||
},
|
||||
'outputSelection': {'*': {'*': ['evm.bytecode.object', 'metadata']}}
|
||||
},
|
||||
'modelCheckerSettings': {
|
||||
"engine": 'none'
|
||||
}
|
||||
}
|
||||
args = [SOLC_BIN, '--standard-json']
|
||||
|
@ -1,43 +0,0 @@
|
||||
@ECHO OFF
|
||||
|
||||
REM ---------------------------------------------------------------------------
|
||||
REM This file is part of solidity.
|
||||
REM
|
||||
REM solidity is free software: you can redistribute it and/or modify
|
||||
REM it under the terms of the GNU General Public License as published by
|
||||
REM the Free Software Foundation, either version 3 of the License, or
|
||||
REM (at your option) any later version.
|
||||
REM
|
||||
REM solidity is distributed in the hope that it will be useful,
|
||||
REM but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
REM MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
REM GNU General Public License for more details.
|
||||
REM
|
||||
REM You should have received a copy of the GNU General Public License
|
||||
REM along with solidity. If not, see <http://www.gnu.org/licenses/>
|
||||
REM
|
||||
REM Copyright (c) 2017 solidity contributors.
|
||||
REM ---------------------------------------------------------------------------
|
||||
|
||||
set CONFIGURATION=%1
|
||||
set DIRECTORY=%2
|
||||
|
||||
mkdir bytecode
|
||||
cd bytecode
|
||||
..\scripts\isolate_tests.py ..\test\
|
||||
..\scripts\bytecodecompare\prepare_report.py ..\build\solc\%CONFIGURATION%\solc.exe
|
||||
|
||||
REM Send to stdout instead of stderr to not confuse powershell
|
||||
git clone --depth 2 git@github.com:ethereum/solidity-test-bytecode.git 2>&1
|
||||
cd solidity-test-bytecode
|
||||
git config user.name "travis"
|
||||
git config user.email "chris@ethereum.org"
|
||||
git clean -f -d -x 2>&1
|
||||
|
||||
if not exist %DIRECTORY% mkdir %DIRECTORY%
|
||||
set REPORT=%DIRECTORY%/windows.txt
|
||||
cp ../report.txt %REPORT%
|
||||
git add %REPORT% 2>$1
|
||||
git commit -a -m "Added report."
|
||||
git pull --rebase 2>&1
|
||||
git push origin 2>&1
|
@ -57,11 +57,6 @@ var fs = require('fs')
|
||||
|
||||
var compiler = require('./solc-js/wrapper.js')(require('./solc-js/soljson.js'))
|
||||
|
||||
function removeSMT(source)
|
||||
{
|
||||
return source.replace('pragma experimental SMTChecker;', '');
|
||||
}
|
||||
|
||||
for (var optimize of [false, true])
|
||||
{
|
||||
for (var filename of process.argv.slice(2))
|
||||
@ -69,13 +64,16 @@ for (var optimize of [false, true])
|
||||
if (filename !== undefined)
|
||||
{
|
||||
var inputs = {}
|
||||
inputs[filename] = { content: removeSMT(fs.readFileSync(filename).toString()) }
|
||||
inputs[filename] = { content: fs.readFileSync(filename).toString() }
|
||||
var input = {
|
||||
language: 'Solidity',
|
||||
sources: inputs,
|
||||
settings: {
|
||||
optimizer: { enabled: optimize },
|
||||
outputSelection: { '*': { '*': ['evm.bytecode.object', 'metadata'] } }
|
||||
},
|
||||
"modelCheckerSettings": {
|
||||
"engine": "none"
|
||||
}
|
||||
}
|
||||
var result = JSON.parse(compiler.compile(JSON.stringify(input)))
|
||||
|
17
scripts/check_symlinks.sh
Executable file
17
scripts/check_symlinks.sh
Executable file
@ -0,0 +1,17 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
REPO_ROOT="$(dirname "$0")"/..
|
||||
REPO_ROOT=$(realpath "${REPO_ROOT}")
|
||||
|
||||
BROKEN_LINKS=$(find -L "${REPO_ROOT}" -type l -ls)
|
||||
if [ -z "${BROKEN_LINKS}" ]
|
||||
then
|
||||
exit 0
|
||||
else
|
||||
echo "broken symbolic link(s) found:"
|
||||
echo "${BROKEN_LINKS}"
|
||||
|
||||
exit 1
|
||||
fi
|
@ -1 +1 @@
|
||||
../../scripts/travis-emscripten/build_emscripten.sh
|
||||
build_emscripten.sh
|
@ -12,3 +12,4 @@ errorstring
|
||||
hist
|
||||
otion
|
||||
keypair
|
||||
ether
|
||||
|
@ -19,52 +19,49 @@ object "C_59" {
|
||||
object "C_59_deployed" {
|
||||
code {
|
||||
{
|
||||
mstore(64, 128)
|
||||
let _1 := 64
|
||||
mstore(_1, 128)
|
||||
if iszero(lt(calldatasize(), 4))
|
||||
{
|
||||
let _1 := 0
|
||||
if eq(0xf8eddcc6, shr(224, calldataload(_1)))
|
||||
let _2 := 0
|
||||
if eq(0xf8eddcc6, shr(224, calldataload(_2)))
|
||||
{
|
||||
if callvalue() { revert(_1, _1) }
|
||||
let _2 := 32
|
||||
if slt(add(calldatasize(), not(3)), _2) { revert(_1, _1) }
|
||||
if callvalue() { revert(_2, _2) }
|
||||
let _3 := 32
|
||||
if slt(add(calldatasize(), not(3)), _3) { revert(_2, _2) }
|
||||
let offset := calldataload(4)
|
||||
let _3 := 0xffffffffffffffff
|
||||
if gt(offset, _3) { revert(_1, _1) }
|
||||
if iszero(slt(add(offset, 35), calldatasize())) { revert(_1, _1) }
|
||||
let length := calldataload(add(4, offset))
|
||||
if gt(length, _3) { panic_error_0x41() }
|
||||
let _4 := mul(length, _2)
|
||||
let dst := allocateMemory(add(_4, _2))
|
||||
let _4 := 0xffffffffffffffff
|
||||
if gt(offset, _4) { revert(_2, _2) }
|
||||
if iszero(slt(add(offset, 35), calldatasize())) { revert(_2, _2) }
|
||||
let _5 := calldataload(add(4, offset))
|
||||
if gt(_5, _4) { panic_error_0x41() }
|
||||
let _6 := mul(_5, _3)
|
||||
let dst := allocateMemory(add(_6, _3))
|
||||
let dst_1 := dst
|
||||
mstore(dst, length)
|
||||
dst := add(dst, _2)
|
||||
mstore(dst, _5)
|
||||
dst := add(dst, _3)
|
||||
let src := add(offset, 36)
|
||||
if gt(add(add(offset, _4), 36), calldatasize()) { revert(_1, _1) }
|
||||
let i := _1
|
||||
for { } lt(i, length) { i := add(i, 1) }
|
||||
if gt(add(add(offset, _6), 36), calldatasize()) { revert(_2, _2) }
|
||||
let i := _2
|
||||
for { } lt(i, _5) { i := add(i, 1) }
|
||||
{
|
||||
mstore(dst, abi_decode_t_struct$_S(src, calldatasize()))
|
||||
dst := add(dst, _2)
|
||||
src := add(src, _2)
|
||||
if slt(sub(calldatasize(), src), _3) { revert(_2, _2) }
|
||||
let memPtr := mload(_1)
|
||||
let newFreePtr := add(memPtr, _3)
|
||||
if or(gt(newFreePtr, _4), lt(newFreePtr, memPtr)) { panic_error_0x41() }
|
||||
mstore(_1, newFreePtr)
|
||||
mstore(memPtr, calldataload(src))
|
||||
mstore(dst, memPtr)
|
||||
dst := add(dst, _3)
|
||||
src := add(src, _3)
|
||||
}
|
||||
let ret, ret_1 := fun_sumArray_58(dst_1)
|
||||
let memPos := allocateMemory(_1)
|
||||
let memPos := allocateMemory(_2)
|
||||
return(memPos, sub(abi_encode_uint256_t_string(memPos, ret, ret_1), memPos))
|
||||
}
|
||||
}
|
||||
revert(0, 0)
|
||||
}
|
||||
function abi_decode_t_struct$_S(headStart, end) -> value
|
||||
{
|
||||
if slt(sub(end, headStart), 0x20) { revert(value, value) }
|
||||
let memPtr := mload(64)
|
||||
let newFreePtr := add(memPtr, 0x20)
|
||||
if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { panic_error_0x41() }
|
||||
mstore(64, newFreePtr)
|
||||
value := memPtr
|
||||
mstore(memPtr, calldataload(headStart))
|
||||
}
|
||||
function abi_encode_uint256_t_string(headStart, value0, value1) -> tail
|
||||
{
|
||||
mstore(headStart, value0)
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,5 +1,5 @@
|
||||
Error (1834): Unimplemented feature error in <FILENAME REMOVED>
|
||||
--> yul_unimplemented/input.sol:8:9:
|
||||
--> yul_unimplemented/input.sol:5:16:
|
||||
|
|
||||
8 | x.f();
|
||||
| ^^^^^
|
||||
5 | return type(test).name;
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
@ -1,10 +1,7 @@
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.0;
|
||||
library L { function f(uint) public {} }
|
||||
contract test {
|
||||
using L for uint;
|
||||
function f() public {
|
||||
uint x;
|
||||
x.f();
|
||||
function f() public pure returns (string memory) {
|
||||
return type(test).name;
|
||||
}
|
||||
}
|
@ -14,9 +14,9 @@ contract C {
|
||||
}
|
||||
// ----
|
||||
// creation:
|
||||
// codeDepositCost: 1116400
|
||||
// executionCost: 1160
|
||||
// totalCost: 1117560
|
||||
// codeDepositCost: 1173600
|
||||
// executionCost: 1221
|
||||
// totalCost: 1174821
|
||||
// external:
|
||||
// a(): 1130
|
||||
// b(uint256): infinite
|
||||
|
@ -17,9 +17,9 @@ contract C {
|
||||
// optimize-yul: true
|
||||
// ----
|
||||
// creation:
|
||||
// codeDepositCost: 615400
|
||||
// executionCost: 651
|
||||
// totalCost: 616051
|
||||
// codeDepositCost: 587400
|
||||
// executionCost: 619
|
||||
// totalCost: 588019
|
||||
// external:
|
||||
// a(): 1029
|
||||
// b(uint256): 2084
|
||||
|
@ -30,6 +30,7 @@ contract C {
|
||||
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// compileToEwasm: also
|
||||
// ----
|
||||
// f0() -> 0x20, 0x0
|
||||
// f1() -> 0x20, 0x40, 0x1, 0x2
|
||||
|
@ -7,5 +7,6 @@ contract C {
|
||||
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// compileToEwasm: also
|
||||
// ----
|
||||
// f() -> 0x21, 0x40, 0x7, "abcdefg"
|
||||
|
@ -7,5 +7,6 @@ contract C {
|
||||
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// compileToEwasm: also
|
||||
// ----
|
||||
// f() -> 0x20, 0x40, 0x1, -2
|
||||
|
@ -5,5 +5,6 @@ contract C {
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// compileToEwasm: also
|
||||
// ----
|
||||
// f(uint256,uint256[],uint256): 6, 0x60, 9, 0x8000000000000000000000000000000000000000000000000000000000000002, 1, 2 -> FAILURE
|
||||
// f(uint256,uint256[],uint256): 6, 0x60, 9, 0x8000000000000000000000000000000000000000000000000000000000000002, 1, 2 -> FAILURE
|
||||
|
@ -8,5 +8,6 @@ contract C {
|
||||
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// compileToEwasm: also
|
||||
// ----
|
||||
// f(uint256,uint256): 42, 23 -> 42, 23, 42, 23
|
||||
|
@ -11,5 +11,6 @@ contract C {
|
||||
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// compileToEwasm: also
|
||||
// ----
|
||||
// f() -> 0x40, 0xa0, 0x40, 0x20, 0x0, 0x0
|
||||
|
@ -10,5 +10,6 @@ contract C {
|
||||
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// compileToEwasm: also
|
||||
// ----
|
||||
// f() -> 0x20, 0x40, 0x1, -2
|
||||
|
@ -5,6 +5,7 @@ contract C {
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// compileToEwasm: also
|
||||
// ----
|
||||
// f(bool): true -> true
|
||||
// f(bool): false -> false
|
||||
|
@ -15,6 +15,7 @@ contract C {
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// EVMVersion: >homestead
|
||||
// ----
|
||||
// f(uint256[][1]): 32, 32, 0 -> true
|
||||
|
@ -7,6 +7,7 @@ contract C {
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// compileToEwasm: also
|
||||
// ----
|
||||
// f(uint256[][2][]): 0x20, 0x01, 0x20, 0x40, 0x60, 0x00, 0x00 -> 23 # this is the common encoding for x.length == 1 && x[0][0].length == 0 && x[0][1].length == 0 #
|
||||
// f(uint256[][2][]): 0x20, 0x01, 0x20, 0x00, 0x00 -> 23 # exotic, but still valid encoding #
|
||||
|
@ -8,6 +8,7 @@ contract C {
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// compileToEwasm: also
|
||||
// ----
|
||||
// f(uint16,int16,address,bytes3,bool): 1, 2, 3, "a", true -> 1, 2, 3, "a", true
|
||||
// f(uint16,int16,address,bytes3,bool): 0xffffff, 0x1ffff, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, "abcd", 1 -> FAILURE
|
||||
|
@ -8,6 +8,7 @@ contract C {
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// compileToEwasm: also
|
||||
// ----
|
||||
// f(uint8): 0 -> 0
|
||||
// f(uint8): 1 -> 1
|
||||
|
@ -8,6 +8,7 @@ contract C {
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// compileToEwasm: also
|
||||
// ----
|
||||
// f((int256,uint256,bytes16)): 0xff010, 0xff0002, "abcd" -> 0xff010, 0xff0002, "abcd"
|
||||
// f((int256,uint256,bytes16)): 0xff010, 0xff0002, 0x1111222233334444555566667777888800000000000000000000000000000000 -> 0xff010, 0xff0002, left(0x11112222333344445555666677778888)
|
||||
|
@ -11,5 +11,6 @@ contract C {
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// compileToEwasm: also
|
||||
// ----
|
||||
// f((uint256,uint8,uint8,bytes2)): 1, 2, 3, "ab" -> 1, 2, 3, 0x6162
|
||||
|
@ -3,5 +3,6 @@ contract Lotto {
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// compileToEwasm: also
|
||||
// ----
|
||||
// ticketPrice() -> 555
|
||||
|
@ -4,5 +4,6 @@ contract Lotto {
|
||||
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// compileToEwasm: also
|
||||
// ----
|
||||
// ticketPrice() -> 500
|
||||
|
@ -10,5 +10,6 @@ contract C {
|
||||
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// compileToEwasm: also
|
||||
// ----
|
||||
// test() -> 0
|
||||
|
@ -20,6 +20,7 @@ contract C {
|
||||
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// compileToEwasm: also
|
||||
// ----
|
||||
// f(uint256): 0 -> FAILURE, hex"4e487b71", 0x12
|
||||
// g(uint256): 0 -> FAILURE, hex"4e487b71", 0x12
|
||||
|
@ -9,6 +9,7 @@ contract C {
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// compileToEwasm: also
|
||||
// ----
|
||||
// div(uint256,uint256): 7, 2 -> 3
|
||||
// div(uint256,uint256): 7, 0 -> FAILURE, hex"4e487b71", 0x12 # throws #
|
||||
|
@ -13,5 +13,6 @@ contract C {
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// compileToEwasm: also
|
||||
// ----
|
||||
// f(uint256[2]): 42, 23 -> 42, 23
|
||||
|
@ -13,6 +13,7 @@ contract C {
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// compileToEwasm: also
|
||||
// ----
|
||||
// f(uint256[][]): 0x20, 0x0 -> 42 # valid access stub #
|
||||
// f(uint256[][]): 0x20, 0x1 -> FAILURE # invalid on argument decoding #
|
||||
|
@ -22,5 +22,6 @@ contract C {
|
||||
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// compileToEwasm: also
|
||||
// ----
|
||||
// f((uint256,uint256)[]): 0x20, 0x2, 0x1, 0x2, 0x3, 0x4 -> 2, 1, 2, 3, 4
|
||||
|
@ -8,6 +8,7 @@ contract C {
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// compileToEwasm: also
|
||||
// ----
|
||||
// f(uint256[],uint256,uint256): 0x80, 0, 0, 0, 1, 42 ->
|
||||
// f(uint256[],uint256,uint256): 0x80, 0, 1, 0, 1, 42 ->
|
||||
|
@ -0,0 +1,38 @@
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
contract c {
|
||||
function test1(uint256[][] calldata c) external returns (uint256, uint256) {
|
||||
uint256[][] memory a1 = c;
|
||||
assert(a1[0][0] == c[0][0]);
|
||||
assert(a1[0][1] == c[0][1]);
|
||||
return (a1.length, a1[0][0] + a1[1][1]);
|
||||
}
|
||||
|
||||
function test2(uint256[][2] calldata c) external returns (uint256, uint256) {
|
||||
uint256[][2] memory a2 = c;
|
||||
assert(a2[0][0] == c[0][0]);
|
||||
assert(a2[0][1] == c[0][1]);
|
||||
return (a2[0].length, a2[0][0] + a2[1][1]);
|
||||
}
|
||||
|
||||
function test3(uint256[2][] calldata c) external returns (uint256, uint256) {
|
||||
uint256[2][] memory a3 = c;
|
||||
assert(a3[0][0] == c[0][0]);
|
||||
assert(a3[0][1] == c[0][1]);
|
||||
return (a3.length, a3[0][0] + a3[1][1]);
|
||||
}
|
||||
|
||||
function test4(uint256[2][2] calldata c) external returns (uint256) {
|
||||
uint256[2][2] memory a4 = c;
|
||||
assert(a4[0][0] == c[0][0]);
|
||||
assert(a4[0][1] == c[0][1]);
|
||||
return (a4[0][0] + a4[1][1]);
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: true
|
||||
// ----
|
||||
// test1(uint256[][]): 0x20, 2, 0x40, 0x40, 2, 23, 42 -> 2, 65
|
||||
// test2(uint256[][2]): 0x20, 0x40, 0x40, 2, 23, 42 -> 2, 65
|
||||
// test3(uint256[2][]): 0x20, 2, 23, 42, 23, 42 -> 2, 65
|
||||
// test4(uint256[2][2]): 23, 42, 23, 42 -> 65
|
@ -10,28 +10,28 @@ contract c {
|
||||
a1 = c;
|
||||
assert(a1[0][0] == c[0][0]);
|
||||
assert(a1[0][1] == c[0][1]);
|
||||
return (a1.length, a1[1][0] + a1[1][1]);
|
||||
return (a1.length, a1[0][0] + a1[1][1]);
|
||||
}
|
||||
|
||||
function test2(uint256[][2] calldata c) external returns (uint256, uint256) {
|
||||
a2 = c;
|
||||
assert(a2[0][0] == c[0][0]);
|
||||
assert(a2[0][1] == c[0][1]);
|
||||
return (a2[0].length, a2[1][0] + a2[1][1]);
|
||||
return (a2[0].length, a2[0][0] + a2[1][1]);
|
||||
}
|
||||
|
||||
function test3(uint256[2][] calldata c) external returns (uint256, uint256) {
|
||||
a3 = c;
|
||||
assert(a3[0][0] == c[0][0]);
|
||||
assert(a3[0][1] == c[0][1]);
|
||||
return (a3.length, a3[1][0] + a3[1][1]);
|
||||
return (a3.length, a3[0][0] + a3[1][1]);
|
||||
}
|
||||
|
||||
function test4(uint256[2][2] calldata c) external returns (uint256) {
|
||||
a4 = c;
|
||||
assert(a4[0][0] == c[0][0]);
|
||||
assert(a4[0][1] == c[0][1]);
|
||||
return (a4[1][0] + a4[1][1]);
|
||||
return (a4[0][0] + a4[1][1]);
|
||||
}
|
||||
}
|
||||
// ====
|
||||
@ -39,5 +39,5 @@ contract c {
|
||||
// ----
|
||||
// test1(uint256[][]): 0x20, 2, 0x40, 0x40, 2, 23, 42 -> 2, 65
|
||||
// test2(uint256[][2]): 0x20, 0x40, 0x40, 2, 23, 42 -> 2, 65
|
||||
// test3(uint256[2][]): 0x20, 2, 0x40, 0x40, 23, 42 -> 2, 65
|
||||
// test3(uint256[2][]): 0x20, 2, 23, 42, 23, 42 -> 2, 65
|
||||
// test4(uint256[2][2]): 23, 42, 23, 42 -> 65
|
||||
|
@ -0,0 +1,22 @@
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
contract C {
|
||||
struct S {
|
||||
uint128 a;
|
||||
uint64 b;
|
||||
uint128 c;
|
||||
}
|
||||
function f(S[3] calldata c) public returns (uint128, uint64, uint128) {
|
||||
S[3] memory m = c;
|
||||
return (m[2].a, m[1].b, m[0].c);
|
||||
}
|
||||
function g(S[] calldata c) public returns (uint128, uint64, uint128) {
|
||||
S[] memory m = c;
|
||||
return (m[2].a, m[1].b, m[0].c);
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// f((uint128, uint64, uint128)[3]): 0, 0, 12, 0, 11, 0, 10, 0, 0 -> 10, 11, 12
|
||||
// g((uint128, uint64, uint128)[]): 0x20, 3, 0, 0, 12, 0, 11, 0, 10, 0, 0 -> 10, 11, 12
|
@ -0,0 +1,23 @@
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
contract C {
|
||||
struct S {
|
||||
uint256[] a;
|
||||
}
|
||||
|
||||
function f(S[] calldata c) external returns (uint256, uint256) {
|
||||
S[] memory s = c;
|
||||
assert(s.length == c.length);
|
||||
for (uint i = 0; i < s.length; i++) {
|
||||
assert(s[i].a.length == c[i].a.length);
|
||||
for (uint j = 0; j < s[i].a.length; j++) {
|
||||
assert(s[i].a[j] == c[i].a[j]);
|
||||
}
|
||||
}
|
||||
return (s[1].a.length, s[1].a[0]);
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: true
|
||||
// ----
|
||||
// f((uint256[])[]): 0x20, 3, 0x60, 0x60, 0x60, 0x20, 3, 1, 2, 3 -> 3, 1
|
@ -9,6 +9,13 @@ contract C {
|
||||
|
||||
function f(S[] calldata c) external returns (uint256, uint256) {
|
||||
s = c;
|
||||
assert(s.length == c.length);
|
||||
for (uint i = 0; i < s.length; i++) {
|
||||
assert(s[i].a.length == c[i].a.length);
|
||||
for (uint j = 0; j < s[i].a.length; j++) {
|
||||
assert(s[i].a[j] == c[i].a[j]);
|
||||
}
|
||||
}
|
||||
return (s[1].a.length, s[1].a[0]);
|
||||
}
|
||||
}
|
||||
|
@ -12,5 +12,6 @@ contract C {
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// compileToEwasm: also
|
||||
// ----
|
||||
// f() -> 1, 2, 3
|
||||
|
@ -8,5 +8,6 @@ contract C {
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// compileToEwasm: also
|
||||
// ----
|
||||
// f() -> "a"
|
||||
|
@ -5,5 +5,8 @@ contract C {
|
||||
return data[0];
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// compileToEwasm: also
|
||||
// ----
|
||||
// f() -> "a"
|
||||
|
@ -21,5 +21,7 @@ contract C {
|
||||
}
|
||||
}
|
||||
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// f((uint256,uint256)[]): 0x20, 0x2, 0x1, 0x2, 0x3, 0x4 -> 2, 1, 2, 3, 4
|
||||
|
@ -0,0 +1,10 @@
|
||||
contract C {
|
||||
function f(uint256[2] calldata c) public returns (uint256, uint256) {
|
||||
uint256[2] memory m1 = c;
|
||||
return (m1[0], m1[1]);
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// f(uint256[2]): 43, 57 -> 43, 57
|
@ -7,5 +7,6 @@ contract C {
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// compileToEwasm: also
|
||||
// ----
|
||||
// f(bytes): 0x20, 0x08, "abcdefgh" -> "a"
|
||||
|
@ -11,5 +11,7 @@ contract C {
|
||||
}
|
||||
}
|
||||
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// f(uint256[][]): 0x20, 0x1, 0x20, 0x2, 0x17, 0x2a -> 0x1, 0x40, 0x2, 0x17, 0x2a
|
||||
|
@ -19,6 +19,7 @@ contract C {
|
||||
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// compileToEwasm: also
|
||||
// ----
|
||||
// one() -> 3
|
||||
// two() -> FAILURE, hex"4e487b71", 0x51
|
||||
|
@ -11,5 +11,6 @@ contract C {
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// compileToEwasm: also
|
||||
// ----
|
||||
// f() -> 2, 3, 4
|
||||
|
@ -6,5 +6,6 @@ contract C {
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// compileToEwasm: also
|
||||
// ----
|
||||
// f() -> 7
|
||||
|
@ -21,6 +21,7 @@ contract C {
|
||||
}}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// compileToEwasm: also
|
||||
// ----
|
||||
// f() -> FAILURE, hex"4e487b71", 0x41
|
||||
// g() -> FAILURE, hex"4e487b71", 0x41
|
||||
|
@ -31,5 +31,6 @@ contract C {
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// compileToEwasm: also
|
||||
// ----
|
||||
// f() -> 7
|
||||
|
@ -14,6 +14,7 @@ contract A {
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// compileToEwasm: also
|
||||
// ----
|
||||
// test() -> false
|
||||
// testIt() -> FAILURE, hex"4e487b71", 0x32
|
||||
|
@ -8,5 +8,6 @@ contract c {
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// compileToEwasm: also
|
||||
// ----
|
||||
// test(uint256[8],uint256[],uint256[5],uint256,uint256,uint256): 1, 2, 3, 4, 5, 6, 7, 8, 0x220, 21, 22, 23, 24, 25, 0, 1, 2, 3, 11, 12, 13 -> 1, 12, 23
|
||||
|
@ -9,6 +9,7 @@ contract c {
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// compileToEwasm: also
|
||||
// ----
|
||||
// storage: empty
|
||||
// fill() ->
|
||||
|
@ -35,6 +35,7 @@ contract c {
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// compileToEwasm: also
|
||||
// ----
|
||||
// setIDStatic(uint256): 0xb ->
|
||||
// getID(uint256): 0x2 -> 0xb
|
||||
|
@ -7,5 +7,6 @@ contract C {
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// compileToEwasm: also
|
||||
// ----
|
||||
// f(bytes32): "789" -> 32, 16, 8
|
||||
|
@ -17,6 +17,7 @@ contract c {
|
||||
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// compileToEwasm: also
|
||||
// ----
|
||||
// length() -> 4
|
||||
// set(uint256,uint256): 3, 4 -> true
|
||||
|
@ -32,6 +32,7 @@ contract C {
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// compileToEwasm: also
|
||||
// ----
|
||||
// test(uint256,uint256): 10, 0 -> 11
|
||||
// test(uint256,uint256): 10, 1 -> 12
|
||||
|
@ -6,5 +6,6 @@ contract C {
|
||||
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// compileToEwasm: also
|
||||
// ----
|
||||
// f() -> 3
|
||||
|
@ -12,5 +12,6 @@ contract Test {
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// compileToEwasm: also
|
||||
// ----
|
||||
// f() -> 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x07
|
||||
|
@ -13,5 +13,6 @@ contract C {
|
||||
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// compileToEwasm: also
|
||||
// ----
|
||||
// f() -> 1, 2, 3, 4, 5
|
||||
|
@ -6,5 +6,6 @@ contract C {
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// compileToEwasm: also
|
||||
// ----
|
||||
// f() -> 4
|
||||
|
@ -9,5 +9,6 @@ contract C {
|
||||
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// compileToEwasm: also
|
||||
// ----
|
||||
// f() -> 3, 6
|
||||
|
@ -8,5 +8,8 @@ contract C {
|
||||
}
|
||||
}
|
||||
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// compileToEwasm: also
|
||||
// ----
|
||||
// f() -> 0x40, 0x80, 0x3, "ray", 0x2, "mi"
|
||||
|
@ -7,6 +7,7 @@ contract C {
|
||||
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// compileToEwasm: also
|
||||
// ----
|
||||
// f(uint256): 0 -> 0x20, 0x4, "This"
|
||||
// f(uint256): 1 -> 0x20, 0x2, "is"
|
||||
|
@ -8,5 +8,6 @@ contract c {
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// compileToEwasm: also
|
||||
// ----
|
||||
// test() -> FAILURE, hex"4e487b71", 0x31
|
||||
|
@ -10,5 +10,6 @@ contract c {
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// compileToEwasm: also
|
||||
// ----
|
||||
// test() -> 3
|
||||
|
@ -14,5 +14,6 @@ contract c {
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// compileToEwasm: also
|
||||
// ----
|
||||
// test() -> 2, 1, 1
|
||||
|
@ -11,5 +11,6 @@ contract c {
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// compileToEwasm: also
|
||||
// ----
|
||||
// test() -> FAILURE, hex"4e487b71", 0x31
|
||||
|
@ -10,5 +10,6 @@ contract c {
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// compileToEwasm: also
|
||||
// ----
|
||||
// test() -> 3
|
||||
|
@ -11,6 +11,7 @@ contract c {
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// compileToEwasm: also
|
||||
// ----
|
||||
// test() ->
|
||||
// storage: empty
|
||||
|
@ -15,5 +15,6 @@ contract c {
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// compileToEwasm: also
|
||||
// ----
|
||||
// test() -> false
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user