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

This commit is contained in:
chriseth 2020-11-17 18:51:57 +01:00
commit e8a278eefa
141 changed files with 2354 additions and 1203 deletions

View File

@ -33,16 +33,21 @@ Language Features:
* Immutable variables with literal number values are considered pure.
Compiler Features:
* Command Line Interface: New option ``--experimental-via-ir`` allows switching compilation process to go through
the Yul intermediate representation. This is highly experimental and is used for development purposes.
* Standard JSON: New option ``settings.viaIR`` allows the same switch as ``--experimental-via-ir`` on the commandline.
* Command Line Interface: Report error if file could not be read in ``--standard-json`` mode.
* Command Line interface: Report proper error for each output file which could not be written. Previously an exception was thrown, and execution aborted, on the first error.
* SMTChecker: Add division by zero checks in the CHC engine.
* SMTChecker: Support ``selector`` for expressions with value known at compile-time.
* SMTChecker: More precise analysis of external calls using ``this``.
* Command Line Interface: New option ``--model-checker-timeout`` sets a timeout in milliseconds for each individual query performed by the SMTChecker.
* Standard JSON: New option ``modelCheckerSettings.timeout`` sets a timeout in milliseconds for each individual query performed by the SMTChecker.
* Assembler: Perform linking in assembly mode when library addresses are provided.
Bugfixes:
* Command Line Interface: Reject duplicate libraries in ``--libraries`` option instead of arbitrarily choosing one.
* SMTChecker: Fix lack of reporting potential violations when using only the CHC engine.
* SMTChecker: Fix internal error on conversion from string literal to byte.
* SMTChecker: Fix internal error when using tuples of rational literals inside the conditional operator.
@ -51,8 +56,11 @@ Bugfixes:
* SMTChecker: Fix false negative in modifier applied multiple times.
* SMTChecker: Fix internal error in the BMC engine when inherited contract from a different source unit has private state variables.
* SMTChecker: Fix internal error when ``array.push()`` is used as the LHS of an assignment.
* Command Line Interface: Fix write error when the directory passed to ``--output-dir`` ends with a slash.
* SMTChecker: Fix CHC false positives when branches are used inside modifiers.
* Code generator: Fix missing creation dependency tracking for abstract contracts.
* NatSpec: Fix internal error when inheriting return parameter documentation but the parameter names differ between base and inherited.
* Standard JSON: Fix library addresses specified in ``libraries`` being used for linking even if the file names do not match.
### 0.7.4 (2020-10-19)

View File

@ -184,24 +184,15 @@ custom types without the overhead of external function calls:
It is possible to obtain the address of a library by converting
the library type to the ``address`` type, i.e. using ``address(LibraryName)``.
As the compiler cannot know where the library will be
deployed at, these addresses have to be filled into the
final bytecode by a linker
(see :ref:`commandline-compiler` for how to use the
commandline compiler for linking). If the addresses are not
given as arguments to the compiler, the compiled hex code
will contain placeholders of the form ``__Set______`` (where
``Set`` is the name of the library). The address can be filled
manually by replacing all those 40 symbols by the hex
encoding of the address of the library contract.
.. note::
Manually linking libraries on the generated bytecode is discouraged, because
in this way, the library name is restricted to 36 characters.
You should ask the compiler to link the libraries at the time
a contract is compiled by either using
the ``--libraries`` option of ``solc`` or the ``libraries`` key if you use
the standard-JSON interface to the compiler.
As the compiler does not know the address where the library will be deployed, the compiled hex code
will contain placeholders of the form ``__$30bbc0abd4d6364515865950d3e0d10953$__``. The placeholder
is a 34 character prefix of the hex encoding of the keccak256 hash of the fully qualified library
name, which would be for example ``libraries/bigint.sol:BigInt`` if the library was stored in a file
called ``bigint.sol`` in a ``libraries/`` directory. Such bytecode is incomplete and should not be
deployed. Placeholders need to be replaced with actual addresses. You can do that by either passing
them to the compiler when the library is being compiled or by using the linker to update an already
compiled binary. See :ref:`library-linking` for information on how to use the commandline compiler
for linking.
In comparison to contracts, libraries are restricted in the following ways:

View File

@ -12,10 +12,16 @@ Using the Commandline Compiler
.. note::
This section does not apply to :ref:`solcjs <solcjs>`, not even if it is used in commandline mode.
Basic usage
-----------
One of the build targets of the Solidity repository is ``solc``, the solidity commandline compiler.
Using ``solc --help`` provides you with an explanation of all options. The compiler can produce various outputs, ranging from simple binaries and assembly over an abstract syntax tree (parse tree) to estimations of gas usage.
If you only want to compile a single file, you run it as ``solc --bin sourceFile.sol`` and it will print the binary. If you want to get some of the more advanced output variants of ``solc``, it is probably better to tell it to output everything to separate files using ``solc -o outputDirectory --bin --ast-json --asm sourceFile.sol``.
Optimizer options
-----------------
Before you deploy your contract, activate the optimizer when compiling using ``solc --optimize --bin sourceFile.sol``.
By default, the optimizer will optimize the contract assuming it is called 200 times across its lifetime
(more specifically, it assumes each opcode is executed around 200 times).
@ -27,6 +33,9 @@ This parameter has effects on the following (this might change in the future):
- the size of the binary search in the function dispatch routine
- the way constants like large numbers or strings are stored
Path remapping
--------------
The commandline compiler will automatically read imported files from the filesystem, but
it is also possible to provide path redirects using ``prefix=path`` in the following way:
@ -53,6 +62,11 @@ For security reasons the compiler has restrictions what directories it can acces
Everything inside the path specified via ``--base-path`` is always allowed.
.. _library-linking:
Library linking
---------------
If your contracts use :ref:`libraries <libraries>`, you will notice that the bytecode contains substrings of the form ``__$53aea86b7d70b31448b230b20ae141a537$__``. These are placeholders for the actual library addresses.
The placeholder is a 34 character prefix of the hex encoding of the keccak256 hash of the fully qualified library name.
The bytecode file will also contain lines of the form ``// <placeholder> -> <fq library name>`` at the end to help
@ -60,13 +74,23 @@ identify which libraries the placeholders represent. Note that the fully qualifi
is the path of its source file and the library name separated by ``:``.
You can use ``solc`` as a linker meaning that it will insert the library addresses for you at those points:
Either add ``--libraries "file.sol:Math:0x1234567890123456789012345678901234567890 file.sol:Heap:0xabCD567890123456789012345678901234567890"`` to your command to provide an address for each library or store the string in a file (one library per line) and run ``solc`` using ``--libraries fileName``.
If ``solc`` is called with the option ``--link``, all input files are interpreted to be unlinked binaries (hex-encoded) in the ``__$53aea86b7d70b31448b230b20ae141a537$__``-format given above and are linked in-place (if the input is read from stdin, it is written to stdout). All options except ``--libraries`` are ignored (including ``-o``) in this case.
Either add ``--libraries "file.sol:Math:0x1234567890123456789012345678901234567890 file.sol:Heap:0xabCD567890123456789012345678901234567890"`` to your command to provide an address for each library (use commas or spaces as separators) or store the string in a file (one library per line) and run ``solc`` using ``--libraries fileName``.
If ``solc`` is called with the option ``--standard-json``, it will expect a JSON input (as explained below) on the standard input, and return a JSON output on the standard output. This is the recommended interface for more complex and especially automated uses. The process will always terminate in a "success" state and report any errors via the JSON output.
The option ``--base-path`` is also processed in standard-json mode.
If ``solc`` is called with the option ``--link``, all input files are interpreted to be unlinked binaries (hex-encoded) in the ``__$53aea86b7d70b31448b230b20ae141a537$__``-format given above and are linked in-place (if the input is read from stdin, it is written to stdout). All options except ``--libraries`` are ignored (including ``-o``) in this case.
.. warning::
Manually linking libraries on the generated bytecode is discouraged because it does not update
contract metadata. Since metadata contains a list of libraries specified at the time of
compilation and bytecode contains a metadata hash, you will get different binaries, depending
on when linking is performed.
You should ask the compiler to link the libraries at the time a contract is compiled by either
using the ``--libraries`` option of ``solc`` or the ``libraries`` key if you use the
standard-JSON interface to the compiler.
.. note::
The library placeholder used to be the fully qualified name of the library itself
instead of the hash of it. This format is still supported by ``solc --link`` but
@ -252,6 +276,9 @@ Input Description
// Affects type checking and code generation. Can be homestead,
// tangerineWhistle, spuriousDragon, byzantium, constantinople, petersburg, istanbul or berlin
"evmVersion": "byzantium",
// Optional: Change compilation pipeline to go through the Yul intermediate representation.
// This is a highly EXPERIMENTAL feature, not to be used for production. This is false by default.
"viaIR": true,
// Optional: Debugging settings
"debug": {
// How to treat revert (and require) reason strings. Settings are

View File

@ -27,6 +27,7 @@
#include <cstdio>
#include <map>
#include <memory>
#include <optional>
#include <string>
#include <vector>

View File

@ -36,21 +36,52 @@ using namespace solidity::frontend;
namespace
{
void copyMissingTags(StructurallyDocumentedAnnotation& _target, set<CallableDeclaration const*> const& _baseFunctions)
void copyMissingTags(set<CallableDeclaration const*> const& _baseFunctions, StructurallyDocumentedAnnotation& _target, CallableDeclaration const* _declaration = nullptr)
{
// Only copy if there is exactly one direct base function.
if (_baseFunctions.size() != 1)
return;
auto& sourceDoc = dynamic_cast<StructurallyDocumentedAnnotation const&>((*_baseFunctions.begin())->annotation());
set<string> existingTags;
for (auto it = sourceDoc.docTags.begin(); it != sourceDoc.docTags.end();)
{
string const& tag = it->first;
// Don't copy tag "inheritdoc" or already existing tags
if (tag == "inheritdoc" || _target.docTags.count(tag))
{
it++;
continue;
}
for (auto const& iterator: _target.docTags)
existingTags.insert(iterator.first);
size_t n = 0;
// Iterate over all values of the current tag (it's a multimap)
for (auto next = sourceDoc.docTags.upper_bound(tag); it != next; it++, n++)
{
DocTag content = it->second;
// Update the parameter name for @return tags
if (_declaration && tag == "return")
{
size_t docParaNameEndPos = content.content.find_first_of(" \t");
string const docParameterName = content.content.substr(0, docParaNameEndPos);
if (docParameterName != _declaration->returnParameters().at(n)->name())
{
bool baseHasNoName = (*_baseFunctions.begin())->returnParameters().at(n)->name().empty();
string paramName = _declaration->returnParameters().at(n)->name();
content.content =
(paramName.empty() ? "" : std::move(paramName) + " ") + (
string::npos == docParaNameEndPos || baseHasNoName ?
content.content :
content.content.substr(docParaNameEndPos + 1)
);
}
}
for (auto const& [tag, content]: sourceDoc.docTags)
if (tag != "inheritdoc" && !existingTags.count(tag))
_target.docTags.emplace(tag, content);
}
}
}
CallableDeclaration const* findBaseCallable(set<CallableDeclaration const*> const& _baseFunctions, int64_t _contractId)
@ -91,9 +122,9 @@ bool DocStringAnalyser::visit(VariableDeclaration const& _variable)
return false;
if (CallableDeclaration const* baseFunction = resolveInheritDoc(_variable.annotation().baseFunctions, _variable, _variable.annotation()))
copyMissingTags(_variable.annotation(), {baseFunction});
copyMissingTags({baseFunction}, _variable.annotation());
else if (_variable.annotation().docTags.empty())
copyMissingTags(_variable.annotation(), _variable.annotation().baseFunctions);
copyMissingTags(_variable.annotation().baseFunctions, _variable.annotation());
return false;
}
@ -119,13 +150,13 @@ void DocStringAnalyser::handleCallable(
)
{
if (CallableDeclaration const* baseFunction = resolveInheritDoc(_callable.annotation().baseFunctions, _node, _annotation))
copyMissingTags(_annotation, {baseFunction});
copyMissingTags({baseFunction}, _annotation, &_callable);
else if (
_annotation.docTags.empty() &&
_callable.annotation().baseFunctions.size() == 1 &&
parameterNamesEqual(_callable, **_callable.annotation().baseFunctions.begin())
)
copyMissingTags(_annotation, _callable.annotation().baseFunctions);
copyMissingTags(_callable.annotation().baseFunctions, _annotation, &_callable);
}
CallableDeclaration const* DocStringAnalyser::resolveInheritDoc(

View File

@ -449,6 +449,36 @@ string YulUtilFunctions::maskBytesFunctionDynamic()
});
}
string YulUtilFunctions::maskLowerOrderBytesFunction(size_t _bytes)
{
string functionName = "mask_lower_order_bytes_" + to_string(_bytes);
solAssert(_bytes <= 32, "");
return m_functionCollector.createFunction(functionName, [&]() {
return Whiskers(R"(
function <functionName>(data) -> result {
result := and(data, <mask>)
})")
("functionName", functionName)
("mask", formatNumber((~u256(0)) >> (256 - 8 * _bytes)))
.render();
});
}
string YulUtilFunctions::maskLowerOrderBytesFunctionDynamic()
{
string functionName = "mask_lower_order_bytes_dynamic";
return m_functionCollector.createFunction(functionName, [&]() {
return Whiskers(R"(
function <functionName>(data, bytes) -> result {
let mask := not(<shl>(mul(8, bytes), not(0)))
result := and(data, mask)
})")
("functionName", functionName)
("shl", shiftLeftFunctionDynamic())
.render();
});
}
string YulUtilFunctions::roundUpFunction()
{
string functionName = "round_up_to_mul_of_32";
@ -1342,7 +1372,6 @@ string YulUtilFunctions::storageArrayPushFunction(ArrayType const& _type)
{
solAssert(_type.location() == DataLocation::Storage, "");
solAssert(_type.isDynamicallySized(), "");
solUnimplementedAssert(_type.baseType()->storageBytes() <= 32, "Base type is not yet implemented.");
string functionName = "array_push_" + _type.identifier();
return m_functionCollector.createFunction(functionName, [&]() {
@ -1568,23 +1597,20 @@ string YulUtilFunctions::copyArrayToStorageFunction(ArrayType const& _fromType,
);
if (_fromType.isByteArray())
return copyByteArrayToStorageFunction(_fromType, _toType);
solUnimplementedAssert(!_fromType.dataStoredIn(DataLocation::Storage), "");
if (_fromType.dataStoredIn(DataLocation::Storage) && _toType.baseType()->isValueType())
return copyValueArrayStorageToStorageFunction(_fromType, _toType);
string functionName = "copy_array_to_storage_from_" + _fromType.identifier() + "_to_" + _toType.identifier();
return m_functionCollector.createFunction(functionName, [&](){
Whiskers templ(R"(
function <functionName>(slot, value<?isFromDynamicCalldata>, len</isFromDynamicCalldata>) {
<?fromStorage> if eq(slot, value) { leave } </fromStorage>
let length := <arrayLength>(value<?isFromDynamicCalldata>, len</isFromDynamicCalldata>)
<?isToDynamic>
<resizeArray>(slot, length)
</isToDynamic>
let srcPtr :=
<?isFromMemoryDynamic>
add(value, 0x20)
<!isFromMemoryDynamic>
value
</isFromMemoryDynamic>
let srcPtr := <srcDataLocation>(value)
let elementSlot := <dstDataLocation>(slot)
let elementOffset := 0
@ -1607,9 +1633,13 @@ string YulUtilFunctions::copyArrayToStorageFunction(ArrayType const& _fromType,
let <elementValues> := <readFromCalldataOrMemory>(srcPtr)
</fromMemory>
<updateStorageValue>(elementSlot<?isValueType>, elementOffset</isValueType>, <elementValues>)
<?fromStorage>
let <elementValues> := srcPtr
</fromStorage>
srcPtr := add(srcPtr, <stride>)
<updateStorageValue>(elementSlot, elementOffset, <elementValues>)
srcPtr := add(srcPtr, <srcStride>)
<?multipleItemsPerSlot>
elementOffset := add(elementOffset, <storageStride>)
@ -1619,18 +1649,21 @@ string YulUtilFunctions::copyArrayToStorageFunction(ArrayType const& _fromType,
}
<!multipleItemsPerSlot>
elementSlot := add(elementSlot, <storageSize>)
elementOffset := 0
</multipleItemsPerSlot>
}
}
)");
if (_fromType.dataStoredIn(DataLocation::Storage))
solAssert(!_fromType.isValueType(), "");
templ("functionName", functionName);
bool fromCalldata = _fromType.dataStoredIn(DataLocation::CallData);
templ("isFromDynamicCalldata", _fromType.isDynamicallySized() && fromCalldata);
templ("fromMemory", _fromType.dataStoredIn(DataLocation::Memory));
templ("fromStorage", _fromType.dataStoredIn(DataLocation::Storage));
bool fromMemory = _fromType.dataStoredIn(DataLocation::Memory);
templ("fromMemory", fromMemory);
templ("fromCalldata", fromCalldata);
templ("isToDynamic", _toType.isDynamicallySized());
templ("isFromMemoryDynamic", _fromType.isDynamicallySized() && _fromType.dataStoredIn(DataLocation::Memory));
templ("srcDataLocation", arrayDataAreaFunction(_fromType));
if (fromCalldata)
{
templ("dynamicallySizedBase", _fromType.baseType()->isDynamicallySized());
@ -1643,7 +1676,7 @@ string YulUtilFunctions::copyArrayToStorageFunction(ArrayType const& _fromType,
templ("arrayLength",arrayLengthFunction(_fromType));
templ("isValueType", _fromType.baseType()->isValueType());
templ("dstDataLocation", arrayDataAreaFunction(_toType));
if (!fromCalldata || _fromType.baseType()->isValueType())
if (fromMemory || (fromCalldata && _fromType.baseType()->isValueType()))
templ("readFromCalldataOrMemory", readFromMemoryOrCalldata(*_fromType.baseType(), fromCalldata));
templ("elementValues", suffixedVariableNameList(
"elementValue_",
@ -1651,7 +1684,13 @@ string YulUtilFunctions::copyArrayToStorageFunction(ArrayType const& _fromType,
_fromType.baseType()->stackItems().size()
));
templ("updateStorageValue", updateStorageValueFunction(*_fromType.baseType(), *_toType.baseType()));
templ("stride", to_string(fromCalldata ? _fromType.calldataStride() : _fromType.memoryStride()));
templ("srcStride",
fromCalldata ?
to_string(_fromType.calldataStride()) :
fromMemory ?
to_string(_fromType.memoryStride()) :
formatNumber(_fromType.baseType()->storageSize())
);
templ("multipleItemsPerSlot", _toType.storageStride() <= 16);
templ("storageStride", to_string(_toType.storageStride()));
templ("storageSize", _toType.baseType()->storageSize().str());
@ -1669,12 +1708,13 @@ string YulUtilFunctions::copyByteArrayToStorageFunction(ArrayType const& _fromTy
);
solAssert(_fromType.isByteArray(), "");
solAssert(_toType.isByteArray(), "");
solUnimplementedAssert(!_fromType.dataStoredIn(DataLocation::Storage), "");
string functionName = "copy_byte_array_to_storage_from_" + _fromType.identifier() + "_to_" + _toType.identifier();
return m_functionCollector.createFunction(functionName, [&](){
Whiskers templ(R"(
function <functionName>(slot, src<?fromCalldata>, len</fromCalldata>) {
<?fromStorage> if eq(slot, src) { leave } </fromStorage>
let newLen := <arrayLength>(src<?fromCalldata>, len</fromCalldata>)
// Make sure array length is sane
if gt(newLen, 0xffffffffffffffff) { <panic>() }
@ -1703,11 +1743,11 @@ string YulUtilFunctions::copyByteArrayToStorageFunction(ArrayType const& _fromTy
let dstPtr := dstDataArea
let i := 0
for { } lt(i, loopEnd) { i := add(i, 32) } {
sstore(dstPtr, <readFromCalldataOrMemory>(add(src, i)))
sstore(dstPtr, <read>(add(src, i)))
dstPtr := add(dstPtr, 1)
}
if lt(loopEnd, newLen) {
let lastValue := <readFromCalldataOrMemory>(add(src, i))
let lastValue := <read>(add(src, i))
sstore(dstPtr, <maskBytes>(lastValue, and(newLen, 0x1f)))
}
sstore(slot, add(mul(newLen, 2), 1))
@ -1715,13 +1755,15 @@ string YulUtilFunctions::copyByteArrayToStorageFunction(ArrayType const& _fromTy
default {
let value := 0
if newLen {
value := <readFromCalldataOrMemory>(src)
value := <read>(src)
}
sstore(slot, <byteArrayCombineShort>(value, newLen))
}
}
)");
templ("functionName", functionName);
bool fromStorage = _fromType.dataStoredIn(DataLocation::Storage);
templ("fromStorage", fromStorage);
bool fromCalldata = _fromType.dataStoredIn(DataLocation::CallData);
templ("fromMemory", _fromType.dataStoredIn(DataLocation::Memory));
templ("fromCalldata", fromCalldata);
@ -1730,7 +1772,7 @@ string YulUtilFunctions::copyByteArrayToStorageFunction(ArrayType const& _fromTy
templ("byteArrayLength", extractByteArrayLengthFunction());
templ("dstDataLocation", arrayDataAreaFunction(_toType));
templ("clearStorageRange", clearStorageRangeFunction(*_toType.baseType()));
templ("readFromCalldataOrMemory", readFromMemoryOrCalldata(*TypeProvider::uint256(), fromCalldata));
templ("read", fromStorage ? "sload" : fromCalldata ? "calldataload" : "mload");
templ("maskBytes", maskBytesFunctionDynamic());
templ("byteArrayCombineShort", shortByteArrayEncodeUsedAreaSetLengthFunction());
@ -1738,6 +1780,65 @@ string YulUtilFunctions::copyByteArrayToStorageFunction(ArrayType const& _fromTy
});
}
string YulUtilFunctions::copyValueArrayStorageToStorageFunction(ArrayType const& _fromType, ArrayType const& _toType)
{
solAssert(
*_fromType.copyForLocation(_toType.location(), _toType.isPointer()) == dynamic_cast<ReferenceType const&>(_toType),
""
);
solAssert(!_fromType.isByteArray(), "");
solAssert(_fromType.dataStoredIn(DataLocation::Storage) && _toType.baseType()->isValueType(), "");
solAssert(_toType.dataStoredIn(DataLocation::Storage), "");
string functionName = "copy_array_to_storage_from_" + _fromType.identifier() + "_to_" + _toType.identifier();
return m_functionCollector.createFunction(functionName, [&](){
Whiskers templ(R"(
function <functionName>(dst, src) {
if eq(dst, src) { leave }
let length := <arrayLength>(src)
// Make sure array length is sane
if gt(length, 0xffffffffffffffff) { <panic>() }
<?isToDynamic>
<resizeArray>(dst, length)
</isToDynamic>
let srcPtr := <srcDataLocation>(src)
let dstPtr := <dstDataLocation>(dst)
let fullSlots := div(length, <itemsPerSlot>)
let i := 0
for { } lt(i, fullSlots) { i := add(i, 1) } {
sstore(add(dstPtr, i), <maskFull>(sload(add(srcPtr, i))))
}
let spill := sub(length, mul(i, <itemsPerSlot>))
if gt(spill, 0) {
sstore(add(dstPtr, i), <maskBytes>(sload(add(srcPtr, i)), mul(spill, <bytesPerItem>)))
}
}
)");
if (_fromType.dataStoredIn(DataLocation::Storage))
solAssert(!_fromType.isValueType(), "");
templ("functionName", functionName);
templ("isToDynamic", _toType.isDynamicallySized());
if (_toType.isDynamicallySized())
templ("resizeArray", resizeDynamicArrayFunction(_toType));
templ("arrayLength",arrayLengthFunction(_fromType));
templ("panic", panicFunction());
templ("srcDataLocation", arrayDataAreaFunction(_fromType));
templ("dstDataLocation", arrayDataAreaFunction(_toType));
unsigned itemsPerSlot = 32 / _toType.storageStride();
templ("itemsPerSlot", to_string(itemsPerSlot));
templ("bytesPerItem", to_string(_toType.storageStride()));
templ("maskFull", maskLowerOrderBytesFunction(itemsPerSlot * _toType.storageStride()));
templ("maskBytes", maskLowerOrderBytesFunctionDynamic());
return templ.render();
});
}
string YulUtilFunctions::arrayConvertLengthToSize(ArrayType const& _type)
{
string functionName = "array_convert_length_to_size_" + _type.identifier();
@ -2298,132 +2399,149 @@ string YulUtilFunctions::updateStorageValueFunction(
("prepare", prepareStoreFunction(_toType))
.render();
}
auto const* toReferenceType = dynamic_cast<ReferenceType const*>(&_toType);
auto const* fromReferenceType = dynamic_cast<ReferenceType const*>(&_fromType);
solAssert(fromReferenceType && toReferenceType, "");
solAssert(*toReferenceType->copyForLocation(
fromReferenceType->location(),
fromReferenceType->isPointer()
).get() == *fromReferenceType, "");
solAssert(toReferenceType->category() == fromReferenceType->category(), "");
if (_toType.category() == Type::Category::Array)
{
solAssert(_offset.value_or(0) == 0, "");
Whiskers templ(R"(
function <functionName>(slot, <?dynamicOffset>offset, </dynamicOffset><value>) {
<?dynamicOffset>if offset { <panic>() }</dynamicOffset>
<copyArrayToStorage>(slot, <value>)
}
)");
templ("functionName", functionName);
templ("dynamicOffset", !_offset.has_value());
templ("panic", panicFunction());
templ("value", suffixedVariableNameList("value_", 0, _fromType.sizeOnStack()));
templ("copyArrayToStorage", copyArrayToStorageFunction(
dynamic_cast<ArrayType const&>(_fromType),
dynamic_cast<ArrayType const&>(_toType)
));
return templ.render();
}
else
{
auto const* toReferenceType = dynamic_cast<ReferenceType const*>(&_toType);
auto const* fromReferenceType = dynamic_cast<ReferenceType const*>(&_fromType);
solAssert(fromReferenceType && toReferenceType, "");
solAssert(*toReferenceType->copyForLocation(
fromReferenceType->location(),
fromReferenceType->isPointer()
).get() == *fromReferenceType, "");
solUnimplementedAssert(
fromReferenceType->location() != DataLocation::Storage,
"Copying from storage to storage is not yet implemented."
);
solAssert(toReferenceType->category() == fromReferenceType->category(), "");
solAssert(_toType.category() == Type::Category::Struct, "");
if (_toType.category() == Type::Category::Array)
{
solAssert(_offset.value_or(0) == 0, "");
auto const& fromStructType = dynamic_cast<StructType const&>(_fromType);
auto const& toStructType = dynamic_cast<StructType const&>(_toType);
solAssert(fromStructType.structDefinition() == toStructType.structDefinition(), "");
solAssert(_offset.value_or(0) == 0, "");
Whiskers templ(R"(
function <functionName>(slot, <value>) {
<copyArrayToStorage>(slot, <value>)
Whiskers templ(R"(
function <functionName>(slot, <?dynamicOffset>offset, </dynamicOffset>value) {
<?dynamicOffset>if offset { <panic>() }</dynamicOffset>
<?fromStorage> if eq(slot, value) { leave } </fromStorage>
<#member>
{
<updateMemberCall>
}
)");
templ("functionName", functionName);
templ("value", suffixedVariableNameList("value_", 0, _fromType.sizeOnStack()));
templ("copyArrayToStorage", copyArrayToStorageFunction(
dynamic_cast<ArrayType const&>(_fromType),
dynamic_cast<ArrayType const&>(_toType)
));
</member>
}
)");
templ("functionName", functionName);
templ("dynamicOffset", !_offset.has_value());
templ("panic", panicFunction());
templ("fromStorage", fromStructType.dataStoredIn(DataLocation::Storage));
return templ.render();
}
else if (_toType.category() == Type::Category::Struct)
MemberList::MemberMap structMembers = fromStructType.nativeMembers(nullptr);
MemberList::MemberMap toStructMembers = toStructType.nativeMembers(nullptr);
vector<map<string, string>> memberParams(structMembers.size());
for (size_t i = 0; i < structMembers.size(); ++i)
{
auto const& fromStructType = dynamic_cast<StructType const&>(_fromType);
auto const& toStructType = dynamic_cast<StructType const&>(_toType);
solAssert(fromStructType.structDefinition() == toStructType.structDefinition(), "");
solAssert(_offset.value_or(0) == 0, "");
Type const& memberType = *structMembers[i].type;
solAssert(memberType.memoryHeadSize() == 32, "");
auto const& [slotDiff, offset] = toStructType.storageOffsetsOfMember(structMembers[i].name);
Whiskers templ(R"(
function <functionName>(slot, value) {
<#member>
{
<updateMemberCall>
}
</member>
}
)");
templ("functionName", functionName);
Whiskers t(R"(
let memberSlot := add(slot, <memberStorageSlotDiff>)
let memberSrcPtr := add(value, <memberOffset>)
MemberList::MemberMap structMembers = fromStructType.nativeMembers(nullptr);
MemberList::MemberMap toStructMembers = toStructType.nativeMembers(nullptr);
vector<map<string, string>> memberParams(structMembers.size());
for (size_t i = 0; i < structMembers.size(); ++i)
{
solAssert(structMembers[i].type->memoryHeadSize() == 32, "");
bool fromCalldata = fromStructType.location() == DataLocation::CallData;
auto const& [slotDiff, offset] = toStructType.storageOffsetsOfMember(structMembers[i].name);
Whiskers t(R"(
let memberSlot := add(slot, <memberStorageSlotDiff>)
<?fromCalldata>
<?fromCalldata>
let <memberValues> :=
<?dynamicallyEncodedMember>
let <memberCalldataOffset> := <accessCalldataTail>(value, add(value, <memberOffset>))
<accessCalldataTail>(value, memberSrcPtr)
<!dynamicallyEncodedMember>
let <memberCalldataOffset> := add(value, <memberOffset>)
memberSrcPtr
</dynamicallyEncodedMember>
<?isValueType>
let <memberValues> := <loadFromMemoryOrCalldata>(<memberCalldataOffset>)
<updateMember>(memberSlot, <memberStorageOffset>, <memberValues>)
<!isValueType>
<updateMember>(memberSlot, <memberCalldataOffset>)
</isValueType>
<!fromCalldata>
let memberMemoryOffset := add(value, <memberOffset>)
let <memberValues> := <loadFromMemoryOrCalldata>(memberMemoryOffset)
<updateMember>(memberSlot, <?hasOffset><memberStorageOffset>,</hasOffset> <memberValues>)
</fromCalldata>
)");
t("fromCalldata", fromCalldata);
if (fromCalldata)
{
t("memberCalldataOffset", suffixedVariableNameList(
"memberCalldataOffset_",
0,
structMembers[i].type->stackItems().size()
));
t("dynamicallyEncodedMember", structMembers[i].type->isDynamicallyEncoded());
if (structMembers[i].type->isDynamicallyEncoded())
t("accessCalldataTail", accessCalldataTailFunction(*structMembers[i].type));
}
t("isValueType", structMembers[i].type->isValueType());
t("memberValues", suffixedVariableNameList(
"memberValue_",
0,
structMembers[i].type->stackItems().size()
));
t("hasOffset", structMembers[i].type->isValueType());
t(
"updateMember",
structMembers[i].type->isValueType() ?
updateStorageValueFunction(*structMembers[i].type, *toStructMembers[i].type) :
updateStorageValueFunction(*structMembers[i].type, *toStructMembers[i].type, offset)
);
t("memberStorageSlotDiff", slotDiff.str());
t("memberStorageOffset", to_string(offset));
t(
"memberOffset",
fromCalldata ?
to_string(fromStructType.calldataOffsetOfMember(structMembers[i].name)) :
fromStructType.memoryOffsetOfMember(structMembers[i].name).str()
);
if (!fromCalldata || structMembers[i].type->isValueType())
t("loadFromMemoryOrCalldata", readFromMemoryOrCalldata(*structMembers[i].type, fromCalldata));
memberParams[i]["updateMemberCall"] = t.render();
}
templ("member", memberParams);
<?isValueType>
<memberValues> := <read>(<memberValues>)
</isValueType>
</fromCalldata>
return templ.render();
<?fromMemory>
let <memberValues> := <read>(memberSrcPtr)
</fromMemory>
<?fromStorage>
let <memberValues> :=
<?isValueType>
<read>(memberSrcPtr)
<!isValueType>
memberSrcPtr
</isValueType>
</fromStorage>
<updateStorageValue>(memberSlot, <memberValues>)
)");
bool fromCalldata = fromStructType.location() == DataLocation::CallData;
t("fromCalldata", fromCalldata);
bool fromMemory = fromStructType.location() == DataLocation::Memory;
t("fromMemory", fromMemory);
bool fromStorage = fromStructType.location() == DataLocation::Storage;
t("fromStorage", fromStorage);
t("isValueType", memberType.isValueType());
t("memberValues", suffixedVariableNameList("memberValue_", 0, memberType.stackItems().size()));
t("memberStorageSlotDiff", slotDiff.str());
if (fromCalldata)
{
t("memberOffset", to_string(fromStructType.calldataOffsetOfMember(structMembers[i].name)));
t("dynamicallyEncodedMember", memberType.isDynamicallyEncoded());
if (memberType.isDynamicallyEncoded())
t("accessCalldataTail", accessCalldataTailFunction(memberType));
if (memberType.isValueType())
t("read", readFromCalldata(memberType));
}
else if (fromMemory)
{
t("memberOffset", fromStructType.memoryOffsetOfMember(structMembers[i].name).str());
t("read", readFromMemory(memberType));
}
else if (fromStorage)
{
auto [srcSlotOffset, srcOffset] = fromStructType.storageOffsetsOfMember(structMembers[i].name);
t("memberOffset", formatNumber(srcSlotOffset));
if (memberType.isValueType())
t("read", readFromStorageValueType(memberType, srcOffset, false));
else
solAssert(srcOffset == 0, "");
}
t("memberStorageSlotOffset", to_string(offset));
t("updateStorageValue", updateStorageValueFunction(
memberType,
*toStructMembers[i].type,
optional<unsigned>{offset}
));
memberParams[i]["updateMemberCall"] = t.render();
}
else
solAssert(false, "Invalid non-value type for assignment.");
templ("member", memberParams);
return templ.render();
}
});
}

View File

@ -106,6 +106,15 @@ public:
/// signature: (value, bytes) -> result
std::string maskBytesFunctionDynamic();
/// Zeroes out all bytes above the first ``_bytes`` lower order bytes.
/// signature: (value) -> result
std::string maskLowerOrderBytesFunction(size_t _bytes);
/// Zeroes out all bytes above the first ``bytes`` lower order bytes.
/// @note ``bytes`` has to be small enough not to overflow ``8 * bytes``.
/// signature: (value, bytes) -> result
std::string maskLowerOrderBytesFunctionDynamic();
/// @returns the name of a function that rounds its input to the next multiple
/// of 32 or the input if it is a multiple of 32.
/// signature: (value) -> result
@ -209,14 +218,18 @@ public:
/// signature: (slot) ->
std::string clearStorageArrayFunction(ArrayType const& _type);
/// @returns the name of a function that will copy array from calldata or memory to storage
/// @returns the name of a function that will copy an array to storage
/// signature (to_slot, from_ptr) ->
std::string copyArrayToStorageFunction(ArrayType const& _fromType, ArrayType const& _toType);
/// @returns the name of a function that will copy a byte array from calldata or memory to storage
/// @returns the name of a function that will copy a byte array to storage
/// signature (to_slot, from_ptr) ->
std::string copyByteArrayToStorageFunction(ArrayType const& _fromType, ArrayType const& _toType);
/// @returns the name of a function that will copy an array of value types from storage to storage.
/// signature (to_slot, from_slot) ->
std::string copyValueArrayStorageToStorageFunction(ArrayType const& _fromType, ArrayType const& _toType);
/// Returns the name of a function that will convert a given length to the
/// size in memory (number of storage slots or calldata/memory bytes) it
/// will require.
@ -297,6 +310,7 @@ public:
/// Returns the name of a function will write the given value to
/// the specified slot and offset. If offset is not given, it is expected as
/// runtime parameter.
/// For reference types, offset is checked to be zero at runtime.
/// signature: (slot, [offset,] value)
std::string updateStorageValueFunction(
Type const& _fromType,

View File

@ -2658,22 +2658,22 @@ void IRGeneratorForStatements::writeToLValue(IRLValue const& _lvalue, IRVariable
std::visit(
util::GenericVisitor{
[&](IRLValue::Storage const& _storage) {
std::optional<unsigned> offset;
string offsetArgument;
optional<unsigned> offsetStatic;
if (std::holds_alternative<unsigned>(_storage.offset))
offset = std::get<unsigned>(_storage.offset);
std::visit(GenericVisitor{
[&](unsigned _offset) { offsetStatic = _offset; },
[&](string const& _offset) { offsetArgument = ", " + _offset; }
}, _storage.offset);
m_code <<
m_utils.updateStorageValueFunction(_value.type(), _lvalue.type, offset) <<
m_utils.updateStorageValueFunction(_value.type(), _lvalue.type, offsetStatic) <<
"(" <<
_storage.slot <<
(
std::holds_alternative<string>(_storage.offset) ?
(", " + std::get<string>(_storage.offset)) :
""
) <<
offsetArgument <<
_value.commaSeparatedListPrefixed() <<
")\n";
},
[&](IRLValue::Memory const& _memory) {
if (_lvalue.type.isValueType())

View File

@ -96,20 +96,7 @@ bool BMC::shouldInlineFunctionCall(FunctionCall const& _funCall)
FunctionType const& funType = dynamic_cast<FunctionType const&>(*_funCall.expression().annotation().type);
if (funType.kind() == FunctionType::Kind::External)
{
auto memberAccess = dynamic_cast<MemberAccess const*>(&_funCall.expression());
if (!memberAccess)
return false;
auto identifier = dynamic_cast<Identifier const*>(&memberAccess->expression());
if (!(
identifier &&
identifier->name() == "this" &&
identifier->annotation().referencedDeclaration &&
dynamic_cast<MagicVariableDeclaration const*>(identifier->annotation().referencedDeclaration)
))
return false;
}
return isTrustedExternalCall(&_funCall.expression());
else if (funType.kind() != FunctionType::Kind::Internal)
return false;
@ -133,7 +120,10 @@ void BMC::endVisit(ContractDefinition const& _contract)
constructor->accept(*this);
else
{
/// Visiting implicit constructor - we need a dummy callstack frame
pushCallStack({nullptr, nullptr});
inlineConstructorHierarchy(_contract);
popCallStack();
/// Check targets created by state variable initialization.
smtutil::Expression constraints = m_context.assertions();
checkVerificationTargets(constraints);
@ -844,32 +834,28 @@ void BMC::checkCondition(
{
case smtutil::CheckResult::SATISFIABLE:
{
solAssert(!_callStack.empty(), "");
std::ostringstream message;
message << "BMC: " << _description << " happens here.";
if (_callStack.size())
{
std::ostringstream modelMessage;
modelMessage << "Counterexample:\n";
solAssert(values.size() == expressionNames.size(), "");
map<string, string> sortedModel;
for (size_t i = 0; i < values.size(); ++i)
if (expressionsToEvaluate.at(i).name != values.at(i))
sortedModel[expressionNames.at(i)] = values.at(i);
std::ostringstream modelMessage;
modelMessage << "Counterexample:\n";
solAssert(values.size() == expressionNames.size(), "");
map<string, string> sortedModel;
for (size_t i = 0; i < values.size(); ++i)
if (expressionsToEvaluate.at(i).name != values.at(i))
sortedModel[expressionNames.at(i)] = values.at(i);
for (auto const& eval: sortedModel)
modelMessage << " " << eval.first << " = " << eval.second << "\n";
for (auto const& eval: sortedModel)
modelMessage << " " << eval.first << " = " << eval.second << "\n";
m_errorReporter.warning(
_errorHappens,
_location,
message.str(),
SecondarySourceLocation().append(modelMessage.str(), SourceLocation{})
.append(SMTEncoder::callStackMessage(_callStack))
.append(move(secondaryLocation))
);
}
else
m_errorReporter.warning(6084_error, _location, message.str(), secondaryLocation);
m_errorReporter.warning(
_errorHappens,
_location,
message.str(),
SecondarySourceLocation().append(modelMessage.str(), SourceLocation{})
.append(SMTEncoder::callStackMessage(_callStack))
.append(move(secondaryLocation))
);
break;
}
case smtutil::CheckResult::UNSATISFIABLE:

View File

@ -562,8 +562,6 @@ void CHC::internalFunctionCall(FunctionCall const& _funCall)
m_context.addAssertion(interface(*contract));
}
auto previousError = errorFlag().currentValue();
m_context.addAssertion(predicate(_funCall));
connectBlocks(
@ -572,8 +570,6 @@ void CHC::internalFunctionCall(FunctionCall const& _funCall)
(errorFlag().currentValue() > 0)
);
m_context.addAssertion(errorFlag().currentValue() == 0);
errorFlag().increaseIndex();
m_context.addAssertion(errorFlag().currentValue() == previousError);
}
void CHC::externalFunctionCall(FunctionCall const& _funCall)
@ -583,6 +579,11 @@ void CHC::externalFunctionCall(FunctionCall const& _funCall)
/// so we just add the nondet_interface predicate.
solAssert(m_currentContract, "");
if (isTrustedExternalCall(&_funCall.expression()))
{
externalFunctionCallToTrustedCode(_funCall);
return;
}
FunctionType const& funType = dynamic_cast<FunctionType const&>(*_funCall.expression().annotation().type);
auto kind = funType.kind();
@ -615,6 +616,42 @@ void CHC::externalFunctionCall(FunctionCall const& _funCall)
m_context.addAssertion(errorFlag().currentValue() == 0);
}
void CHC::externalFunctionCallToTrustedCode(FunctionCall const& _funCall)
{
solAssert(m_currentContract, "");
FunctionType const& funType = dynamic_cast<FunctionType const&>(*_funCall.expression().annotation().type);
auto kind = funType.kind();
solAssert(kind == FunctionType::Kind::External || kind == FunctionType::Kind::BareStaticCall, "");
auto const* function = functionCallToDefinition(_funCall);
if (!function)
return;
// External call creates a new transaction.
auto originalTx = state().tx();
auto txOrigin = state().txMember("tx.origin");
state().newTx();
// set the transaction sender as this contract
m_context.addAssertion(state().txMember("msg.sender") == state().thisAddress());
// set the origin to be the current transaction origin
m_context.addAssertion(state().txMember("tx.origin") == txOrigin);
smtutil::Expression pred = predicate(_funCall);
auto txConstraints = m_context.state().txConstraints(*function);
m_context.addAssertion(pred && txConstraints);
// restore the original transaction data
state().newTx();
m_context.addAssertion(originalTx == state().tx());
connectBlocks(
m_currentBlock,
(m_currentFunction && !m_currentFunction->isConstructor()) ? summary(*m_currentFunction) : summary(*m_currentContract),
(errorFlag().currentValue() > 0)
);
m_context.addAssertion(errorFlag().currentValue() == 0);
}
void CHC::unknownFunctionCall(FunctionCall const&)
{
/// Function calls are not handled at the moment,
@ -1011,27 +1048,34 @@ smtutil::Expression CHC::predicate(Predicate const& _block)
smtutil::Expression CHC::predicate(FunctionCall const& _funCall)
{
/// Used only for internal calls.
FunctionType const& funType = dynamic_cast<FunctionType const&>(*_funCall.expression().annotation().type);
auto kind = funType.kind();
solAssert(kind == FunctionType::Kind::Internal || kind == FunctionType::Kind::External || kind == FunctionType::Kind::BareStaticCall, "");
auto const* function = functionCallToDefinition(_funCall);
if (!function)
return smtutil::Expression(true);
auto contractAddressValue = [this](FunctionCall const& _f) {
FunctionType const& funType = dynamic_cast<FunctionType const&>(*_f.expression().annotation().type);
if (funType.kind() == FunctionType::Kind::Internal)
return state().thisAddress();
if (MemberAccess const* callBase = dynamic_cast<MemberAccess const*>(&_f.expression()))
return expr(callBase->expression());
solAssert(false, "Unreachable!");
};
errorFlag().increaseIndex();
vector<smtutil::Expression> args{errorFlag().currentValue(), state().thisAddress(), state().crypto(), state().tx(), state().state()};
vector<smtutil::Expression> args{errorFlag().currentValue(), contractAddressValue(_funCall), state().crypto(), state().tx(), state().state()};
FunctionType const& funType = dynamic_cast<FunctionType const&>(*_funCall.expression().annotation().type);
solAssert(funType.kind() == FunctionType::Kind::Internal, "");
/// Internal calls can be made to the contract itself or a library.
auto const* contract = function->annotation().contract;
auto const& hierarchy = m_currentContract->annotation().linearizedBaseContracts;
solAssert(contract->isLibrary() || find(hierarchy.begin(), hierarchy.end(), contract) != hierarchy.end(), "");
solAssert(kind != FunctionType::Kind::Internal || contract->isLibrary() || contains(hierarchy, contract), "");
/// If the call is to a library, we use that library as the called contract.
/// If it is not, we use the current contract even if it is a call to a contract
/// up in the inheritance hierarchy, since the interfaces/predicates are different.
auto const* calledContract = contract->isLibrary() ? contract : m_currentContract;
/// If the call is to a contract not in the inheritance hierarchy, we also use that as the called contract.
/// Otherwise, the call is to some contract in the inheritance hierarchy of the current contract.
/// In this case we use current contract as the called one since the interfaces/predicates are different.
auto const* calledContract = contains(hierarchy, contract) ? m_currentContract : contract;
solAssert(calledContract, "");
bool usesStaticCall = function->stateMutability() == StateMutability::Pure || function->stateMutability() == StateMutability::View;

View File

@ -88,6 +88,7 @@ private:
void visitAddMulMod(FunctionCall const& _funCall) override;
void internalFunctionCall(FunctionCall const& _funCall);
void externalFunctionCall(FunctionCall const& _funCall);
void externalFunctionCallToTrustedCode(FunctionCall const& _funCall);
void unknownFunctionCall(FunctionCall const& _funCall);
void makeArrayPopVerificationTarget(FunctionCall const& _arrayPop) override;
/// Creates underflow/overflow verification targets.

View File

@ -2386,6 +2386,19 @@ MemberAccess const* SMTEncoder::isEmptyPush(Expression const& _expr) const
return nullptr;
}
bool SMTEncoder::isTrustedExternalCall(Expression const* _expr) {
auto memberAccess = dynamic_cast<MemberAccess const*>(_expr);
if (!memberAccess)
return false;
auto identifier = dynamic_cast<Identifier const*>(&memberAccess->expression());
return identifier &&
identifier->name() == "this" &&
identifier->annotation().referencedDeclaration &&
dynamic_cast<MagicVariableDeclaration const*>(identifier->annotation().referencedDeclaration)
;
}
string SMTEncoder::extraComment()
{
string extra;

View File

@ -295,6 +295,10 @@ protected:
/// otherwise nullptr.
MemberAccess const* isEmptyPush(Expression const& _expr) const;
/// @returns true if the given identifier is a contract which is known and trusted.
/// This means we don't have to abstract away effects of external function calls to this contract.
static bool isTrustedExternalCall(Expression const* _expr);
/// Creates symbolic expressions for the returned values
/// and set them as the components of the symbolic tuple.
void createReturnedExpressions(FunctionCall const& _funCall);

View File

@ -131,6 +131,13 @@ void CompilerStack::setRemappings(vector<Remapping> const& _remappings)
m_remappings = _remappings;
}
void CompilerStack::setViaIR(bool _viaIR)
{
if (m_stackState >= ParsedAndImported)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must set viaIR before parsing."));
m_viaIR = _viaIR;
}
void CompilerStack::setEVMVersion(langutil::EVMVersion _version)
{
if (m_stackState >= ParsedAndImported)
@ -213,6 +220,7 @@ void CompilerStack::reset(bool _keepSettings)
{
m_remappings.clear();
m_libraries.clear();
m_viaIR = false;
m_evmVersion = langutil::EVMVersion();
m_modelCheckerSettings = ModelCheckerSettings{};
m_enabledSMTSolvers = smtutil::SMTSolverChoice::All();
@ -532,10 +540,15 @@ bool CompilerStack::compile(State _stopAfter)
{
try
{
if (m_generateEvmBytecode)
compileContract(*contract, otherCompilers);
if (m_generateIR || m_generateEwasm)
if (m_viaIR || m_generateIR || m_generateEwasm)
generateIR(*contract);
if (m_generateEvmBytecode)
{
if (m_viaIR)
generateEVMFromIR(*contract);
else
compileContract(*contract, otherCompilers);
}
if (m_generateEwasm)
generateEwasm(*contract);
}
@ -1250,12 +1263,45 @@ void CompilerStack::generateIR(ContractDefinition const& _contract)
tie(compiledContract.yulIR, compiledContract.yulIROptimized) = generator.run(_contract, otherYulSources);
}
void CompilerStack::generateEVMFromIR(ContractDefinition const& _contract)
{
solAssert(m_stackState >= AnalysisPerformed, "");
if (m_hasError)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Called generateEVMFromIR with errors."));
if (!_contract.canBeDeployed())
return;
Contract& compiledContract = m_contracts.at(_contract.fullyQualifiedName());
solAssert(!compiledContract.yulIROptimized.empty(), "");
if (!compiledContract.object.bytecode.empty())
return;
// Re-parse the Yul IR in EVM dialect
yul::AssemblyStack stack(m_evmVersion, yul::AssemblyStack::Language::StrictAssembly, m_optimiserSettings);
stack.parseAndAnalyze("", compiledContract.yulIROptimized);
stack.optimize();
//cout << yul::AsmPrinter{}(*stack.parserResult()->code) << endl;
// TODO: support passing metadata
auto result = stack.assemble(yul::AssemblyStack::Machine::EVM);
compiledContract.object = std::move(*result.bytecode);
// TODO: support runtimeObject
// TODO: add EIP-170 size check for runtimeObject
// TODO: refactor assemblyItems, runtimeAssemblyItems, generatedSources,
// assemblyString, assemblyJSON, and functionEntryPoints to work with this code path
}
void CompilerStack::generateEwasm(ContractDefinition const& _contract)
{
solAssert(m_stackState >= AnalysisPerformed, "");
if (m_hasError)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Called generateEwasm with errors."));
if (!_contract.canBeDeployed())
return;
Contract& compiledContract = m_contracts.at(_contract.fullyQualifiedName());
solAssert(!compiledContract.yulIROptimized.empty(), "");
if (!compiledContract.ewasm.empty())
@ -1392,6 +1438,8 @@ string CompilerStack::createMetadata(Contract const& _contract) const
static vector<string> hashes{"ipfs", "bzzr1", "none"};
meta["settings"]["metadata"]["bytecodeHash"] = hashes.at(unsigned(m_metadataHash));
if (m_viaIR)
meta["settings"]["viaIR"] = m_viaIR;
meta["settings"]["evmVersion"] = m_evmVersion.name();
meta["settings"]["compilationTarget"][_contract.contract->sourceUnitName()] =
*_contract.contract->annotation().canonicalName;
@ -1514,7 +1562,7 @@ bytes CompilerStack::createCBORMetadata(Contract const& _contract) const
else
solAssert(m_metadataHash == MetadataHash::None, "Invalid metadata hash");
if (experimentalMode)
if (experimentalMode || m_viaIR)
encoder.pushBool("experimental", true);
if (m_release)
encoder.pushBytes("solc", VersionCompactBytes);

View File

@ -162,6 +162,10 @@ public:
m_parserErrorRecovery = _wantErrorRecovery;
}
/// Sets the pipeline to go through the Yul IR or not.
/// Must be set before parsing.
void setViaIR(bool _viaIR);
/// Set the EVM version used before running compile.
/// When called without an argument it will revert to the default version.
/// Must be set before parsing.
@ -399,7 +403,12 @@ private:
/// The IR is stored but otherwise unused.
void generateIR(ContractDefinition const& _contract);
/// Generate EVM representation for a single contract.
/// Depends on output generated by generateIR.
void generateEVMFromIR(ContractDefinition const& _contract);
/// Generate Ewasm representation for a single contract.
/// Depends on output generated by generateIR.
void generateEwasm(ContractDefinition const& _contract);
/// Links all the known library addresses in the available objects. Any unknown
@ -455,6 +464,7 @@ private:
OptimiserSettings m_optimiserSettings;
RevertStrings m_revertStrings = RevertStrings::Default;
State m_stopAfter = State::CompilationSuccessful;
bool m_viaIR = false;
langutil::EVMVersion m_evmVersion;
ModelCheckerSettings m_modelCheckerSettings;
smtutil::SMTSolverChoice m_enabledSMTSolvers;

View File

@ -323,10 +323,12 @@ Json::Value formatLinkReferences(std::map<size_t, std::string> const& linkRefere
for (auto const& ref: linkReferences)
{
string const& fullname = ref.second;
// If the link reference does not contain a colon, assume that the file name is missing and
// the whole string represents the library name.
size_t colon = fullname.rfind(':');
solAssert(colon != string::npos, "");
string file = fullname.substr(0, colon);
string name = fullname.substr(colon + 1);
string file = (colon != string::npos ? fullname.substr(0, colon) : "");
string name = (colon != string::npos ? fullname.substr(colon + 1) : fullname);
Json::Value fileObject = ret.get(file, Json::objectValue);
Json::Value libraryArray = fileObject.get(name, Json::arrayValue);
@ -414,7 +416,7 @@ std::optional<Json::Value> checkAuxiliaryInputKeys(Json::Value const& _input)
std::optional<Json::Value> checkSettingsKeys(Json::Value const& _input)
{
static set<string> keys{"parserErrorRecovery", "debug", "evmVersion", "libraries", "metadata", "optimizer", "outputSelection", "remappings", "stopAfter"};
static set<string> keys{"parserErrorRecovery", "debug", "evmVersion", "libraries", "metadata", "optimizer", "outputSelection", "remappings", "stopAfter", "viaIR"};
return checkKeys(_input, keys, "settings");
}
@ -749,6 +751,13 @@ std::variant<StandardCompiler::InputsAndSettings, Json::Value> StandardCompiler:
ret.parserErrorRecovery = settings["parserErrorRecovery"].asBool();
}
if (settings.isMember("viaIR"))
{
if (!settings["viaIR"].isBool())
return formatFatalError("JSONError", "\"settings.viaIR\" must be a Boolean.");
ret.viaIR = settings["viaIR"].asBool();
}
if (settings.isMember("evmVersion"))
{
if (!settings["evmVersion"].isString())
@ -830,8 +839,7 @@ std::variant<StandardCompiler::InputsAndSettings, Json::Value> StandardCompiler:
try
{
// @TODO use libraries only for the given source
ret.libraries[library] = util::h160(address);
ret.libraries[sourceName + ":" + library] = util::h160(address);
}
catch (util::BadHexCharacter const&)
{
@ -906,6 +914,7 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting
compilerStack.setSources(sourceList);
for (auto const& smtLib2Response: _inputsAndSettings.smtLib2Responses)
compilerStack.addSMTLib2Response(smtLib2Response.first, smtLib2Response.second);
compilerStack.setViaIR(_inputsAndSettings.viaIR);
compilerStack.setEVMVersion(_inputsAndSettings.evmVersion);
compilerStack.setParserErrorRecovery(_inputsAndSettings.parserErrorRecovery);
compilerStack.setRemappings(_inputsAndSettings.remappings);

View File

@ -72,6 +72,7 @@ private:
CompilerStack::MetadataHash metadataHash = CompilerStack::MetadataHash::IPFS;
Json::Value outputSelection;
ModelCheckerSettings modelCheckerSettings = ModelCheckerSettings{};
bool viaIR = false;
};
/// Parses the input json (and potentially invokes the read callback) and either returns

View File

@ -127,6 +127,7 @@ static string const g_strEVM = "evm";
static string const g_strEVM15 = "evm15";
static string const g_strEVMVersion = "evm-version";
static string const g_strEwasm = "ewasm";
static string const g_strExperimentalViaIR = "experimental-via-ir";
static string const g_strGeneratedSources = "generated-sources";
static string const g_strGeneratedSourcesRuntime = "generated-sources-runtime";
static string const g_strGas = "gas";
@ -211,6 +212,7 @@ static string const g_argYul = g_strYul;
static string const g_argIR = g_strIR;
static string const g_argIROptimized = g_strIROptimized;
static string const g_argEwasm = g_strEwasm;
static string const g_argExperimentalViaIR = g_strExperimentalViaIR;
static string const g_argLibraries = g_strLibraries;
static string const g_argLink = g_strLink;
static string const g_argMachine = g_strMachine;
@ -662,9 +664,16 @@ bool CommandLineInterface::parseLibraryOption(string const& _input)
serr() << "Colon separator missing in library address specifier \"" << lib << "\"" << endl;
return false;
}
string libName(lib.begin(), lib.begin() + static_cast<ptrdiff_t>(colon));
string addrString(lib.begin() + static_cast<ptrdiff_t>(colon) + 1, lib.end());
boost::trim(libName);
if (m_libraries.count(libName))
{
serr() << "Address specified more than once for library \"" << libName << "\"." << endl;
return false;
}
string addrString(lib.begin() + static_cast<ptrdiff_t>(colon) + 1, lib.end());
boost::trim(addrString);
if (addrString.substr(0, 2) == "0x")
addrString = addrString.substr(2);
@ -728,12 +737,15 @@ map<string, Json::Value> CommandLineInterface::parseAstFromInput()
void CommandLineInterface::createFile(string const& _fileName, string const& _data)
{
namespace fs = boost::filesystem;
// create directory if not existent
fs::path p(m_args.at(g_argOutputDir).as<string>());
// Do not try creating the directory if the first item is . or ..
if (p.filename() != "." && p.filename() != "..")
fs::create_directories(p);
string pathName = (p / _fileName).string();
fs::path outputDir(m_args.at(g_argOutputDir).as<string>());
// NOTE: create_directories() raises an exception if the path consists solely of '.' or '..'
// (or equivalent such as './././.'). Paths like 'a/b/.' and 'a/b/..' are fine though.
// The simplest workaround is to use an absolute path.
fs::create_directories(fs::absolute(outputDir));
string pathName = (outputDir / _fileName).string();
if (fs::exists(pathName) && !m_args.count(g_strOverwrite))
{
serr() << "Refusing to overwrite existing file \"" << pathName << "\" (use --" << g_strOverwrite << " to force)." << endl;
@ -825,6 +837,10 @@ General Information)").c_str(),
"Select desired EVM version. Either homestead, tangerineWhistle, spuriousDragon, "
"byzantium, constantinople, petersburg, istanbul (default) or berlin."
)
(
g_strExperimentalViaIR.c_str(),
"Turn on experimental compilation mode via the IR (EXPERIMENTAL)."
)
(
g_strRevertStrings.c_str(),
po::value<string>()->value_name(boost::join(g_revertStringsArgs, ",")),
@ -1456,6 +1472,8 @@ bool CommandLineInterface::processInput()
if (m_args.count(g_argLibraries))
m_compiler->setLibraries(m_libraries);
if (m_args.count(g_argExperimentalViaIR))
m_compiler->setViaIR(true);
m_compiler->setEVMVersion(m_evmVersion);
m_compiler->setRevertStringBehaviour(m_revertStrings);
// TODO: Perhaps we should not compile unless requested

View File

@ -118,7 +118,7 @@ function test_solc_behaviour()
# Remove bytecode (but not linker references).
sed -i.bak -E -e 's/(\"object\":\")[0-9a-f]+([^"]*\")/\1<BYTECODE REMOVED>\2/g' "$stdout_path"
sed -i.bak -E -e 's/(\"object\":\"[^"]+\$__)[0-9a-f]+(\")/\1<BYTECODE REMOVED>\2/g' "$stdout_path"
sed -i.bak -E -e 's/(__\$[0-9a-f]{34}\$__)[0-9a-f]+(__\$[0-9a-f]{34}\$__)/\1<BYTECODE REMOVED>\2/g' "$stdout_path"
sed -i.bak -E -e 's/([0-9a-f]{34}\$__)[0-9a-f]+(__\$[0-9a-f]{17})/\1<BYTECODE REMOVED>\2/g' "$stdout_path"
# Replace escaped newlines by actual newlines for readability
sed -i.bak -E -e 's/\\n/\'$'\n/g' "$stdout_path"
@ -128,7 +128,7 @@ function test_solc_behaviour()
sed -i.bak -e '/^Warning (3805): This is a pre-release compiler version, please do not use it in production./d' "$stderr_path"
sed -i.bak -e 's/\(^[ ]*auxdata: \)0x[0-9a-f]*$/\1<AUXDATA REMOVED>/' "$stdout_path"
sed -i.bak -e 's/ Consider adding "pragma .*$//' "$stderr_path"
sed -i.bak -e 's/\(Unimplemented feature error: .* in \).*$/\1<FILENAME REMOVED>/' "$stderr_path"
sed -i.bak -e 's/\(Unimplemented feature error.* in \).*$/\1<FILENAME REMOVED>/' "$stderr_path"
sed -i.bak -e 's/"version": "[^"]*"/"version": "<VERSION REMOVED>"/' "$stdout_path"
# Remove bytecode (but not linker references). Since non-JSON output is unstructured,
@ -137,7 +137,7 @@ function test_solc_behaviour()
# 64697066735822 = hex encoding of 0x64 'i' 'p' 'f' 's' 0x58 0x22
# 64736f6c63 = hex encoding of 0x64 's' 'o' 'l' 'c'
sed -i.bak -E -e 's/[0-9a-f]*64697066735822[0-9a-f]+64736f6c63[0-9a-f]+/<BYTECODE REMOVED>/g' "$stdout_path"
sed -i.bak -E -e 's/(__\$[0-9a-f]{34}\$__)[0-9a-f]+(__\$[0-9a-f]{34}\$__)/\1<BYTECODE REMOVED>\2/g' "$stdout_path"
sed -i.bak -E -e 's/([0-9a-f]{17}\$__)[0-9a-f]+(__\$[0-9a-f]{17})/\1<BYTECODE REMOVED>\2/g' "$stdout_path"
sed -i.bak -E -e 's/[0-9a-f]+((__\$[0-9a-f]{34}\$__)*<BYTECODE REMOVED>)/<BYTECODE REMOVED>\1/g' "$stdout_path"
# Remove trailing empty lines. Needs a line break to make OSX sed happy.

View File

@ -20,7 +20,7 @@
},
"settings": {
"libraries": {
"contract/test.sol": {
"A": {
"L": "0x1234567890123456789012345678901234567890"
}
},

View File

@ -1,7 +1,7 @@
{
"language": "Solidity",
"sources": {
"A": {
"A\"B": {
"content": "
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.0;
@ -20,7 +20,7 @@
},
"settings": {
"libraries": {
"contract/test\"test.sol": {
"A\"B": {
"L": "0x1234567890123456789012345678901234567890"
}
},

View File

@ -1 +1 @@
{"contracts":{"A":{"C":{"evm":{"bytecode":{"generatedSources":[],"linkReferences":{},"object":"<BYTECODE REMOVED>","opcodes":"<OPCODES REMOVED>","sourceMap":"<SOURCEMAP REMOVED>"}}}}},"sources":{"A":{"id":0}}}
{"contracts":{"A\"B":{"C":{"evm":{"bytecode":{"generatedSources":[],"linkReferences":{},"object":"<BYTECODE REMOVED>","opcodes":"<OPCODES REMOVED>","sourceMap":"<SOURCEMAP REMOVED>"}}}}},"sources":{"A\"B":{"id":0}}}

View File

@ -28,7 +28,7 @@
},
"settings": {
"libraries": {
"contract/test.sol": {
"A": {
"L1": "0x1234567890123456789012345678901234567890"
}
},

View File

@ -0,0 +1 @@
--strict-assembly --libraries library.sol:L:0x1234567890123456789012345678901234567890,library.sol:L:0x0987654321098765432109876543210987654321

View File

@ -0,0 +1 @@
Address specified more than once for library "library.sol:L".

View File

@ -0,0 +1,5 @@
object "a" {
code {
let addr := linkersymbol("library.sol:L")
}
}

View File

@ -0,0 +1 @@
--strict-assembly --libraries L:0x1234567890123456789012345678901234567890

View File

@ -0,0 +1 @@
Warning: Yul is still experimental. Please use the output with care.

View File

@ -0,0 +1,5 @@
object "a" {
code {
let addr := linkersymbol("L")
}
}

View File

@ -0,0 +1,16 @@
======= linking_strict_assembly_no_file_name_in_link_reference/input.yul (EVM) =======
Pretty printed source:
object "a" {
code { let addr := linkersymbol("L") }
}
Binary representation:
73123456789012345678901234567890123456789050
Text representation:
linkerSymbol("8aa64f937099b65a4febc243a5ae0f2d6416bb9e473c30dd29c1ee498fb7c5a8")
/* "linking_strict_assembly_no_file_name_in_link_reference/input.yul":22:67 */
pop

View File

@ -0,0 +1 @@
--strict-assembly --libraries library1.sol:L:0x1111111111111111111111111111111111111111,library2.sol:L:0x2222222222222222222222222222222222222222

View File

@ -0,0 +1 @@
Warning: Yul is still experimental. Please use the output with care.

View File

@ -0,0 +1,6 @@
object "a" {
code {
let addr1 := linkersymbol("library1.sol:L")
let addr2 := linkersymbol("library2.sol:L")
}
}

View File

@ -0,0 +1,22 @@
======= linking_strict_assembly_same_library_name_different_files/input.yul (EVM) =======
Pretty printed source:
object "a" {
code {
let addr1 := linkersymbol("library1.sol:L")
let addr2 := linkersymbol("library2.sol:L")
}
}
Binary representation:
7311111111111111111111111111111111111111117322222222222222222222222222222222222222225050
Text representation:
linkerSymbol("f3ffc10c396a7cc41ae954b050792839d20947bf73497d30c49a9fda1ea477ec")
/* "linking_strict_assembly_same_library_name_different_files/input.yul":32:75 */
linkerSymbol("c3523432985587641d17c68161d2f700c57aaf4ed21cda4f25d76193c831f97f")
/* "linking_strict_assembly_same_library_name_different_files/input.yul":22:133 */
pop
pop

View File

@ -0,0 +1 @@
--strict-assembly --libraries library1.sol:L:0x1234567890123456789012345678901234567890

View File

@ -0,0 +1 @@
Warning: Yul is still experimental. Please use the output with care.

View File

@ -0,0 +1,6 @@
object "a" {
code {
let addr1 := linkersymbol("library1.sol:L")
let addr2 := linkersymbol("library2.sol:L")
}
}

View File

@ -0,0 +1,22 @@
======= linking_strict_assembly_same_library_name_different_files_in_link_references/input.yul (EVM) =======
Pretty printed source:
object "a" {
code {
let addr1 := linkersymbol("library1.sol:L")
let addr2 := linkersymbol("library2.sol:L")
}
}
Binary representation:
73123456789012345678901234567890123456789073__$c3523432985587641d17c68161d2f700c5$__5050
Text representation:
linkerSymbol("f3ffc10c396a7cc41ae954b050792839d20947bf73497d30c49a9fda1ea477ec")
/* "linking_strict_assembly_same_library_name_different_files_in_link_references/input.yul":32:75 */
linkerSymbol("c3523432985587641d17c68161d2f700c57aaf4ed21cda4f25d76193c831f97f")
/* "linking_strict_assembly_same_library_name_different_files_in_link_references/input.yul":22:133 */
pop
pop

View File

@ -16,7 +16,7 @@
},
"outputSelection":
{
"*": { "*": ["ewasm.wast"] }
"*": { "*": ["ewasm.wast", "ewasm.wasm"] }
}
}
}

View File

@ -1,4 +1,4 @@
{"contracts":{"A":{"C":{"ewasm":{"wast":"(module
{"contracts":{"A":{"C":{"ewasm":{"wasm":"0061736d01000000013a0860000060017e017e60047e7e7e7e017f60087e7e7e7e7e7e7e7e00600c7e7e7e7e7e7e7e7e7e7e7e7e0060017f0060027f7f0060037f7f7f0002510408657468657265756d08636f6465436f7079000708657468657265756d06726576657274000608657468657265756d0c67657443616c6c56616c7565000508657468657265756d0666696e6973680006030a090002020401010103030503010001060100071102066d656d6f72790200046d61696e0004009d030c435f325f6465706c6f7965640061736d0100000001160460000060017e017e60047e7e7e7e017f60027f7f0002130108657468657265756d067265766572740003030504000201010503010001060100071102066d656d6f72790200046d61696e00010ab60204ca0104017e027f057e037f02404200210020002000200042c00010022101200141c0006a210220022001490440000b20001003421086210320032000421088100384422086210420042000422088100484210520022005370000200241086a2005370000200241106a20053700004280011003421086210620064280014210881003844220862107200241186a2007428001422088100484370000200020002000200010022108200020002000200010022109200941c0006a210a200a2009490440000b200a200810000b0b2901017f024042002000200184200284520440000b42002003422088520440000b2003a721040b20040b1f01017e024020004208864280fe0383200042088842ff01838421010b20010b1e01027e02402000100342108621022002200042108810038421010b20010b0aec0309dc0103017e027f057e02404200210020002000200042c00010052101200141c0006a210220022001490440000b2000100a210320022003370000200241086a2003370000200241106a2003370000200241186a428001100a370000410010024100290000100a2104410041086a290000100a2105410041106a290000100a210620042005842006410041186a290000100a84845045044020002000200020002000200020002000100c0b4290032107200020002000200020002000200042ce012000200020002007100720002000200020002000200020002007100b0b0b2901017f024042002000200184200284520440000b42002003422088520440000b2003a721040b20040b2601027f0240200020012002200310052105200541c0006a210420042005490440000b0b20040b25000240200020012002200310062004200520062007100520082009200a200b100510000b0b1f01017e024020004208864280fe0383200042088842ff01838421010b20010b1e01027e02402000100842108621022002200042108810088421010b20010b1e01027e02402000100942208621022002200042208810098421010b20010b1b000240200020012002200310062004200520062007100510030b0b1b000240200020012002200310062004200520062007100510010b0b","wast":"(module
;; custom section for sub-module
;; The Keccak-256 hash of the text representation of \"C_2_deployed\": f03f5b9154b9eb6803a947177e38e92e2860de95e90ba0e75eb71a58f18ed589
;; (@custom \"C_2_deployed\" \"0061736d0100000001160460000060017e017e60047e7e7e7e017f60027f7f0002130108657468657265756d067265766572740003030504000201010503010001060100071102066d656d6f72790200046d61696e00010ab60204ca0104017e027f057e037f02404200210020002000200042c00010022101200141c0006a210220022001490440000b20001003421086210320032000421088100384422086210420042000422088100484210520022005370000200241086a2005370000200241106a20053700004280011003421086210620064280014210881003844220862107200241186a2007428001422088100484370000200020002000200010022108200020002000200010022109200941c0006a210a200a2009490440000b200a200810000b0b2901017f024042002000200184200284520440000b42002003422088520440000b2003a721040b20040b1f01017e024020004208864280fe0383200042088842ff01838421010b20010b1e01027e02402000100342108621022002200042108810038421010b20010b\")

View File

@ -0,0 +1,22 @@
{
"language": "Solidity",
"sources":
{
"A":
{
"content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0; abstract contract C { }"
}
},
"settings":
{
"optimizer":
{
"enabled": true,
"details": {"yul": true}
},
"outputSelection":
{
"*": { "*": ["ewasm.wast", "ewasm.wasm"] }
}
}
}

View File

@ -0,0 +1 @@
{"contracts":{"A":{"C":{"ewasm":{"wasm":"","wast":""}}}},"sources":{"A":{"id":0}}}

View File

@ -0,0 +1,21 @@
{
"language": "Solidity",
"sources":
{
"A":
{
"content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0; contract C {} contract D { function f() public { C c = new C(); } }"
}
},
"settings":
{
"optimizer": {
"enabled": true
},
"outputSelection":
{
"*": { "*": ["ir", "evm.bytecode.object", "evm.bytecode.generatedSources", "evm.deployedBytecode.object"] }
},
"viaIR": true
}
}

View File

@ -0,0 +1,210 @@
{"contracts":{"A":{"C":{"evm":{"bytecode":{"generatedSources":[],"linkReferences":{},"object":"<BYTECODE REMOVED>","opcodes":"<OPCODES REMOVED>","sourceMap":""},"deployedBytecode":{"generatedSources":[],"immutableReferences":{},"linkReferences":{},"object":"","opcodes":"","sourceMap":""}},"ir":"/*******************************************************
* WARNING *
* Solidity to Yul compilation is still EXPERIMENTAL *
* It can result in LOSS OF FUNDS or worse *
* !USE AT YOUR OWN RISK! *
*******************************************************/
object \"C_2\" {
code {
mstore(64, 128)
if callvalue() { revert(0, 0) }
constructor_C_2()
codecopy(0, dataoffset(\"C_2_deployed\"), datasize(\"C_2_deployed\"))
return(0, datasize(\"C_2_deployed\"))
function constructor_C_2() {
}
}
object \"C_2_deployed\" {
code {
mstore(64, 128)
if iszero(lt(calldatasize(), 4))
{
let selector := shift_right_224_unsigned(calldataload(0))
switch selector
default {}
}
if iszero(calldatasize()) { }
revert(0, 0)
function shift_right_224_unsigned(value) -> newValue {
newValue :=
shr(224, value)
}
}
}
}
"},"D":{"evm":{"bytecode":{"generatedSources":[],"linkReferences":{},"object":"<BYTECODE REMOVED>","opcodes":"<OPCODES REMOVED>","sourceMap":""},"deployedBytecode":{"generatedSources":[],"immutableReferences":{},"linkReferences":{},"object":"","opcodes":"","sourceMap":""}},"ir":"/*******************************************************
* WARNING *
* Solidity to Yul compilation is still EXPERIMENTAL *
* It can result in LOSS OF FUNDS or worse *
* !USE AT YOUR OWN RISK! *
*******************************************************/
object \"D_13\" {
code {
mstore(64, 128)
if callvalue() { revert(0, 0) }
constructor_D_13()
codecopy(0, dataoffset(\"D_13_deployed\"), datasize(\"D_13_deployed\"))
return(0, datasize(\"D_13_deployed\"))
function constructor_D_13() {
}
}
object \"D_13_deployed\" {
code {
mstore(64, 128)
if iszero(lt(calldatasize(), 4))
{
let selector := shift_right_224_unsigned(calldataload(0))
switch selector
case 0x26121ff0
{
// f()
if callvalue() { revert(0, 0) }
abi_decode_tuple_(4, calldatasize())
fun_f_12()
let memPos := allocateMemory(0)
let memEnd := abi_encode_tuple__to__fromStack(memPos )
return(memPos, sub(memEnd, memPos))
}
default {}
}
if iszero(calldatasize()) { }
revert(0, 0)
function abi_decode_tuple_(headStart, dataEnd) {
if slt(sub(dataEnd, headStart), 0) { revert(0, 0) }
}
function abi_encode_tuple__to__fromStack(headStart ) -> tail {
tail := add(headStart, 0)
}
function allocateMemory(size) -> memPtr {
memPtr := mload(64)
let newFreePtr := add(memPtr, size)
// protect against overflow
if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { panic_error() }
mstore(64, newFreePtr)
}
function allocateTemporaryMemory() -> memPtr {
memPtr := mload(64)
}
function fun_f_12() {
let _1 := allocateTemporaryMemory()
let _2 := add(_1, datasize(\"C_2\"))
if or(gt(_2, 0xffffffffffffffff), lt(_2, _1)) { panic_error() }
datacopy(_1, dataoffset(\"C_2\"), datasize(\"C_2\"))
_2 := abi_encode_tuple__to__fromStack(_2)
let expr_9_address := create(0, _1, sub(_2, _1))
releaseTemporaryMemory()
let vloc_c_6_address := expr_9_address
}
function panic_error() {
invalid()
}
function releaseTemporaryMemory() {
}
function shift_right_224_unsigned(value) -> newValue {
newValue :=
shr(224, value)
}
}
/*******************************************************
* WARNING *
* Solidity to Yul compilation is still EXPERIMENTAL *
* It can result in LOSS OF FUNDS or worse *
* !USE AT YOUR OWN RISK! *
*******************************************************/
object \"C_2\" {
code {
mstore(64, 128)
if callvalue() { revert(0, 0) }
constructor_C_2()
codecopy(0, dataoffset(\"C_2_deployed\"), datasize(\"C_2_deployed\"))
return(0, datasize(\"C_2_deployed\"))
function constructor_C_2() {
}
}
object \"C_2_deployed\" {
code {
mstore(64, 128)
if iszero(lt(calldatasize(), 4))
{
let selector := shift_right_224_unsigned(calldataload(0))
switch selector
default {}
}
if iszero(calldatasize()) { }
revert(0, 0)
function shift_right_224_unsigned(value) -> newValue {
newValue :=
shr(224, value)
}
}
}
}
}
}
"}}},"errors":[{"component":"general","errorCode":"2072","formattedMessage":"A:2:73: Warning: Unused local variable.
pragma solidity >=0.0; contract C {} contract D { function f() public { C c = new C(); } }
^-^
","message":"Unused local variable.","severity":"warning","sourceLocation":{"end":111,"file":"A","start":108},"type":"Warning"}],"sources":{"A":{"id":0}}}

View File

@ -0,0 +1 @@
--ir-optimized --experimental-via-ir --optimize --bin --bin-runtime

View File

@ -0,0 +1,5 @@
Warning: Unused local variable.
--> viair_subobjects/input.sol:7:9:
|
7 | C c = new C();
| ^^^

View File

@ -0,0 +1,9 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.6.0;
contract C {}
contract D {
function f() public {
C c = new C();
}
}

View File

@ -0,0 +1,109 @@
======= viair_subobjects/input.sol:C =======
Binary:
60806040523415600f5760006000fd5b600a80601e600039806000f350fe608060405260006000fd
Binary of the runtime part:
Optimized IR:
/*******************************************************
* WARNING *
* Solidity to Yul compilation is still EXPERIMENTAL *
* It can result in LOSS OF FUNDS or worse *
* !USE AT YOUR OWN RISK! *
*******************************************************/
object "C_2" {
code {
{
mstore(64, 128)
if callvalue() { revert(0, 0) }
let _1 := datasize("C_2_deployed")
codecopy(0, dataoffset("C_2_deployed"), _1)
return(0, _1)
}
}
object "C_2_deployed" {
code {
{
mstore(64, 128)
revert(0, 0)
}
}
}
}
======= viair_subobjects/input.sol:D =======
Binary:
608060405234156100105760006000fd5b60d380610020600039806000f350fe6080604052600436101515610074576000803560e01c6326121ff0141561007257341561002a578081fd5b806003193601121561003a578081fd5b6028806080016080811067ffffffffffffffff8211171561005757fe5b50806100ab60803980608083f05050806100708261007e565bf35b505b60006000fd6100a9565b6000604051905081810181811067ffffffffffffffff8211171561009e57fe5b80604052505b919050565bfe60806040523415600f5760006000fd5b600a80601e600039806000f350fe608060405260006000fd
Binary of the runtime part:
Optimized IR:
/*******************************************************
* WARNING *
* Solidity to Yul compilation is still EXPERIMENTAL *
* It can result in LOSS OF FUNDS or worse *
* !USE AT YOUR OWN RISK! *
*******************************************************/
object "D_13" {
code {
{
mstore(64, 128)
if callvalue() { revert(0, 0) }
let _1 := datasize("D_13_deployed")
codecopy(0, dataoffset("D_13_deployed"), _1)
return(0, _1)
}
}
object "D_13_deployed" {
code {
{
mstore(64, 128)
if iszero(lt(calldatasize(), 4))
{
let _1 := 0
if eq(0x26121ff0, shr(224, calldataload(_1)))
{
if callvalue() { revert(_1, _1) }
if slt(add(calldatasize(), not(3)), _1) { revert(_1, _1) }
let _2 := datasize("C_2")
let _3 := add(128, _2)
if or(gt(_3, 0xffffffffffffffff), lt(_3, 128)) { invalid() }
datacopy(128, dataoffset("C_2"), _2)
pop(create(_1, 128, _2))
return(allocateMemory(_1), _1)
}
}
revert(0, 0)
}
function allocateMemory(size) -> memPtr
{
memPtr := mload(64)
let newFreePtr := add(memPtr, size)
if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { invalid() }
mstore(64, newFreePtr)
}
}
object "C_2" {
code {
{
mstore(64, 128)
if callvalue() { revert(0, 0) }
let _1 := datasize("C_2_deployed")
codecopy(0, dataoffset("C_2_deployed"), _1)
return(0, _1)
}
}
object "C_2_deployed" {
code {
{
mstore(64, 128)
revert(0, 0)
}
}
}
}
}
}

View File

@ -1,5 +1,5 @@
Error (1834): Unimplemented feature error: Copying from storage to storage is not yet implemented. in <FILENAME REMOVED>
--> yul_unimplemented/input.sol:7:9:
Error (1834): Unimplemented feature error in <FILENAME REMOVED>
--> yul_unimplemented/input.sol:8:9:
|
7 | a = b;
8 | x.f();
| ^^^^^

View File

@ -1,9 +1,10 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.0;
library L { function f(uint) public {} }
contract test {
bytes a;
bytes b;
using L for uint;
function f() public {
a = b;
uint x;
x.f();
}
}

View File

@ -282,6 +282,38 @@ BOOST_AUTO_TEST_CASE(metadata_useLiteralContent)
check(sourceCode, false);
}
BOOST_AUTO_TEST_CASE(metadata_viair)
{
char const* sourceCode = R"(
pragma solidity >=0.0;
contract test {
}
)";
auto check = [](char const* _src, bool _viair)
{
CompilerStack compilerStack;
compilerStack.setSources({{"", std::string(_src)}});
compilerStack.setEVMVersion(solidity::test::CommonOptions::get().evmVersion());
compilerStack.setOptimiserSettings(solidity::test::CommonOptions::get().optimize);
compilerStack.setViaIR(_viair);
BOOST_REQUIRE_MESSAGE(compilerStack.compile(), "Compiling contract failed");
string metadata_str = compilerStack.metadata("test");
Json::Value metadata;
util::jsonParseStrict(metadata_str, metadata);
BOOST_CHECK(solidity::test::isValidMetadata(metadata_str));
BOOST_CHECK(metadata.isMember("settings"));
if (_viair)
{
BOOST_CHECK(metadata["settings"].isMember("viaIR"));
BOOST_CHECK(metadata["settings"]["viaIR"].asBool());
}
};
check(sourceCode, true);
check(sourceCode, false);
}
BOOST_AUTO_TEST_CASE(metadata_revert_strings)
{
CompilerStack compilerStack;

View File

@ -2500,25 +2500,6 @@ BOOST_AUTO_TEST_CASE(copying_bytes_multiassign)
ABI_CHECK(callContractFunction("val()"), encodeArgs(0x80));
}
BOOST_AUTO_TEST_CASE(delete_removes_bytes_data)
{
char const* sourceCode = R"(
contract c {
fallback() external { data = msg.data; }
function del() public returns (bool) { delete data; return true; }
bytes data;
}
)";
ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode);
ABI_CHECK(callContractFunction("---", 7), bytes());
BOOST_CHECK(!storageEmpty(m_contractAddress));
ABI_CHECK(callContractFunction("del()", 7), encodeArgs(true));
BOOST_CHECK(storageEmpty(m_contractAddress));
);
}
BOOST_AUTO_TEST_CASE(copy_from_calldata_removes_bytes_data)
{
char const* sourceCode = R"(
@ -2540,83 +2521,6 @@ BOOST_AUTO_TEST_CASE(copy_from_calldata_removes_bytes_data)
);
}
BOOST_AUTO_TEST_CASE(copy_removes_bytes_data)
{
char const* sourceCode = R"(
contract c {
function set() public returns (bool) { data1 = msg.data; return true; }
function reset() public returns (bool) { data1 = data2; return true; }
bytes data1;
bytes data2;
}
)";
compileAndRun(sourceCode);
ABI_CHECK(callContractFunction("set()", 1, 2, 3, 4, 5), encodeArgs(true));
BOOST_CHECK(!storageEmpty(m_contractAddress));
ABI_CHECK(callContractFunction("reset()"), encodeArgs(true));
BOOST_CHECK(storageEmpty(m_contractAddress));
}
BOOST_AUTO_TEST_CASE(bytes_inside_mappings)
{
char const* sourceCode = R"(
contract c {
function set(uint key) public returns (bool) { data[key] = msg.data; return true; }
function copy(uint from, uint to) public returns (bool) { data[to] = data[from]; return true; }
mapping(uint => bytes) data;
}
)";
compileAndRun(sourceCode);
// store a short byte array at 1 and a longer one at 2
ABI_CHECK(callContractFunction("set(uint256)", 1, 2), encodeArgs(true));
ABI_CHECK(callContractFunction("set(uint256)", 2, 2, 3, 4, 5), encodeArgs(true));
BOOST_CHECK(!storageEmpty(m_contractAddress));
// copy shorter to longer
ABI_CHECK(callContractFunction("copy(uint256,uint256)", 1, 2), encodeArgs(true));
BOOST_CHECK(!storageEmpty(m_contractAddress));
// copy empty to both
ABI_CHECK(callContractFunction("copy(uint256,uint256)", 99, 1), encodeArgs(true));
BOOST_CHECK(!storageEmpty(m_contractAddress));
ABI_CHECK(callContractFunction("copy(uint256,uint256)", 99, 2), encodeArgs(true));
BOOST_CHECK(storageEmpty(m_contractAddress));
}
BOOST_AUTO_TEST_CASE(struct_containing_bytes_copy_and_delete)
{
char const* sourceCode = R"(
contract c {
struct Struct { uint a; bytes data; uint b; }
Struct data1;
Struct data2;
function set(uint _a, bytes calldata _data, uint _b) external returns (bool) {
data1.a = _a;
data1.b = _b;
data1.data = _data;
return true;
}
function copy() public returns (bool) {
data1 = data2;
return true;
}
function del() public returns (bool) {
delete data1;
return true;
}
}
)";
compileAndRun(sourceCode);
string data = "123456789012345678901234567890123";
BOOST_CHECK(storageEmpty(m_contractAddress));
ABI_CHECK(callContractFunction("set(uint256,bytes,uint256)", 12, 0x60, 13, u256(data.length()), data), encodeArgs(true));
BOOST_CHECK(!storageEmpty(m_contractAddress));
ABI_CHECK(callContractFunction("copy()"), encodeArgs(true));
BOOST_CHECK(storageEmpty(m_contractAddress));
ABI_CHECK(callContractFunction("set(uint256,bytes,uint256)", 12, 0x60, 13, u256(data.length()), data), encodeArgs(true));
BOOST_CHECK(!storageEmpty(m_contractAddress));
ABI_CHECK(callContractFunction("del()"), encodeArgs(true));
BOOST_CHECK(storageEmpty(m_contractAddress));
}
BOOST_AUTO_TEST_CASE(storing_invalid_boolean)
{
char const* sourceCode = R"(
@ -2801,197 +2705,6 @@ BOOST_AUTO_TEST_CASE(bytes_in_arguments)
);
}
BOOST_AUTO_TEST_CASE(fixed_array_cleanup)
{
char const* sourceCode = R"(
contract c {
uint spacer1;
uint spacer2;
uint[20] data;
function fill() public {
for (uint i = 0; i < data.length; ++i) data[i] = i+1;
}
function clear() public { delete data; }
}
)";
ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode);
BOOST_CHECK(storageEmpty(m_contractAddress));
ABI_CHECK(callContractFunction("fill()"), bytes());
BOOST_CHECK(!storageEmpty(m_contractAddress));
ABI_CHECK(callContractFunction("clear()"), bytes());
BOOST_CHECK(storageEmpty(m_contractAddress));
);
}
BOOST_AUTO_TEST_CASE(short_fixed_array_cleanup)
{
char const* sourceCode = R"(
contract c {
uint spacer1;
uint spacer2;
uint[3] data;
function fill() public {
for (uint i = 0; i < data.length; ++i) data[i] = i+1;
}
function clear() public { delete data; }
}
)";
ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode);
BOOST_CHECK(storageEmpty(m_contractAddress));
ABI_CHECK(callContractFunction("fill()"), bytes());
BOOST_CHECK(!storageEmpty(m_contractAddress));
ABI_CHECK(callContractFunction("clear()"), bytes());
BOOST_CHECK(storageEmpty(m_contractAddress));
);
}
BOOST_AUTO_TEST_CASE(dynamic_array_cleanup)
{
char const* sourceCode = R"(
contract c {
uint[20] spacer;
uint[] dynamic;
function fill() public {
for (uint i = 0; i < 21; ++i)
dynamic.push(i + 1);
}
function halfClear() public {
while (dynamic.length > 5)
dynamic.pop();
}
function fullClear() public { delete dynamic; }
}
)";
ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode);
BOOST_CHECK(storageEmpty(m_contractAddress));
ABI_CHECK(callContractFunction("fill()"), bytes());
BOOST_CHECK(!storageEmpty(m_contractAddress));
ABI_CHECK(callContractFunction("halfClear()"), bytes());
BOOST_CHECK(!storageEmpty(m_contractAddress));
ABI_CHECK(callContractFunction("fullClear()"), bytes());
BOOST_CHECK(storageEmpty(m_contractAddress));
);
}
BOOST_AUTO_TEST_CASE(dynamic_multi_array_cleanup)
{
char const* sourceCode = R"(
contract c {
struct s { uint[][] d; }
s[] data;
function fill() public returns (uint) {
while (data.length < 3)
data.push();
while (data[2].d.length < 4)
data[2].d.push();
while (data[2].d[3].length < 5)
data[2].d[3].push();
data[2].d[3][4] = 8;
return data[2].d[3][4];
}
function clear() public { delete data; }
}
)";
ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode);
BOOST_CHECK(storageEmpty(m_contractAddress));
ABI_CHECK(callContractFunction("fill()"), encodeArgs(8));
BOOST_CHECK(!storageEmpty(m_contractAddress));
ABI_CHECK(callContractFunction("clear()"), bytes());
BOOST_CHECK(storageEmpty(m_contractAddress));
);
}
BOOST_AUTO_TEST_CASE(array_copy_storage_storage_dyn_dyn)
{
char const* sourceCode = R"(
contract c {
uint[] data1;
uint[] data2;
function setData1(uint length, uint index, uint value) public {
data1 = new uint[](length);
if (index < length)
data1[index] = value;
}
function copyStorageStorage() public { data2 = data1; }
function getData2(uint index) public returns (uint len, uint val) {
len = data2.length; if (index < len) val = data2[index];
}
}
)";
compileAndRun(sourceCode);
ABI_CHECK(callContractFunction("setData1(uint256,uint256,uint256)", 10, 5, 4), bytes());
ABI_CHECK(callContractFunction("copyStorageStorage()"), bytes());
ABI_CHECK(callContractFunction("getData2(uint256)", 5), encodeArgs(10, 4));
ABI_CHECK(callContractFunction("setData1(uint256,uint256,uint256)", 0, 0, 0), bytes());
ABI_CHECK(callContractFunction("copyStorageStorage()"), bytes());
ABI_CHECK(callContractFunction("getData2(uint256)", 0), encodeArgs(0, 0));
BOOST_CHECK(storageEmpty(m_contractAddress));
}
BOOST_AUTO_TEST_CASE(array_copy_target_leftover)
{
// test that leftover elements in the last slot of target are correctly cleared during assignment
char const* sourceCode = R"(
contract c {
byte[10] data1;
bytes2[32] data2;
function test() public returns (uint check, uint res1, uint res2) {
uint i;
for (i = 0; i < data2.length; ++i)
data2[i] = 0xffff;
check = uint(uint16(data2[31])) * 0x10000 | uint(uint16(data2[14]));
for (i = 0; i < data1.length; ++i)
data1[i] = byte(uint8(1 + i));
data2 = data1;
for (i = 0; i < 16; ++i)
res1 |= uint(uint16(data2[i])) * 0x10000**i;
for (i = 0; i < 16; ++i)
res2 |= uint(uint16(data2[16 + i])) * 0x10000**i;
}
}
)";
compileAndRun(sourceCode);
ABI_CHECK(callContractFunction("test()"), encodeArgs(u256("0xffffffff"), asString(fromHex("0000000000000000000000000a00090008000700060005000400030002000100")), asString(fromHex("0000000000000000000000000000000000000000000000000000000000000000"))));
}
BOOST_AUTO_TEST_CASE(array_copy_storage_storage_struct)
{
char const* sourceCode = R"(
contract c {
struct Data { uint x; uint y; }
Data[] data1;
Data[] data2;
function test() public returns (uint x, uint y) {
while (data1.length < 9)
data1.push();
data1[8].x = 4;
data1[8].y = 5;
data2 = data1;
x = data2[8].x;
y = data2[8].y;
while (data1.length > 0)
data1.pop();
data2 = data1;
}
}
)";
compileAndRun(sourceCode);
ABI_CHECK(callContractFunction("test()"), encodeArgs(4, 5));
BOOST_CHECK(storageEmpty(m_contractAddress));
}
BOOST_AUTO_TEST_CASE(array_copy_storage_abi)
{
// NOTE: This does not really test copying from storage to ABI directly,
@ -3048,331 +2761,6 @@ BOOST_AUTO_TEST_CASE(array_copy_storage_abi)
);
}
BOOST_AUTO_TEST_CASE(array_pop_uint16_transition)
{
char const* sourceCode = R"(
contract c {
uint16[] data;
function test() public returns (uint16 x, uint16 y, uint16 z) {
for (uint i = 1; i <= 48; i++)
data.push(uint16(i));
for (uint j = 1; j <= 10; j++)
data.pop();
x = data[data.length - 1];
for (uint k = 1; k <= 10; k++)
data.pop();
y = data[data.length - 1];
for (uint l = 1; l <= 10; l++)
data.pop();
z = data[data.length - 1];
for (uint m = 1; m <= 18; m++)
data.pop();
}
}
)";
ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode);
ABI_CHECK(callContractFunction("test()"), encodeArgs(38, 28, 18));
BOOST_CHECK(storageEmpty(m_contractAddress));
);
}
BOOST_AUTO_TEST_CASE(array_pop_uint24_transition)
{
char const* sourceCode = R"(
contract c {
uint256 a;
uint256 b;
uint256 c;
uint24[] data;
function test() public returns (uint24 x, uint24 y) {
for (uint i = 1; i <= 30; i++)
data.push(uint24(i));
for (uint j = 1; j <= 10; j++)
data.pop();
x = data[data.length - 1];
for (uint k = 1; k <= 10; k++)
data.pop();
y = data[data.length - 1];
for (uint l = 1; l <= 10; l++)
data.pop();
}
}
)";
ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode);
ABI_CHECK(callContractFunction("test()"), encodeArgs(20, 10));
BOOST_CHECK(storageEmpty(m_contractAddress));
);
}
BOOST_AUTO_TEST_CASE(array_pop_array_transition)
{
char const* sourceCode = R"(
contract c {
uint256 a;
uint256 b;
uint256 c;
uint16[] inner = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
uint16[][] data;
function test() public returns (uint x, uint y, uint z) {
for (uint i = 1; i <= 48; i++)
data.push(inner);
for (uint j = 1; j <= 10; j++)
data.pop();
x = data[data.length - 1][0];
for (uint k = 1; k <= 10; k++)
data.pop();
y = data[data.length - 1][1];
for (uint l = 1; l <= 10; l++)
data.pop();
z = data[data.length - 1][2];
for (uint m = 1; m <= 18; m++)
data.pop();
delete inner;
}
}
)";
compileAndRun(sourceCode);
ABI_CHECK(callContractFunction("test()"), encodeArgs(1, 2, 3));
BOOST_CHECK(storageEmpty(m_contractAddress));
}
BOOST_AUTO_TEST_CASE(array_pop_storage_empty)
{
char const* sourceCode = R"(
contract c {
uint[] data;
function test() public {
data.push(7);
data.pop();
}
}
)";
ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode);
ABI_CHECK(callContractFunction("test()"), encodeArgs());
BOOST_CHECK(storageEmpty(m_contractAddress));
);
}
BOOST_AUTO_TEST_CASE(byte_array_pop_storage_empty)
{
char const* sourceCode = R"(
contract c {
bytes data;
function test() public {
data.push(0x07);
data.push(0x05);
data.push(0x03);
data.pop();
data.pop();
data.pop();
}
}
)";
ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode);
ABI_CHECK(callContractFunction("test()"), encodeArgs());
BOOST_CHECK(storageEmpty(m_contractAddress));
);
}
BOOST_AUTO_TEST_CASE(byte_array_pop_long_storage_empty)
{
char const* sourceCode = R"(
contract c {
uint256 a;
uint256 b;
uint256 c;
bytes data;
function test() public returns (bool) {
for (uint8 i = 0; i <= 40; i++)
data.push(byte(i+1));
for (int8 j = 40; j >= 0; j--) {
require(data[uint8(j)] == byte(j+1));
require(data.length == uint8(j+1));
data.pop();
}
return true;
}
}
)";
ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode);
ABI_CHECK(callContractFunction("test()"), encodeArgs(true));
BOOST_CHECK(storageEmpty(m_contractAddress));
);
}
BOOST_AUTO_TEST_CASE(byte_array_pop_long_storage_empty_garbage_ref)
{
char const* sourceCode = R"(
contract c {
uint256 a;
uint256 b;
bytes data;
function test() public {
for (uint8 i = 0; i <= 40; i++)
data.push(0x03);
for (uint8 j = 0; j <= 40; j++) {
assembly {
mstore(0, "garbage")
}
data.pop();
}
}
}
)";
compileAndRun(sourceCode);
ABI_CHECK(callContractFunction("test()"), encodeArgs());
BOOST_CHECK(storageEmpty(m_contractAddress));
}
BOOST_AUTO_TEST_CASE(external_array_args)
{
char const* sourceCode = R"(
contract c {
function test(uint[8] calldata a, uint[] calldata b, uint[5] calldata c, uint a_index, uint b_index, uint c_index)
external returns (uint av, uint bv, uint cv) {
av = a[a_index];
bv = b[b_index];
cv = c[c_index];
}
}
)";
compileAndRun(sourceCode);
bytes params = encodeArgs(
1, 2, 3, 4, 5, 6, 7, 8, // a
32 * (8 + 1 + 5 + 1 + 1 + 1), // offset to b
21, 22, 23, 24, 25, // c
0, 1, 2, // (a,b,c)_index
3, // b.length
11, 12, 13 // b
);
ABI_CHECK(callContractFunction("test(uint256[8],uint256[],uint256[5],uint256,uint256,uint256)", params), encodeArgs(1, 12, 23));
}
BOOST_AUTO_TEST_CASE(bytes_index_access)
{
char const* sourceCode = R"(
contract c {
bytes data;
function direct(bytes calldata arg, uint index) external returns (uint) {
return uint(uint8(arg[index]));
}
function storageCopyRead(bytes calldata arg, uint index) external returns (uint) {
data = arg;
return uint(uint8(data[index]));
}
function storageWrite() external returns (uint) {
data = new bytes(35);
data[31] = 0x77;
data[32] = 0x14;
data[31] = 0x01;
data[31] |= 0x08;
data[30] = 0x01;
data[32] = 0x03;
return uint(uint8(data[30])) * 0x100 | uint(uint8(data[31])) * 0x10 | uint(uint8(data[32]));
}
}
)";
string array{
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
30, 31, 32, 33};
ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode);
ABI_CHECK(callContractFunction("direct(bytes,uint256)", 64, 33, u256(array.length()), array), encodeArgs(33));
ABI_CHECK(callContractFunction("storageCopyRead(bytes,uint256)", 64, 33, u256(array.length()), array), encodeArgs(33));
ABI_CHECK(callContractFunction("storageWrite()"), encodeArgs(0x193));
);
}
BOOST_AUTO_TEST_CASE(array_copy_calldata_storage)
{
char const* sourceCode = R"(
contract c {
uint[9] m_data;
uint[] m_data_dyn;
uint8[][] m_byte_data;
function store(uint[9] calldata a, uint8[3][] calldata b) external returns (uint8) {
m_data = a;
m_data_dyn = a;
m_byte_data = b;
return b[3][1]; // note that access and declaration are reversed to each other
}
function retrieve() public returns (uint a, uint b, uint c, uint d, uint e, uint f, uint g) {
a = m_data.length;
b = m_data[7];
c = m_data_dyn.length;
d = m_data_dyn[7];
e = m_byte_data.length;
f = m_byte_data[3].length;
g = m_byte_data[3][1];
}
}
)";
compileAndRun(sourceCode);
ABI_CHECK(callContractFunction("store(uint256[9],uint8[3][])", encodeArgs(21, 22, 23, 24, 25, 26, 27, 28, 29, u256(32 * (9 + 1)), 4, 1, 2, 3, 11, 12, 13, 21, 22, 23, 31, 32, 33 )), encodeArgs(32));
ABI_CHECK(callContractFunction("retrieve()"), encodeArgs(9, 28, 9, 28, 4, 3, 32));
}
BOOST_AUTO_TEST_CASE(array_copy_including_array)
{
char const* sourceCode = R"(
contract c {
uint[3][90][] large;
uint[3][3][] small;
function test() public returns (uint r) {
for (uint i = 0; i < 7; i++) {
large.push();
small.push();
}
large[3][2][0] = 2;
large[1] = large[3];
small[3][2][0] = 2;
small[1] = small[2];
r = ((
small[3][2][0] * 0x100 |
small[1][2][0]) * 0x100 |
large[3][2][0]) * 0x100 |
large[1][2][0];
delete small;
delete large;
}
function clear() public returns (uint, uint) {
for (uint i = 0; i < 7; i++) {
large.push();
small.push();
}
small[3][2][0] = 0;
large[3][2][0] = 0;
while (small.length > 0)
small.pop();
while (large.length > 0)
large.pop();
return (small.length, large.length);
}
}
)";
compileAndRun(sourceCode);
ABI_CHECK(callContractFunction("test()"), encodeArgs(0x02000202));
BOOST_CHECK(storageEmpty(m_contractAddress));
ABI_CHECK(callContractFunction("clear()"), encodeArgs(0, 0));
BOOST_CHECK(storageEmpty(m_contractAddress));
}
//BOOST_AUTO_TEST_CASE(assignment_to_const_array_vars)
//{
// char const* sourceCode = R"(
@ -3684,159 +3072,6 @@ BOOST_AUTO_TEST_CASE(return_bytes_internal)
}
}
BOOST_AUTO_TEST_CASE(bytes_index_access_memory)
{
char const* sourceCode = R"(
contract Main {
function f(bytes memory _s1, uint i1, uint i2, uint i3) public returns (byte c1, byte c2, byte c3) {
c1 = _s1[i1];
c2 = intern(_s1, i2);
c3 = internIndirect(_s1)[i3];
}
function intern(bytes memory _s1, uint i) public returns (byte c) {
return _s1[i];
}
function internIndirect(bytes memory _s1) public returns (bytes memory) {
return _s1;
}
}
)";
compileAndRun(sourceCode, 0, "Main");
string s1("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz");
bytes dyn1 = encodeArgs(u256(s1.length()), s1);
bytes args1 = encodeArgs(u256(0x80), u256(3), u256(4), u256(5)) + dyn1;
BOOST_REQUIRE(
callContractFunction("f(bytes,uint256,uint256,uint256)", asString(args1)) ==
encodeArgs(string{s1[3]}, string{s1[4]}, string{s1[5]})
);
}
BOOST_AUTO_TEST_CASE(bytes_in_constructors_unpacker)
{
char const* sourceCode = R"(
contract Test {
uint public m_x;
bytes public m_s;
constructor(uint x, bytes memory s) {
m_x = x;
m_s = s;
}
}
)";
string s1("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz");
bytes dyn1 = encodeArgs(u256(s1.length()), s1);
u256 x = 7;
bytes args1 = encodeArgs(x, u256(0x40)) + dyn1;
compileAndRun(sourceCode, 0, "Test", args1);
BOOST_REQUIRE(callContractFunction("m_x()") == encodeArgs(x));
BOOST_REQUIRE(callContractFunction("m_s()") == encodeArgs(u256(0x20)) + dyn1);
}
BOOST_AUTO_TEST_CASE(bytes_in_constructors_packer)
{
char const* sourceCode = R"(
contract Base {
uint public m_x;
bytes m_s;
constructor(uint x, bytes memory s) {
m_x = x;
m_s = s;
}
function part(uint i) public returns (byte) {
return m_s[i];
}
}
contract Main is Base {
constructor(bytes memory s, uint x) Base(x, f(s)) {}
function f(bytes memory s) public returns (bytes memory) {
return s;
}
}
contract Creator {
function f(uint x, bytes memory s) public returns (uint r, byte ch) {
Main c = new Main(s, x);
r = c.m_x();
ch = c.part(x);
}
}
)";
compileAndRun(sourceCode, 0, "Creator");
string s1("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz");
bytes dyn1 = encodeArgs(u256(s1.length()), s1);
u256 x = 7;
bytes args1 = encodeArgs(x, u256(0x40)) + dyn1;
BOOST_REQUIRE(
callContractFunction("f(uint256,bytes)", asString(args1)) ==
encodeArgs(x, string{s1[unsigned(x)]})
);
}
BOOST_AUTO_TEST_CASE(arrays_in_constructors)
{
char const* sourceCode = R"(
contract Base {
uint public m_x;
address[] m_s;
constructor(uint x, address[] memory s) {
m_x = x;
m_s = s;
}
function part(uint i) public returns (address) {
return m_s[i];
}
}
contract Main is Base {
constructor(address[] memory s, uint x) Base(x, f(s)) {}
function f(address[] memory s) public returns (address[] memory) {
return s;
}
}
contract Creator {
function f(uint x, address[] memory s) public returns (uint r, address ch) {
Main c = new Main(s, x);
r = c.m_x();
ch = c.part(x);
}
}
)";
compileAndRun(sourceCode, 0, "Creator");
vector<u256> s1{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
bytes dyn1 = encodeArgs(u256(s1.size()), s1);
u256 x = 7;
bytes args1 = encodeArgs(x, u256(0x40)) + dyn1;
BOOST_REQUIRE(
callContractFunction("f(uint256,address[])", asString(args1)) ==
encodeArgs(x, s1[unsigned(x)])
);
}
BOOST_AUTO_TEST_CASE(arrays_from_and_to_storage)
{
char const* sourceCode = R"(
contract Test {
uint24[] public data;
function set(uint24[] memory _data) public returns (uint) {
data = _data;
return data.length;
}
function get() public returns (uint24[] memory) {
return data;
}
}
)";
compileAndRun(sourceCode, 0, "Test");
vector<u256> data{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18};
BOOST_REQUIRE(
callContractFunction("set(uint24[])", u256(0x20), u256(data.size()), data) ==
encodeArgs(u256(data.size()))
);
ABI_CHECK(callContractFunction("data(uint256)", u256(7)), encodeArgs(u256(8)));
ABI_CHECK(callContractFunction("data(uint256)", u256(15)), encodeArgs(u256(16)));
ABI_CHECK(callContractFunction("data(uint256)", u256(18)), encodeArgs());
ABI_CHECK(callContractFunction("get()"), encodeArgs(u256(0x20), u256(data.size()), data));
}
BOOST_AUTO_TEST_CASE(memory_types_initialisation)
{
char const* sourceCode = R"(
@ -3859,35 +3094,6 @@ BOOST_AUTO_TEST_CASE(memory_types_initialisation)
ABI_CHECK(callContractFunction("nestedStat()"), encodeArgs(vector<u256>(3 * 7)));
}
BOOST_AUTO_TEST_CASE(memory_arrays_delete)
{
char const* sourceCode = R"(
contract Test {
function del() public returns (uint24[3][4] memory) {
uint24[3][4] memory x;
for (uint24 i = 0; i < x.length; i ++)
for (uint24 j = 0; j < x[i].length; j ++)
x[i][j] = i * 0x10 + j;
delete x[1];
delete x[3][2];
return x;
}
}
)";
compileAndRun(sourceCode, 0, "Test");
vector<u256> data(3 * 4);
for (unsigned i = 0; i < 4; i++)
for (unsigned j = 0; j < 3; j++)
{
u256 v = 0;
if (!(i == 1 || (i == 3 && j == 2)))
v = i * 0x10 + j;
data[i * 3 + j] = v;
}
ABI_CHECK(callContractFunction("del()"), encodeArgs(data));
}
BOOST_AUTO_TEST_CASE(calldata_struct_short)
{
char const* sourceCode = R"(
@ -3936,84 +3142,6 @@ BOOST_AUTO_TEST_CASE(calldata_struct_function_type)
ABI_CHECK(callContractFunctionNoEncoding("f((function))", fn_C_h), encodeArgs(23));
}
BOOST_AUTO_TEST_CASE(calldata_bytes_array_bounds)
{
char const* sourceCode = R"(
pragma experimental ABIEncoderV2;
contract C {
function f(bytes[] calldata a, uint256 i) external returns (uint) {
return uint8(a[0][i]);
}
}
)";
compileAndRun(sourceCode, 0, "C");
ABI_CHECK(
callContractFunction("f(bytes[],uint256)", 0x40, 0, 1, 0x20, 2, bytes{'a', 'b'} + bytes(30, 0)),
encodeArgs('a')
);
ABI_CHECK(
callContractFunction("f(bytes[],uint256)", 0x40, 1, 1, 0x20, 2, bytes{'a', 'b'} + bytes(30, 0)),
encodeArgs('b')
);
ABI_CHECK(
callContractFunction("f(bytes[],uint256)", 0x40, 2, 1, 0x20, 2, bytes{'a', 'b'} + bytes(30, 0)),
panicData(PanicCode::ArrayOutOfBounds)
);
}
BOOST_AUTO_TEST_CASE(calldata_array_two_dimensional)
{
vector<vector<u256>> data {
{ 0x0A01, 0x0A02, 0x0A03 },
{ 0x0B01, 0x0B02, 0x0B03, 0x0B04 }
};
for (bool outerDynamicallySized: { true, false })
{
string arrayType = outerDynamicallySized ? "uint256[][]" : "uint256[][2]";
string sourceCode = R"(
pragma experimental ABIEncoderV2;
contract C {
function test()" + arrayType + R"( calldata a) external returns (uint256) {
return a.length;
}
function test()" + arrayType + R"( calldata a, uint256 i) external returns (uint256) {
return a[i].length;
}
function test()" + arrayType + R"( calldata a, uint256 i, uint256 j) external returns (uint256) {
return a[i][j];
}
function reenc()" + arrayType + R"( calldata a, uint256 i, uint256 j) external returns (uint256) {
return this.test(a, i, j);
}
}
)";
compileAndRun(sourceCode, 0, "C");
bytes encoding = encodeArray(
outerDynamicallySized,
true,
data | boost::adaptors::transformed([&](vector<u256> const& _values) {
return encodeArray(true, false, _values);
})
);
ABI_CHECK(callContractFunction("test(" + arrayType + ")", 0x20, encoding), encodeArgs(data.size()));
for (size_t i = 0; i < data.size(); i++)
{
ABI_CHECK(callContractFunction("test(" + arrayType + ",uint256)", 0x40, i, encoding), encodeArgs(data[i].size()));
for (size_t j = 0; j < data[i].size(); j++)
{
ABI_CHECK(callContractFunction("test(" + arrayType + ",uint256,uint256)", 0x60, i, j, encoding), encodeArgs(data[i][j]));
ABI_CHECK(callContractFunction("reenc(" + arrayType + ",uint256,uint256)", 0x60, i, j, encoding), encodeArgs(data[i][j]));
}
ABI_CHECK(callContractFunction("test(" + arrayType + ",uint256,uint256)", 0x60, i, data[i].size(), encoding), panicData(PanicCode::ArrayOutOfBounds));
}
ABI_CHECK(callContractFunction("test(" + arrayType + ",uint256)", 0x40, data.size(), encoding), panicData(PanicCode::ArrayOutOfBounds));
}
}
BOOST_AUTO_TEST_CASE(calldata_array_dynamic_three_dimensional)
{
vector<vector<vector<u256>>> data {
@ -6591,59 +5719,6 @@ BOOST_AUTO_TEST_CASE(dirty_scratch_space_prior_to_constant_optimiser)
);
}
BOOST_AUTO_TEST_CASE(try_catch_library_call)
{
char const* sourceCode = R"(
library L {
struct S { uint x; }
function integer(uint t, bool b) public view returns (uint) {
if (b) {
return t;
} else {
revert("failure");
}
}
function stru(S storage t, bool b) public view returns (uint) {
if (b) {
return t.x;
} else {
revert("failure");
}
}
}
contract C {
using L for L.S;
L.S t;
function f(bool b) public returns (uint, string memory) {
uint x = 8;
try L.integer(x, b) returns (uint _x) {
return (_x, "");
} catch Error(string memory message) {
return (18, message);
}
}
function g(bool b) public returns (uint, string memory) {
t.x = 9;
try t.stru(b) returns (uint x) {
return (x, "");
} catch Error(string memory message) {
return (19, message);
}
}
}
)";
if (solidity::test::CommonOptions::get().evmVersion().supportsReturndata())
{
compileAndRun(sourceCode, 0, "L", bytes());
compileAndRun(sourceCode, 0, "C", bytes(), map<string, Address>{{"L", m_contractAddress}});
ABI_CHECK(callContractFunction("f(bool)", true), encodeArgs(8, 0x40, 0));
ABI_CHECK(callContractFunction("f(bool)", false), encodeArgs(18, 0x40, 7, "failure"));
ABI_CHECK(callContractFunction("g(bool)", true), encodeArgs(9, 0x40, 0));
ABI_CHECK(callContractFunction("g(bool)", false), encodeArgs(19, 0x40, 7, "failure"));
}
}
BOOST_AUTO_TEST_CASE(strip_reason_strings)
{
char const* sourceCode = R"(

View File

@ -1748,6 +1748,7 @@ BOOST_AUTO_TEST_CASE(user_explicit_inherit_partial2)
checkNatspec(sourceCode, "ERC20", natspec, true);
checkNatspec(sourceCode, "Token", natspec2, true);
}
BOOST_AUTO_TEST_CASE(dev_explicit_inherit_partial)
{
char const *sourceCode = R"(
@ -2022,6 +2023,230 @@ BOOST_AUTO_TEST_CASE(dev_explicit_inehrit_complex)
);
}
BOOST_AUTO_TEST_CASE(dev_different_return_name)
{
char const *sourceCode = R"(
contract A {
/// @return y value
function g(int x) public pure virtual returns (int y) { return x; }
}
contract B is A {
function g(int x) public pure override returns (int z) { return x; }
}
)";
char const *natspec = R"ABCDEF({
"methods":
{
"g(int256)":
{
"returns":
{
"y": "value"
}
}
}
})ABCDEF";
char const *natspec2 = R"ABCDEF({
"methods":
{
"g(int256)":
{
"returns":
{
"z": "value"
}
}
}
})ABCDEF";
checkNatspec(sourceCode, "A", natspec, false);
checkNatspec(sourceCode, "B", natspec2, false);
}
BOOST_AUTO_TEST_CASE(dev_different_return_name_multiple)
{
char const *sourceCode = R"(
contract A {
/// @return a value A
/// @return b value B
function g(int x) public pure virtual returns (int a, int b) { return (1, 2); }
}
contract B is A {
function g(int x) public pure override returns (int z, int y) { return (1, 2); }
}
)";
char const *natspec = R"ABCDEF({
"methods":
{
"g(int256)":
{
"returns":
{
"a": "value A",
"b": "value B"
}
}
}
})ABCDEF";
char const *natspec2 = R"ABCDEF({
"methods":
{
"g(int256)":
{
"returns":
{
"z": "value A",
"y": "value B"
}
}
}
})ABCDEF";
checkNatspec(sourceCode, "A", natspec, false);
checkNatspec(sourceCode, "B", natspec2, false);
}
BOOST_AUTO_TEST_CASE(dev_different_return_name_multiple_partly_unnamed)
{
char const *sourceCode = R"(
contract A {
/// @return value A
/// @return b value B
function g(int x) public pure virtual returns (int, int b) { return (1, 2); }
}
contract B is A {
function g(int x) public pure override returns (int z, int) { return (1, 2); }
}
)";
char const *natspec = R"ABCDEF({
"methods":
{
"g(int256)":
{
"returns":
{
"_0": "value A",
"b": "value B"
}
}
}
})ABCDEF";
char const *natspec2 = R"ABCDEF({
"methods":
{
"g(int256)":
{
"returns":
{
"z": "value A",
"_1": "value B"
}
}
}
})ABCDEF";
checkNatspec(sourceCode, "A", natspec, false);
checkNatspec(sourceCode, "B", natspec2, false);
}
BOOST_AUTO_TEST_CASE(dev_different_return_name_multiple_unnamed)
{
char const *sourceCode = R"(
contract A {
/// @return value A
/// @return value B
function g(int x) public pure virtual returns (int, int) { return (1, 2); }
}
contract B is A {
function g(int x) public pure override returns (int z, int y) { return (1, 2); }
}
)";
char const *natspec = R"ABCDEF({
"methods":
{
"g(int256)":
{
"returns":
{
"_0": "value A",
"_1": "value B"
}
}
}
})ABCDEF";
char const *natspec2 = R"ABCDEF({
"methods":
{
"g(int256)":
{
"returns":
{
"z": "value A",
"y": "value B"
}
}
}
})ABCDEF";
checkNatspec(sourceCode, "A", natspec, false);
checkNatspec(sourceCode, "B", natspec2, false);
}
BOOST_AUTO_TEST_CASE(dev_return_name_no_description)
{
char const *sourceCode = R"(
contract A {
/// @return a
function g(int x) public pure virtual returns (int a) { return 2; }
}
contract B is A {
function g(int x) public pure override returns (int b) { return 2; }
}
)";
char const *natspec = R"ABCDEF({
"methods":
{
"g(int256)":
{
"returns":
{
"a": "a",
}
}
}
})ABCDEF";
char const *natspec2 = R"ABCDEF({
"methods":
{
"g(int256)":
{
"returns":
{
"b": "a",
}
}
}
})ABCDEF";
checkNatspec(sourceCode, "A", natspec, false);
checkNatspec(sourceCode, "B", natspec2, false);
}
}
BOOST_AUTO_TEST_SUITE_END()

View File

@ -86,6 +86,48 @@ Json::Value getContractResult(Json::Value const& _compilerResult, string const&
return _compilerResult["contracts"][_file][_name];
}
void checkLinkReferencesSchema(Json::Value const& _contractResult)
{
BOOST_TEST_REQUIRE(_contractResult.isObject());
BOOST_TEST_REQUIRE(_contractResult["evm"]["bytecode"].isObject());
Json::Value const& linkReferenceResult = _contractResult["evm"]["bytecode"]["linkReferences"];
BOOST_TEST_REQUIRE(linkReferenceResult.isObject());
for (string const& fileName: linkReferenceResult.getMemberNames())
{
BOOST_TEST_REQUIRE(linkReferenceResult[fileName].isObject());
for (string const& libraryName: linkReferenceResult[fileName].getMemberNames())
{
BOOST_TEST_REQUIRE(linkReferenceResult[fileName][libraryName].isArray());
BOOST_TEST_REQUIRE(!linkReferenceResult[fileName][libraryName].empty());
for (int i = 0; i < static_cast<int>(linkReferenceResult.size()); ++i)
{
BOOST_TEST_REQUIRE(linkReferenceResult[fileName][libraryName][i].isObject());
BOOST_TEST_REQUIRE(linkReferenceResult[fileName][libraryName][i].size() == 2);
BOOST_TEST_REQUIRE(linkReferenceResult[fileName][libraryName][i]["length"].isUInt());
BOOST_TEST_REQUIRE(linkReferenceResult[fileName][libraryName][i]["start"].isUInt());
}
}
}
}
void expectLinkReferences(Json::Value const& _contractResult, map<string, set<string>> const& _expectedLinkReferences)
{
checkLinkReferencesSchema(_contractResult);
Json::Value const& linkReferenceResult = _contractResult["evm"]["bytecode"]["linkReferences"];
BOOST_TEST(linkReferenceResult.size() == _expectedLinkReferences.size());
for (auto const& [fileName, libraries]: _expectedLinkReferences)
{
BOOST_TEST(linkReferenceResult.isMember(fileName));
BOOST_TEST(linkReferenceResult[fileName].size() == libraries.size());
for (string const& libraryName: libraries)
BOOST_TEST(linkReferenceResult[fileName].isMember(libraryName));
}
}
Json::Value compile(string _input)
{
StandardCompiler compiler;
@ -710,11 +752,7 @@ BOOST_AUTO_TEST_CASE(library_filename_with_colon)
BOOST_CHECK(containsAtMostWarnings(result));
Json::Value contract = getContractResult(result, "fileA", "A");
BOOST_CHECK(contract.isObject());
BOOST_CHECK(contract["evm"]["bytecode"].isObject());
BOOST_CHECK(contract["evm"]["bytecode"]["linkReferences"].isObject());
BOOST_CHECK(contract["evm"]["bytecode"]["linkReferences"]["git:library.sol"].isObject());
BOOST_CHECK(contract["evm"]["bytecode"]["linkReferences"]["git:library.sol"]["L"].isArray());
BOOST_CHECK(contract["evm"]["bytecode"]["linkReferences"]["git:library.sol"]["L"][0].isObject());
expectLinkReferences(contract, {{"git:library.sol", {"L"}}});
}
BOOST_AUTO_TEST_CASE(libraries_invalid_top_level)
@ -860,15 +898,137 @@ BOOST_AUTO_TEST_CASE(library_linking)
}
)";
Json::Value result = compile(input);
BOOST_CHECK(containsAtMostWarnings(result));
Json::Value contract = getContractResult(result, "fileA", "A");
BOOST_CHECK(contract.isObject());
BOOST_CHECK(contract["evm"]["bytecode"].isObject());
BOOST_CHECK(contract["evm"]["bytecode"]["linkReferences"].isObject());
BOOST_CHECK(!contract["evm"]["bytecode"]["linkReferences"]["library.sol"].isObject());
BOOST_CHECK(contract["evm"]["bytecode"]["linkReferences"]["library2.sol"].isObject());
BOOST_CHECK(contract["evm"]["bytecode"]["linkReferences"]["library2.sol"]["L2"].isArray());
BOOST_CHECK(contract["evm"]["bytecode"]["linkReferences"]["library2.sol"]["L2"][0].isObject());
BOOST_TEST(containsAtMostWarnings(result));
Json::Value contractResult = getContractResult(result, "fileA", "A");
expectLinkReferences(contractResult, {{"library2.sol", {"L2"}}});
}
BOOST_AUTO_TEST_CASE(linking_yul)
{
char const* input = R"(
{
"language": "Yul",
"settings": {
"libraries": {
"fileB": {
"L": "0x4200000000000000000000000000000000000001"
}
},
"outputSelection": {
"fileA": {
"*": [
"evm.bytecode.linkReferences"
]
}
}
},
"sources": {
"fileA": {
"content": "object \"a\" { code { let addr := linkersymbol(\"fileB:L\") } }"
}
}
}
)";
Json::Value result = compile(input);
BOOST_TEST(containsAtMostWarnings(result));
Json::Value contractResult = getContractResult(result, "fileA", "a");
expectLinkReferences(contractResult, {});
}
BOOST_AUTO_TEST_CASE(linking_yul_empty_link_reference)
{
char const* input = R"(
{
"language": "Yul",
"settings": {
"libraries": {
"": {
"": "0x4200000000000000000000000000000000000001"
}
},
"outputSelection": {
"fileA": {
"*": [
"evm.bytecode.linkReferences"
]
}
}
},
"sources": {
"fileA": {
"content": "object \"a\" { code { let addr := linkersymbol(\"\") } }"
}
}
}
)";
Json::Value result = compile(input);
BOOST_TEST(containsAtMostWarnings(result));
Json::Value contractResult = getContractResult(result, "fileA", "a");
expectLinkReferences(contractResult, {{"", {""}}});
}
BOOST_AUTO_TEST_CASE(linking_yul_no_filename_in_link_reference)
{
char const* input = R"(
{
"language": "Yul",
"settings": {
"libraries": {
"": {
"L": "0x4200000000000000000000000000000000000001"
}
},
"outputSelection": {
"fileA": {
"*": [
"evm.bytecode.linkReferences"
]
}
}
},
"sources": {
"fileA": {
"content": "object \"a\" { code { let addr := linkersymbol(\"L\") } }"
}
}
}
)";
Json::Value result = compile(input);
BOOST_TEST(containsAtMostWarnings(result));
Json::Value contractResult = getContractResult(result, "fileA", "a");
expectLinkReferences(contractResult, {{"", {"L"}}});
}
BOOST_AUTO_TEST_CASE(linking_yul_same_library_name_different_files)
{
char const* input = R"(
{
"language": "Yul",
"settings": {
"libraries": {
"fileB": {
"L": "0x4200000000000000000000000000000000000001"
}
},
"outputSelection": {
"fileA": {
"*": [
"evm.bytecode.linkReferences"
]
}
}
},
"sources": {
"fileA": {
"content": "object \"a\" { code { let addr := linkersymbol(\"fileC:L\") } }"
}
}
}
)";
Json::Value result = compile(input);
BOOST_TEST(containsAtMostWarnings(result));
Json::Value contractResult = getContractResult(result, "fileA", "a");
expectLinkReferences(contractResult, {{"fileC", {"L"}}});
}
BOOST_AUTO_TEST_CASE(evm_version)

View File

@ -0,0 +1,38 @@
pragma experimental ABIEncoderV2;
contract C {
function test(uint256[][2] calldata a) external returns (uint256) {
return a.length;
}
function test(uint256[][2] calldata a, uint256 i) external returns (uint256) {
return a[i].length;
}
function test(uint256[][2] calldata a, uint256 i, uint256 j) external returns (uint256) {
return a[i][j];
}
function reenc(uint256[][2] calldata a, uint256 i, uint256 j) external returns (uint256) {
return this.test(a, i, j);
}
}
// ====
// compileViaYul: also
// ----
// test(uint256[][2]): 0x20, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 2
// test(uint256[][2],uint256): 0x40, 0, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 3
// test(uint256[][2],uint256): 0x40, 1, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 4
// test(uint256[][2],uint256,uint256): 0x60, 0, 0, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0A01
// reenc(uint256[][2],uint256,uint256): 0x60, 0, 0, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0A01
// test(uint256[][2],uint256,uint256): 0x60, 0, 1, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0A02
// reenc(uint256[][2],uint256,uint256): 0x60, 0, 1, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0A02
// test(uint256[][2],uint256,uint256): 0x60, 0, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0A03
// reenc(uint256[][2],uint256,uint256): 0x60, 0, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0A03
// test(uint256[][2],uint256,uint256): 0x60, 1, 0, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0B01
// reenc(uint256[][2],uint256,uint256): 0x60, 1, 0, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0B01
// test(uint256[][2],uint256,uint256): 0x60, 1, 1, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0B02
// reenc(uint256[][2],uint256,uint256): 0x60, 1, 1, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0B02
// test(uint256[][2],uint256,uint256): 0x60, 1, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0B03
// reenc(uint256[][2],uint256,uint256): 0x60, 1, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0B03
// test(uint256[][2],uint256,uint256): 0x60, 1, 3, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0B04
// reenc(uint256[][2],uint256,uint256): 0x60, 1, 3, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0B04
// test(uint256[][2],uint256,uint256): 0x60, 0, 3, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> FAILURE
// test(uint256[][2],uint256,uint256): 0x60, 1, 4, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> FAILURE
// test(uint256[][2],uint256): 0x40, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> FAILURE

View File

@ -0,0 +1,38 @@
pragma experimental ABIEncoderV2;
contract C {
function test(uint256[][] calldata a) external returns (uint256) {
return a.length;
}
function test(uint256[][] calldata a, uint256 i) external returns (uint256) {
return a[i].length;
}
function test(uint256[][] calldata a, uint256 i, uint256 j) external returns (uint256) {
return a[i][j];
}
function reenc(uint256[][] calldata a, uint256 i, uint256 j) external returns (uint256) {
return this.test(a, i, j);
}
}
// ====
// compileViaYul: also
// ----
// test(uint256[][]): 0x20, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 2
// test(uint256[][],uint256): 0x40, 0, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 3
// test(uint256[][],uint256): 0x40, 1, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 4
// test(uint256[][],uint256,uint256): 0x60, 0, 0, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0A01
// reenc(uint256[][],uint256,uint256): 0x60, 0, 0, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0A01
// test(uint256[][],uint256,uint256): 0x60, 0, 1, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0A02
// reenc(uint256[][],uint256,uint256): 0x60, 0, 1, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0A02
// test(uint256[][],uint256,uint256): 0x60, 0, 2, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0A03
// reenc(uint256[][],uint256,uint256): 0x60, 0, 2, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0A03
// test(uint256[][],uint256,uint256): 0x60, 1, 0, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0B01
// reenc(uint256[][],uint256,uint256): 0x60, 1, 0, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0B01
// test(uint256[][],uint256,uint256): 0x60, 1, 1, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0B02
// reenc(uint256[][],uint256,uint256): 0x60, 1, 1, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0B02
// test(uint256[][],uint256,uint256): 0x60, 1, 2, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0B03
// reenc(uint256[][],uint256,uint256): 0x60, 1, 2, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0B03
// test(uint256[][],uint256,uint256): 0x60, 1, 3, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0B04
// reenc(uint256[][],uint256,uint256): 0x60, 1, 3, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0B04
// test(uint256[][],uint256,uint256): 0x60, 0, 3, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> FAILURE
// test(uint256[][],uint256,uint256): 0x60, 1, 4, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> FAILURE
// test(uint256[][],uint256): 0x40, 2, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> FAILURE

View File

@ -0,0 +1,12 @@
pragma experimental ABIEncoderV2;
contract C {
function f(bytes[] calldata a, uint256 i) external returns (uint) {
return uint8(a[0][i]);
}
}
// ====
// compileViaYul: also
// ----
// f(bytes[],uint256): 0x40, 0, 1, 0x20, 2, 0x6162000000000000000000000000000000000000000000000000000000000000 -> 0x61
// f(bytes[],uint256): 0x40, 1, 1, 0x20, 2, 0x6162000000000000000000000000000000000000000000000000000000000000 -> 0x62
// f(bytes[],uint256): 0x40, 2, 1, 0x20, 2, 0x6162000000000000000000000000000000000000000000000000000000000000 -> FAILURE

View File

@ -0,0 +1,25 @@
contract c {
uint[9] m_data;
uint[] m_data_dyn;
uint8[][] m_byte_data;
function store(uint[9] calldata a, uint8[3][] calldata b) external returns (uint8) {
m_data = a;
m_data_dyn = a;
m_byte_data = b;
return b[3][1]; // note that access and declaration are reversed to each other
}
function retrieve() public returns (uint a, uint b, uint c, uint d, uint e, uint f, uint g) {
a = m_data.length;
b = m_data[7];
c = m_data_dyn.length;
d = m_data_dyn[7];
e = m_byte_data.length;
f = m_byte_data[3].length;
g = m_byte_data[3][1];
}
}
// ====
// compileViaYul: also
// ----
// store(uint256[9],uint8[3][]): 21, 22, 23, 24, 25, 26, 27, 28, 29, 0x140, 4, 1, 2, 3, 11, 12, 13, 21, 22, 23, 31, 32, 33 -> 32
// retrieve() -> 9, 28, 9, 28, 4, 3, 32

View File

@ -0,0 +1,42 @@
contract c {
uint[3][90][] large;
uint[3][3][] small;
function test() public returns (uint r) {
for (uint i = 0; i < 7; i++) {
large.push();
small.push();
}
large[3][2][0] = 2;
large[1] = large[3];
small[3][2][0] = 2;
small[1] = small[2];
r = ((
small[3][2][0] * 0x100 |
small[1][2][0]) * 0x100 |
large[3][2][0]) * 0x100 |
large[1][2][0];
delete small;
delete large;
}
function clear() public returns (uint, uint) {
for (uint i = 0; i < 7; i++) {
large.push();
small.push();
}
small[3][2][0] = 0;
large[3][2][0] = 0;
while (small.length > 0)
small.pop();
while (large.length > 0)
large.pop();
return (small.length, large.length);
}
}
// ====
// compileViaYul: also
// ----
// test() -> 0x02000202
// storage: empty
// clear() -> 0, 0
// storage: empty

View File

@ -11,5 +11,7 @@ contract c {
}
}
// ====
// compileViaYul: also
// ----
// test(uint256[2][]): 32, 3, 7, 8, 9, 10, 11, 12 -> 10

View File

@ -0,0 +1,24 @@
contract c {
uint[] data1;
uint[] data2;
function setData1(uint length, uint index, uint value) public {
data1 = new uint[](length);
if (index < length)
data1[index] = value;
}
function copyStorageStorage() public { data2 = data1; }
function getData2(uint index) public returns (uint len, uint val) {
len = data2.length; if (index < len) val = data2[index];
}
}
// ====
// compileViaYul: also
// ----
// setData1(uint256,uint256,uint256): 10, 5, 4 ->
// copyStorageStorage() ->
// getData2(uint256): 5 -> 10, 4
// setData1(uint256,uint256,uint256): 0, 0, 0 ->
// copyStorageStorage() ->
// getData2(uint256): 0 -> 0, 0
// storage: empty

View File

@ -10,5 +10,7 @@ contract c {
}
}
// ====
// compileViaYul: also
// ----
// test() -> 9, 4

View File

@ -0,0 +1,22 @@
contract c {
struct Data { uint x; uint y; }
Data[] data1;
Data[] data2;
function test() public returns (uint x, uint y) {
while (data1.length < 9)
data1.push();
data1[8].x = 4;
data1[8].y = 5;
data2 = data1;
x = data2[8].x;
y = data2[8].y;
while (data1.length > 0)
data1.pop();
data2 = data1;
}
}
// ====
// compileViaYul: also
// ----
// test() -> 4, 5
// storage: empty

View File

@ -0,0 +1,19 @@
contract c {
byte[10] data1;
bytes2[32] data2;
function test() public returns (uint check, uint res1, uint res2) {
uint i;
for (i = 0; i < data2.length; ++i)
data2[i] = 0xffff;
check = uint(uint16(data2[31])) * 0x10000 | uint(uint16(data2[14]));
for (i = 0; i < data1.length; ++i)
data1[i] = byte(uint8(1 + i));
data2 = data1;
for (i = 0; i < 16; ++i)
res1 |= uint(uint16(data2[i])) * 0x10000**i;
for (i = 0; i < 16; ++i)
res2 |= uint(uint16(data2[16 + i])) * 0x10000**i;
}
}
// ----
// test() -> 0xffffffff, 0x0000000000000000000000000a00090008000700060005000400030002000100, 0x0000000000000000000000000000000000000000000000000000000000000000

View File

@ -0,0 +1,18 @@
contract Test {
uint24[] public data;
function set(uint24[] memory _data) public returns (uint) {
data = _data;
return data.length;
}
function get() public returns (uint24[] memory) {
return data;
}
}
// ====
// compileViaYul: also
// ----
// set(uint24[]): 0x20, 18, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18 -> 18
// data(uint256): 7 -> 8
// data(uint256): 15 -> 16
// data(uint256): 18 -> FAILURE
// get() -> 0x20, 18, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18

View File

@ -0,0 +1,17 @@
contract c {
function set(uint key) public returns (bool) { data[key] = msg.data; return true; }
function copy(uint from, uint to) public returns (bool) { data[to] = data[from]; return true; }
mapping(uint => bytes) data;
}
// ====
// compileViaYul: also
// ----
// set(uint256): 1, 2 -> true
// set(uint256): 2, 2, 3, 4, 5 -> true
// storage: nonempty
// copy(uint256,uint256): 1, 2 -> true
// storage: nonempty
// copy(uint256,uint256): 99, 1 -> true
// storage: nonempty
// copy(uint256,uint256): 99, 2 -> true
// storage: empty

View File

@ -14,5 +14,7 @@ contract C {
}
}
// ====
// compileViaYul: also
// ----
// test() -> 7

View File

@ -0,0 +1,14 @@
contract c {
function set() public returns (bool) { data1 = msg.data; return true; }
function reset() public returns (bool) { data1 = data2; return true; }
bytes data1;
bytes data2;
}
// ====
// compileViaYul: also
// ----
// set(): 1, 2, 3, 4, 5 -> true
// storage: nonempty
// reset() -> true
// storage: empty

View File

@ -0,0 +1,12 @@
contract c {
fallback() external { data = msg.data; }
function del() public returns (bool) { delete data; return true; }
bytes data;
}
// ====
// compileViaYul: also
// ----
// (): 7 ->
// storage: nonempty
// del(): 7 -> true
// storage: empty

View File

@ -0,0 +1,15 @@
contract Test {
function del() public returns (uint24[3][4] memory) {
uint24[3][4] memory x;
for (uint24 i = 0; i < x.length; i ++)
for (uint24 j = 0; j < x[i].length; j ++)
x[i][j] = i * 0x10 + j;
delete x[1];
delete x[3][2];
return x;
}
}
// ====
// compileViaYul: also
// ----
// del() -> 0, 1, 2, 0, 0, 0, 0x20, 0x21, 0x22, 0x30, 0x31, 0

View File

@ -0,0 +1,23 @@
contract c {
uint[20] spacer;
uint[] dynamic;
function fill() public {
for (uint i = 0; i < 21; ++i)
dynamic.push(i + 1);
}
function halfClear() public {
while (dynamic.length > 5)
dynamic.pop();
}
function fullClear() public { delete dynamic; }
}
// ====
// compileViaYul: also
// ----
// storage: empty
// fill() ->
// storage: nonempty
// halfClear() ->
// storage: nonempty
// fullClear() ->
// storage: empty

View File

@ -0,0 +1,23 @@
contract c {
struct s { uint[][] d; }
s[] data;
function fill() public returns (uint) {
while (data.length < 3)
data.push();
while (data[2].d.length < 4)
data[2].d.push();
while (data[2].d[3].length < 5)
data[2].d[3].push();
data[2].d[3][4] = 8;
return data[2].d[3][4];
}
function clear() public { delete data; }
}
// ====
// compileViaYul: also
// ----
// storage: empty
// fill() -> 8
// storage: nonempty
// clear() ->
// storage: empty

View File

@ -0,0 +1,12 @@
contract c {
function test(uint[8] calldata a, uint[] calldata b, uint[5] calldata c, uint a_index, uint b_index, uint c_index)
external returns (uint av, uint bv, uint cv) {
av = a[a_index];
bv = b[b_index];
cv = c[c_index];
}
}
// ====
// compileViaYul: 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

@ -0,0 +1,17 @@
contract c {
uint spacer1;
uint spacer2;
uint[20] data;
function fill() public {
for (uint i = 0; i < data.length; ++i) data[i] = i+1;
}
function clear() public { delete data; }
}
// ====
// compileViaYul: also
// ----
// storage: empty
// fill() ->
// storage: nonempty
// clear() ->
// storage: empty

View File

@ -0,0 +1,27 @@
contract c {
bytes data;
function direct(bytes calldata arg, uint index) external returns (uint) {
return uint(uint8(arg[index]));
}
function storageCopyRead(bytes calldata arg, uint index) external returns (uint) {
data = arg;
return uint(uint8(data[index]));
}
function storageWrite() external returns (uint) {
data = new bytes(35);
data[31] = 0x77;
data[32] = 0x14;
data[31] = 0x01;
data[31] |= 0x08;
data[30] = 0x01;
data[32] = 0x03;
return uint(uint8(data[30])) * 0x100 | uint(uint8(data[31])) * 0x10 | uint(uint8(data[32]));
}
}
// ====
// compileViaYul: also
// ----
// direct(bytes,uint256): 0x40, 33, 34, 0x000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F, left(0x2021) -> 0x21
// storageCopyRead(bytes,uint256): 0x40, 33, 34, 0x000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F, left(0x2021) -> 0x21
// storageWrite() -> 0x193

View File

@ -0,0 +1,17 @@
contract Main {
function f(bytes memory _s1, uint i1, uint i2, uint i3) public returns (byte c1, byte c2, byte c3) {
c1 = _s1[i1];
c2 = intern(_s1, i2);
c3 = internIndirect(_s1)[i3];
}
function intern(bytes memory _s1, uint i) public returns (byte c) {
return _s1[i];
}
function internIndirect(bytes memory _s1) public returns (bytes memory) {
return _s1;
}
}
// ====
// compileViaYul: also
// ----
// f(bytes,uint256,uint256,uint256): 0x80, 3, 4, 5, 78, "abcdefghijklmnopqrstuvwxyzabcdef", "ghijklmnopqrstuvwxyzabcdefghijkl", "mnopqrstuvwxyz" -> "d", "e", "f"

View File

@ -0,0 +1,28 @@
contract c {
uint256 a;
uint256 b;
uint256 c;
uint16[] inner = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
uint16[][] data;
function test() public returns (uint x, uint y, uint z) {
for (uint i = 1; i <= 48; i++)
data.push(inner);
for (uint j = 1; j <= 10; j++)
data.pop();
x = data[data.length - 1][0];
for (uint k = 1; k <= 10; k++)
data.pop();
y = data[data.length - 1][1];
for (uint l = 1; l <= 10; l++)
data.pop();
z = data[data.length - 1][2];
for (uint m = 1; m <= 18; m++)
data.pop();
delete inner;
}
}
// ====
// compileViaYul: also
// ----
// test() -> 1, 2, 3
// storage: empty

View File

@ -0,0 +1,12 @@
contract c {
uint[] data;
function test() public {
data.push(7);
data.pop();
}
}
// ====
// compileViaYul: also
// ----
// test() ->
// storage: empty

View File

@ -0,0 +1,23 @@
contract c {
uint16[] data;
function test() public returns (uint16 x, uint16 y, uint16 z) {
for (uint i = 1; i <= 48; i++)
data.push(uint16(i));
for (uint j = 1; j <= 10; j++)
data.pop();
x = data[data.length - 1];
for (uint k = 1; k <= 10; k++)
data.pop();
y = data[data.length - 1];
for (uint l = 1; l <= 10; l++)
data.pop();
z = data[data.length - 1];
for (uint m = 1; m <= 18; m++)
data.pop();
}
}
// ====
// compileViaYul: also
// ----
// test() -> 38, 28, 18
// storage: empty

View File

@ -0,0 +1,23 @@
contract c {
uint256 a;
uint256 b;
uint256 c;
uint24[] data;
function test() public returns (uint24 x, uint24 y) {
for (uint i = 1; i <= 30; i++)
data.push(uint24(i));
for (uint j = 1; j <= 10; j++)
data.pop();
x = data[data.length - 1];
for (uint k = 1; k <= 10; k++)
data.pop();
y = data[data.length - 1];
for (uint l = 1; l <= 10; l++)
data.pop();
}
}
// ====
// compileViaYul: also
// ----
// test() -> 20, 10
// storage: empty

Some files were not shown because too many files have changed in this diff Show More