Merge remote-tracking branch 'origin/develop' into breaking

This commit is contained in:
chriseth 2020-11-23 19:23:36 +01:00
commit a0a02f2307
511 changed files with 1263 additions and 372 deletions
.circleci
Changelog.md
docs
libsolidity
libyul/backends/wasm
scripts
test
cmdlineTests
name_simplifier
standard_generatedSources
standard_optimizer_generatedSources
yul_unimplemented
libsolidity
gasTests
semanticTests
abiEncoderV1
abiEncoderV2
accessor
arithmetics
array

View File

@ -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:

View File

@ -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)

View File

@ -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:

View File

@ -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>`_.

View File

@ -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>`.

View File

@ -1,3 +1,6 @@
.. index: calldata layout
*******************
Layout of Call Data
*******************

View File

@ -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;
}

View File

@ -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>();

View File

@ -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;

View File

@ -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;
}

View File

@ -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

View File

@ -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));

View File

@ -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);

View File

@ -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)
{

View File

@ -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

View File

@ -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()
};
}

View File

@ -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");

View File

@ -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())
{

View File

@ -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);

View File

@ -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();
}

View File

@ -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;
};

View File

@ -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)

View File

@ -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 + "\"";

View File

@ -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; };

View File

@ -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?

View File

@ -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)
}
}

View File

@ -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']

View File

@ -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

View File

@ -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
View 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

View File

@ -1 +1 @@
../../scripts/travis-emscripten/build_emscripten.sh
build_emscripten.sh

View File

@ -12,3 +12,4 @@ errorstring
hist
otion
keypair
ether

View File

@ -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

View File

@ -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;
| ^^^^^^^^^^^^^^^

View File

@ -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;
}
}

View File

@ -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

View File

@ -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

View File

@ -30,6 +30,7 @@ contract C {
// ====
// compileViaYul: also
// compileToEwasm: also
// ----
// f0() -> 0x20, 0x0
// f1() -> 0x20, 0x40, 0x1, 0x2

View File

@ -7,5 +7,6 @@ contract C {
// ====
// compileViaYul: also
// compileToEwasm: also
// ----
// f() -> 0x21, 0x40, 0x7, "abcdefg"

View File

@ -7,5 +7,6 @@ contract C {
// ====
// compileViaYul: also
// compileToEwasm: also
// ----
// f() -> 0x20, 0x40, 0x1, -2

View File

@ -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

View File

@ -8,5 +8,6 @@ contract C {
// ====
// compileViaYul: also
// compileToEwasm: also
// ----
// f(uint256,uint256): 42, 23 -> 42, 23, 42, 23

View File

@ -11,5 +11,6 @@ contract C {
// ====
// compileViaYul: also
// compileToEwasm: also
// ----
// f() -> 0x40, 0xa0, 0x40, 0x20, 0x0, 0x0

View File

@ -10,5 +10,6 @@ contract C {
// ====
// compileViaYul: also
// compileToEwasm: also
// ----
// f() -> 0x20, 0x40, 0x1, -2

View File

@ -5,6 +5,7 @@ contract C {
}
// ====
// compileViaYul: also
// compileToEwasm: also
// ----
// f(bool): true -> true
// f(bool): false -> false

View File

@ -15,6 +15,7 @@ contract C {
}
}
// ====
// compileViaYul: also
// EVMVersion: >homestead
// ----
// f(uint256[][1]): 32, 32, 0 -> true

View File

@ -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 #

View File

@ -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

View File

@ -8,6 +8,7 @@ contract C {
}
// ====
// compileViaYul: also
// compileToEwasm: also
// ----
// f(uint8): 0 -> 0
// f(uint8): 1 -> 1

View File

@ -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)

View File

@ -11,5 +11,6 @@ contract C {
}
// ====
// compileViaYul: also
// compileToEwasm: also
// ----
// f((uint256,uint8,uint8,bytes2)): 1, 2, 3, "ab" -> 1, 2, 3, 0x6162

View File

@ -3,5 +3,6 @@ contract Lotto {
}
// ====
// compileViaYul: also
// compileToEwasm: also
// ----
// ticketPrice() -> 555

View File

@ -4,5 +4,6 @@ contract Lotto {
// ====
// compileViaYul: also
// compileToEwasm: also
// ----
// ticketPrice() -> 500

View File

@ -10,5 +10,6 @@ contract C {
// ====
// compileViaYul: also
// compileToEwasm: also
// ----
// test() -> 0

View File

@ -20,6 +20,7 @@ contract C {
// ====
// compileViaYul: also
// compileToEwasm: also
// ----
// f(uint256): 0 -> FAILURE, hex"4e487b71", 0x12
// g(uint256): 0 -> FAILURE, hex"4e487b71", 0x12

View File

@ -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 #

View File

@ -13,5 +13,6 @@ contract C {
}
// ====
// compileViaYul: also
// compileToEwasm: also
// ----
// f(uint256[2]): 42, 23 -> 42, 23

View File

@ -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 #

View File

@ -22,5 +22,6 @@ contract C {
// ====
// compileViaYul: also
// compileToEwasm: also
// ----
// f((uint256,uint256)[]): 0x20, 0x2, 0x1, 0x2, 0x3, 0x4 -> 2, 1, 2, 3, 4

View File

@ -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 ->

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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]);
}
}

View File

@ -12,5 +12,6 @@ contract C {
}
// ====
// compileViaYul: also
// compileToEwasm: also
// ----
// f() -> 1, 2, 3

View File

@ -8,5 +8,6 @@ contract C {
}
// ====
// compileViaYul: also
// compileToEwasm: also
// ----
// f() -> "a"

View File

@ -5,5 +5,8 @@ contract C {
return data[0];
}
}
// ====
// compileViaYul: also
// compileToEwasm: also
// ----
// f() -> "a"

View File

@ -21,5 +21,7 @@ contract C {
}
}
// ====
// compileViaYul: also
// ----
// f((uint256,uint256)[]): 0x20, 0x2, 0x1, 0x2, 0x3, 0x4 -> 2, 1, 2, 3, 4

View File

@ -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

View File

@ -7,5 +7,6 @@ contract C {
}
// ====
// compileViaYul: also
// compileToEwasm: also
// ----
// f(bytes): 0x20, 0x08, "abcdefgh" -> "a"

View File

@ -11,5 +11,7 @@ contract C {
}
}
// ====
// compileViaYul: also
// ----
// f(uint256[][]): 0x20, 0x1, 0x20, 0x2, 0x17, 0x2a -> 0x1, 0x40, 0x2, 0x17, 0x2a

View File

@ -19,6 +19,7 @@ contract C {
// ====
// compileViaYul: also
// compileToEwasm: also
// ----
// one() -> 3
// two() -> FAILURE, hex"4e487b71", 0x51

View File

@ -11,5 +11,6 @@ contract C {
}
// ====
// compileViaYul: also
// compileToEwasm: also
// ----
// f() -> 2, 3, 4

View File

@ -6,5 +6,6 @@ contract C {
}
// ====
// compileViaYul: also
// compileToEwasm: also
// ----
// f() -> 7

View File

@ -21,6 +21,7 @@ contract C {
}}
// ====
// compileViaYul: also
// compileToEwasm: also
// ----
// f() -> FAILURE, hex"4e487b71", 0x41
// g() -> FAILURE, hex"4e487b71", 0x41

View File

@ -31,5 +31,6 @@ contract C {
}
// ====
// compileViaYul: also
// compileToEwasm: also
// ----
// f() -> 7

View File

@ -14,6 +14,7 @@ contract A {
}
// ====
// compileViaYul: also
// compileToEwasm: also
// ----
// test() -> false
// testIt() -> FAILURE, hex"4e487b71", 0x32

View File

@ -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

View File

@ -9,6 +9,7 @@ contract c {
}
// ====
// compileViaYul: also
// compileToEwasm: also
// ----
// storage: empty
// fill() ->

View File

@ -35,6 +35,7 @@ contract c {
}
// ====
// compileViaYul: also
// compileToEwasm: also
// ----
// setIDStatic(uint256): 0xb ->
// getID(uint256): 0x2 -> 0xb

View File

@ -7,5 +7,6 @@ contract C {
}
// ====
// compileViaYul: also
// compileToEwasm: also
// ----
// f(bytes32): "789" -> 32, 16, 8

View File

@ -17,6 +17,7 @@ contract c {
// ====
// compileViaYul: also
// compileToEwasm: also
// ----
// length() -> 4
// set(uint256,uint256): 3, 4 -> true

View File

@ -32,6 +32,7 @@ contract C {
}
// ====
// compileViaYul: also
// compileToEwasm: also
// ----
// test(uint256,uint256): 10, 0 -> 11
// test(uint256,uint256): 10, 1 -> 12

View File

@ -6,5 +6,6 @@ contract C {
// ====
// compileViaYul: also
// compileToEwasm: also
// ----
// f() -> 3

View File

@ -12,5 +12,6 @@ contract Test {
}
// ====
// compileViaYul: also
// compileToEwasm: also
// ----
// f() -> 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x07

View File

@ -13,5 +13,6 @@ contract C {
// ====
// compileViaYul: also
// compileToEwasm: also
// ----
// f() -> 1, 2, 3, 4, 5

View File

@ -6,5 +6,6 @@ contract C {
}
// ====
// compileViaYul: also
// compileToEwasm: also
// ----
// f() -> 4

View File

@ -9,5 +9,6 @@ contract C {
// ====
// compileViaYul: also
// compileToEwasm: also
// ----
// f() -> 3, 6

View File

@ -8,5 +8,8 @@ contract C {
}
}
// ====
// compileViaYul: also
// compileToEwasm: also
// ----
// f() -> 0x40, 0x80, 0x3, "ray", 0x2, "mi"

View File

@ -7,6 +7,7 @@ contract C {
// ====
// compileViaYul: also
// compileToEwasm: also
// ----
// f(uint256): 0 -> 0x20, 0x4, "This"
// f(uint256): 1 -> 0x20, 0x2, "is"

View File

@ -8,5 +8,6 @@ contract c {
}
// ====
// compileViaYul: also
// compileToEwasm: also
// ----
// test() -> FAILURE, hex"4e487b71", 0x31

View File

@ -10,5 +10,6 @@ contract c {
}
// ====
// compileViaYul: also
// compileToEwasm: also
// ----
// test() -> 3

View File

@ -14,5 +14,6 @@ contract c {
}
// ====
// compileViaYul: also
// compileToEwasm: also
// ----
// test() -> 2, 1, 1

View File

@ -11,5 +11,6 @@ contract c {
}
// ====
// compileViaYul: also
// compileToEwasm: also
// ----
// test() -> FAILURE, hex"4e487b71", 0x31

View File

@ -10,5 +10,6 @@ contract c {
}
// ====
// compileViaYul: also
// compileToEwasm: also
// ----
// test() -> 3

View File

@ -11,6 +11,7 @@ contract c {
}
// ====
// compileViaYul: also
// compileToEwasm: also
// ----
// test() ->
// storage: empty

View File

@ -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