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

This commit is contained in:
chriseth 2019-11-14 13:42:46 +01:00
commit 216e1749f4
85 changed files with 1406 additions and 110 deletions

View File

@ -104,17 +104,6 @@ defaults:
name: command line tests name: command line tests
command: ./test/cmdlineTests.sh command: ./test/cmdlineTests.sh
- test_ubuntu1904: &test_ubuntu1904
docker:
- image: ethereum/solidity-buildpack-deps:ubuntu1904
steps:
- checkout
- attach_workspace:
at: build
- run: *run_soltest
- store_test_results: *store_test_results
- store_artifacts: *artifacts_test_results
- test_ubuntu1904_clang: &test_ubuntu1904_clang - test_ubuntu1904_clang: &test_ubuntu1904_clang
docker: docker:
- image: ethereum/solidity-buildpack-deps:ubuntu1904-clang - image: ethereum/solidity-buildpack-deps:ubuntu1904-clang
@ -126,7 +115,7 @@ defaults:
- store_test_results: *store_test_results - store_test_results: *store_test_results
- store_artifacts: *artifacts_test_results - store_artifacts: *artifacts_test_results
- test_ubuntu1904_all: &test_ubuntu1904 - test_ubuntu1904: &test_ubuntu1904
docker: docker:
- image: ethereum/solidity-buildpack-deps:ubuntu1904 - image: ethereum/solidity-buildpack-deps:ubuntu1904
steps: steps:

View File

@ -36,23 +36,29 @@ Compiler Features:
* Allow revert strings to be stripped from the binary using the ``--revert-strings`` option or the ``settings.debug.revertStrings`` setting. * Allow revert strings to be stripped from the binary using the ``--revert-strings`` option or the ``settings.debug.revertStrings`` setting.
### 0.5.13 (unreleased) ### 0.5.13 (2019-11-14)
Language Features: Language Features:
* Allow to obtain the address of a linked library with ``address(LibraryName)``.
Compiler Features: Compiler Features:
* Code Generator: Use SELFBALANCE for ``address(this).balance`` if using Istanbul EVM * Code Generator: Use SELFBALANCE opcode for ``address(this).balance`` if using Istanbul EVM.
* EWasm: Experimental EWasm binary output via ``--ewasm`` and as documented in standard-json.
* SMTChecker: Add break/continue support to the CHC engine. * SMTChecker: Add break/continue support to the CHC engine.
* SMTChecker: Support assignments to multi-dimensional arrays and mappings. * SMTChecker: Support assignments to multi-dimensional arrays and mappings.
* SMTChecker: Support inheritance and function overriding. * SMTChecker: Support inheritance and function overriding.
* EWasm: Experimental EWasm binary output. * Standard JSON Interface: Output the storage layout of a contract when artifact ``storageLayout`` is requested.
* TypeChecker: List possible candidates when overload resolution fails.
* TypeChecker: Disallow variables of library types.
Bugfixes: Bugfixes:
* Type Checker: Disallow constructor of the same class to be used as modifier * Code Generator: Fixed a faulty assert that would wrongly trigger for array sizes exceeding unsigned integer.
* Code Generator: Fixed a faulty assert that would wrongly trigger for array sizes exceeding unsigned integer
* Type Checker: Treat magic variables as unknown identifiers in inline assembly
* SMTChecker: Fix internal error when accessing indices of fixed bytes. * SMTChecker: Fix internal error when accessing indices of fixed bytes.
* SMTChecker: Fix internal error when using function pointers as arguments.
* SMTChecker: Fix internal error when implicitly converting string literals to fixed bytes.
* Type Checker: Disallow constructor of the same class to be used as modifier.
* Type Checker: Treat magic variables as unknown identifiers in inline assembly.
### 0.5.12 (2019-10-01) ### 0.5.12 (2019-10-01)

View File

@ -754,6 +754,10 @@
"bugs": [], "bugs": [],
"released": "2019-10-01" "released": "2019-10-01"
}, },
"0.5.13": {
"bugs": [],
"released": "2019-11-14"
},
"0.5.2": { "0.5.2": {
"bugs": [ "bugs": [
"SignedArrayStorageCopy", "SignedArrayStorageCopy",

View File

@ -178,6 +178,9 @@ 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 As the compiler cannot know where the library will be
deployed at, these addresses have to be filled into the deployed at, these addresses have to be filled into the
final bytecode by a linker final bytecode by a linker

View File

@ -8,6 +8,8 @@ Miscellaneous
Layout of State Variables in Storage Layout of State Variables in Storage
************************************ ************************************
.. _storage-inplace-encoding:
Statically-sized variables (everything except mapping and dynamically-sized array types) are laid out contiguously in storage starting from position ``0``. Multiple, contiguous items that need less than 32 bytes are packed into a single storage slot if possible, according to the following rules: Statically-sized variables (everything except mapping and dynamically-sized array types) are laid out contiguously in storage starting from position ``0``. Multiple, contiguous items that need less than 32 bytes are packed into a single storage slot if possible, according to the following rules:
- The first item in a storage slot is stored lower-order aligned. - The first item in a storage slot is stored lower-order aligned.
@ -49,6 +51,8 @@ The elements of structs and arrays are stored after each other, just as if they
Mappings and Dynamic Arrays Mappings and Dynamic Arrays
=========================== ===========================
.. _storage-hashed-encoding:
Due to their unpredictable size, mapping and dynamically-sized array types use a Keccak-256 hash Due to their unpredictable size, mapping and dynamically-sized array types use a Keccak-256 hash
computation to find the starting position of the value or the array data. These starting positions are always full stack slots. computation to find the starting position of the value or the array data. These starting positions are always full stack slots.
@ -88,6 +92,267 @@ by checking if the lowest bit is set: short (not set) and long (set).
.. note:: .. note::
Handling invalidly encoded slots is currently not supported but may be added in the future. Handling invalidly encoded slots is currently not supported but may be added in the future.
JSON Output
===========
.. _storage-layout-top-level:
The storage layout of a contract can be requested via the :ref:`standard JSON interface <compiler-api>`. The output is a JSON object containing two keys,
``storage`` and ``types``. The ``storage`` object is an array where each
element has the following form:
.. code::
{
"astId": 2,
"contract": "fileA:A",
"label": "x",
"offset": 0,
"slot": "0",
"type": "t_uint256"
}
where the example above is the storage layout of ``contract A { uint x; }`` from source unit ``fileA``
and
- ``astId`` is the id of the AST node of the state variable's declaration
- ``contract`` is the name of the contract including its path as prefix
- ``label`` is the name of the state variable
- ``offset`` is the offset in bytes within the storage slot according to the encoding
- ``slot`` is the storage slot where the state variable resides or starts. This
number may be very large and therefore its JSON value is represented as a
string.
- ``type`` is an identifier used as key to the variable's type information (described in the following)
The given ``type``, in this case ``t_uint256`` represents an element in
``types``, which has the form:
.. code::
{
"encoding": "inplace",
"label": "uint256",
"numberOfBytes": "32",
}
where
- ``encoding`` how the data is encoded in storage, where the possible values are:
- ``inplace``: data is laid out contiguously in storage (see :ref:`above <storage-inplace-encoding>`).
- ``mapping``: Keccak-256 hash-based method (see :ref:`above <storage-hashed-encoding>`).
- ``dynamic_array``: Keccak-256 hash-based method (see :ref:`above <storage-hashed-encoding>`).
- ``bytes``: single slot or Keccak-256 hash-based depending on the data size (see :ref:`above <bytes-and-string>`).
- ``label`` is the canonical type name.
- ``numberOfBytes`` is the number of used bytes (as a decimal string). Note that if ``numberOfBytes > 32`` this means that more than one slot is used.
Some types have extra information besides the four above. Mappings contain
its ``key`` and ``value`` types (again referencing an entry in this mapping
of types), arrays have its ``base`` type, and structs list their ``members`` in
the same format as the top-level ``storage`` (see :ref:`above
<storage-layout-top-level>`).
.. note ::
The JSON output format of a contract's storage layout is still considered experimental
and is subject to change in non-breaking releases of Solidity.
The following example shows a contract and its storage layout, containing
value and reference types, types that are encoded packed, and nested types.
.. code::
pragma solidity >=0.4.0 <0.7.0;
contract A {
struct S {
uint128 a;
uint128 b;
uint[2] staticArray;
uint[] dynArray;
}
uint x;
uint y;
S s;
address addr;
mapping (uint => mapping (address => bool)) map;
uint[] array;
string s1;
bytes b1;
}
.. code::
"storageLayout": {
"storage": [
{
"astId": 14,
"contract": "fileA:A",
"label": "x",
"offset": 0,
"slot": "0",
"type": "t_uint256"
},
{
"astId": 16,
"contract": "fileA:A",
"label": "y",
"offset": 0,
"slot": "1",
"type": "t_uint256"
},
{
"astId": 18,
"contract": "fileA:A",
"label": "s",
"offset": 0,
"slot": "2",
"type": "t_struct(S)12_storage"
},
{
"astId": 20,
"contract": "fileA:A",
"label": "addr",
"offset": 0,
"slot": "6",
"type": "t_address"
},
{
"astId": 26,
"contract": "fileA:A",
"label": "map",
"offset": 0,
"slot": "7",
"type": "t_mapping(t_uint256,t_mapping(t_address,t_bool))"
},
{
"astId": 29,
"contract": "fileA:A",
"label": "array",
"offset": 0,
"slot": "8",
"type": "t_array(t_uint256)dyn_storage"
},
{
"astId": 31,
"contract": "fileA:A",
"label": "s1",
"offset": 0,
"slot": "9",
"type": "t_string_storage"
},
{
"astId": 33,
"contract": "fileA:A",
"label": "b1",
"offset": 0,
"slot": "10",
"type": "t_bytes_storage"
}
],
"types": {
"t_address": {
"encoding": "inplace",
"label": "address",
"numberOfBytes": "20"
},
"t_array(t_uint256)2_storage": {
"base": "t_uint256",
"encoding": "inplace",
"label": "uint256[2]",
"numberOfBytes": "64"
},
"t_array(t_uint256)dyn_storage": {
"base": "t_uint256",
"encoding": "dynamic_array",
"label": "uint256[]",
"numberOfBytes": "32"
},
"t_bool": {
"encoding": "inplace",
"label": "bool",
"numberOfBytes": "1"
},
"t_bytes_storage": {
"encoding": "bytes",
"label": "bytes",
"numberOfBytes": "32"
},
"t_mapping(t_address,t_bool)": {
"encoding": "mapping",
"key": "t_address",
"label": "mapping(address => bool)",
"numberOfBytes": "32",
"value": "t_bool"
},
"t_mapping(t_uint256,t_mapping(t_address,t_bool))": {
"encoding": "mapping",
"key": "t_uint256",
"label": "mapping(uint256 => mapping(address => bool))",
"numberOfBytes": "32",
"value": "t_mapping(t_address,t_bool)"
},
"t_string_storage": {
"encoding": "bytes",
"label": "string",
"numberOfBytes": "32"
},
"t_struct(S)12_storage": {
"encoding": "inplace",
"label": "struct A.S",
"members": [
{
"astId": 2,
"contract": "fileA:A",
"label": "a",
"offset": 0,
"slot": "0",
"type": "t_uint128"
},
{
"astId": 4,
"contract": "fileA:A",
"label": "b",
"offset": 16,
"slot": "0",
"type": "t_uint128"
},
{
"astId": 8,
"contract": "fileA:A",
"label": "staticArray",
"offset": 0,
"slot": "1",
"type": "t_array(t_uint256)2_storage"
},
{
"astId": 11,
"contract": "fileA:A",
"label": "dynArray",
"offset": 0,
"slot": "3",
"type": "t_array(t_uint256)dyn_storage"
}
],
"numberOfBytes": "128"
},
"t_uint128": {
"encoding": "inplace",
"label": "uint128",
"numberOfBytes": "16"
},
"t_uint256": {
"encoding": "inplace",
"label": "uint256",
"numberOfBytes": "32"
}
}
}
.. index: memory layout .. index: memory layout
**************** ****************

View File

@ -291,6 +291,7 @@ Input Description
// metadata - Metadata // metadata - Metadata
// ir - Yul intermediate representation of the code before optimization // ir - Yul intermediate representation of the code before optimization
// irOptimized - Intermediate representation after optimization // irOptimized - Intermediate representation after optimization
// storageLayout - Slots, offsets and types of the contract's state variables.
// evm.assembly - New assembly format // evm.assembly - New assembly format
// evm.legacyAssembly - Old-style assembly format in JSON // evm.legacyAssembly - Old-style assembly format in JSON
// evm.bytecode.object - Bytecode object // evm.bytecode.object - Bytecode object
@ -391,6 +392,8 @@ Output Description
"devdoc": {}, "devdoc": {},
// Intermediate representation (string) // Intermediate representation (string)
"ir": "", "ir": "",
// See the Storage Layout documentation.
"storageLayout": {"storage": [...], "types": {...} },
// EVM-related outputs // EVM-related outputs
"evm": { "evm": {
// Assembly (string) // Assembly (string)

View File

@ -112,6 +112,8 @@ set(sources
interface/ReadFile.h interface/ReadFile.h
interface/StandardCompiler.cpp interface/StandardCompiler.cpp
interface/StandardCompiler.h interface/StandardCompiler.h
interface/StorageLayout.cpp
interface/StorageLayout.h
interface/Version.cpp interface/Version.cpp
interface/Version.h interface/Version.h
parsing/DocStringParser.cpp parsing/DocStringParser.cpp

View File

@ -38,10 +38,12 @@ namespace solidity
NameAndTypeResolver::NameAndTypeResolver( NameAndTypeResolver::NameAndTypeResolver(
GlobalContext& _globalContext, GlobalContext& _globalContext,
langutil::EVMVersion _evmVersion,
map<ASTNode const*, shared_ptr<DeclarationContainer>>& _scopes, map<ASTNode const*, shared_ptr<DeclarationContainer>>& _scopes,
ErrorReporter& _errorReporter ErrorReporter& _errorReporter
) : ):
m_scopes(_scopes), m_scopes(_scopes),
m_evmVersion(_evmVersion),
m_errorReporter(_errorReporter), m_errorReporter(_errorReporter),
m_globalContext(_globalContext) m_globalContext(_globalContext)
{ {
@ -324,7 +326,7 @@ bool NameAndTypeResolver::resolveNamesAndTypesInternal(ASTNode& _node, bool _res
{ {
if (m_scopes.count(&_node)) if (m_scopes.count(&_node))
setScope(&_node); setScope(&_node);
return ReferencesResolver(m_errorReporter, *this, _resolveInsideCode).resolve(_node); return ReferencesResolver(m_errorReporter, *this, m_evmVersion, _resolveInsideCode).resolve(_node);
} }
} }

View File

@ -28,6 +28,8 @@
#include <libsolidity/ast/ASTAnnotations.h> #include <libsolidity/ast/ASTAnnotations.h>
#include <libsolidity/ast/ASTVisitor.h> #include <libsolidity/ast/ASTVisitor.h>
#include <liblangutil/EVMVersion.h>
#include <boost/noncopyable.hpp> #include <boost/noncopyable.hpp>
#include <list> #include <list>
@ -55,6 +57,7 @@ public:
/// are filled during the lifetime of this object. /// are filled during the lifetime of this object.
NameAndTypeResolver( NameAndTypeResolver(
GlobalContext& _globalContext, GlobalContext& _globalContext,
langutil::EVMVersion _evmVersion,
std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>>& _scopes, std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>>& _scopes,
langutil::ErrorReporter& _errorReporter langutil::ErrorReporter& _errorReporter
); );
@ -130,6 +133,7 @@ private:
/// Aliases (for example `import "x" as y;`) create multiple pointers to the same scope. /// Aliases (for example `import "x" as y;`) create multiple pointers to the same scope.
std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>>& m_scopes; std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>>& m_scopes;
langutil::EVMVersion m_evmVersion;
DeclarationContainer* m_currentScope = nullptr; DeclarationContainer* m_currentScope = nullptr;
langutil::ErrorReporter& m_errorReporter; langutil::ErrorReporter& m_errorReporter;
GlobalContext& m_globalContext; GlobalContext& m_globalContext;

View File

@ -350,7 +350,7 @@ bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly)
yul::AsmAnalyzer( yul::AsmAnalyzer(
analysisInfo, analysisInfo,
errorsIgnored, errorsIgnored,
yul::EVMDialect::strictAssemblyForEVM(EVMVersion{}), yul::EVMDialect::strictAssemblyForEVM(m_evmVersion),
resolver resolver
).analyze(_inlineAssembly.operations()); ).analyze(_inlineAssembly.operations());
return false; return false;

View File

@ -24,6 +24,7 @@
#include <libsolidity/ast/ASTVisitor.h> #include <libsolidity/ast/ASTVisitor.h>
#include <libsolidity/ast/ASTAnnotations.h> #include <libsolidity/ast/ASTAnnotations.h>
#include <liblangutil/EVMVersion.h>
#include <boost/noncopyable.hpp> #include <boost/noncopyable.hpp>
#include <list> #include <list>
@ -52,10 +53,12 @@ public:
ReferencesResolver( ReferencesResolver(
langutil::ErrorReporter& _errorReporter, langutil::ErrorReporter& _errorReporter,
NameAndTypeResolver& _resolver, NameAndTypeResolver& _resolver,
langutil::EVMVersion _evmVersion,
bool _resolveInsideCode = false bool _resolveInsideCode = false
): ):
m_errorReporter(_errorReporter), m_errorReporter(_errorReporter),
m_resolver(_resolver), m_resolver(_resolver),
m_evmVersion(_evmVersion),
m_resolveInsideCode(_resolveInsideCode) m_resolveInsideCode(_resolveInsideCode)
{} {}
@ -99,6 +102,7 @@ private:
langutil::ErrorReporter& m_errorReporter; langutil::ErrorReporter& m_errorReporter;
NameAndTypeResolver& m_resolver; NameAndTypeResolver& m_resolver;
langutil::EVMVersion m_evmVersion;
/// Stack of return parameters. /// Stack of return parameters.
std::vector<ParameterList const*> m_returnParameters; std::vector<ParameterList const*> m_returnParameters;
bool const m_resolveInsideCode; bool const m_resolveInsideCode;

View File

@ -457,6 +457,9 @@ bool TypeChecker::visit(VariableDeclaration const& _variable)
TypePointer varType = _variable.annotation().type; TypePointer varType = _variable.annotation().type;
solAssert(!!varType, "Variable type not provided."); solAssert(!!varType, "Variable type not provided.");
if (auto contractType = dynamic_cast<ContractType const*>(varType))
if (contractType->contractDefinition().isLibrary())
m_errorReporter.typeError(_variable.location(), "The type of a variable cannot be a library.");
if (_variable.value()) if (_variable.value())
expectType(*_variable.value(), *varType); expectType(*_variable.value(), *varType);
if (_variable.isConstant()) if (_variable.isConstant())
@ -2633,12 +2636,30 @@ bool TypeChecker::visit(Identifier const& _identifier)
if (functionType->canTakeArguments(*annotation.arguments)) if (functionType->canTakeArguments(*annotation.arguments))
candidates.push_back(declaration); candidates.push_back(declaration);
} }
if (candidates.empty()) if (candidates.size() == 1)
m_errorReporter.fatalTypeError(_identifier.location(), "No matching declaration found after argument-dependent lookup.");
else if (candidates.size() == 1)
annotation.referencedDeclaration = candidates.front(); annotation.referencedDeclaration = candidates.front();
else else
m_errorReporter.fatalTypeError(_identifier.location(), "No unique declaration found after argument-dependent lookup."); {
SecondarySourceLocation ssl;
for (Declaration const* declaration: annotation.overloadedDeclarations)
if (declaration->location().isEmpty())
{
// Try to re-construct function definition
string description;
for (auto const& param: declaration->functionType(true)->parameterTypes())
description += (description.empty() ? "" : ", ") + param->toString(false);
description = "function " + _identifier.name() + "(" + description + ")";
ssl.append("Candidate: " + description, declaration->location());
}
else
ssl.append("Candidate:", declaration->location());
if (candidates.empty())
m_errorReporter.fatalTypeError(_identifier.location(), ssl, "No matching declaration found after argument-dependent lookup.");
else
m_errorReporter.fatalTypeError(_identifier.location(), ssl, "No unique declaration found after argument-dependent lookup.");
}
} }
} }
solAssert( solAssert(

View File

@ -3411,6 +3411,15 @@ MemberList::MemberMap TypeType::nativeMembers(ContractDefinition const* _current
return members; return members;
} }
BoolResult TypeType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{
if (auto const* address = dynamic_cast<AddressType const*>(&_convertTo))
if (address->stateMutability() == StateMutability::NonPayable)
if (auto const* contractType = dynamic_cast<ContractType const*>(m_actualType))
return contractType->contractDefinition().isLibrary();
return isImplicitlyConvertibleTo(_convertTo);
}
ModifierType::ModifierType(ModifierDefinition const& _modifier) ModifierType::ModifierType(ModifierDefinition const& _modifier)
{ {
TypePointers params; TypePointers params;

View File

@ -1317,6 +1317,7 @@ public:
std::string toString(bool _short) const override { return "type(" + m_actualType->toString(_short) + ")"; } std::string toString(bool _short) const override { return "type(" + m_actualType->toString(_short) + ")"; }
MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override; MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override;
BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override;
private: private:
TypePointer m_actualType; TypePointer m_actualType;
}; };

View File

@ -475,7 +475,22 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
{ {
solAssert(_functionCall.arguments().size() == 1, ""); solAssert(_functionCall.arguments().size() == 1, "");
solAssert(_functionCall.names().empty(), ""); solAssert(_functionCall.names().empty(), "");
acceptAndConvert(*_functionCall.arguments().front(), *_functionCall.annotation().type); auto const& expression = *_functionCall.arguments().front();
auto const& targetType = *_functionCall.annotation().type;
if (auto const* typeType = dynamic_cast<TypeType const*>(expression.annotation().type))
if (auto const* addressType = dynamic_cast<AddressType const*>(&targetType))
{
auto const* contractType = dynamic_cast<ContractType const*>(typeType->actualType());
solAssert(
contractType &&
contractType->contractDefinition().isLibrary() &&
addressType->stateMutability() == StateMutability::NonPayable,
""
);
m_context.appendLibraryAddress(contractType->contractDefinition().fullyQualifiedName());
return false;
}
acceptAndConvert(expression, targetType);
return false; return false;
} }

View File

@ -428,15 +428,20 @@ void BMC::inlineFunctionCall(FunctionCall const& _funCall)
auto const& funType = dynamic_cast<FunctionType const*>(calledExpr->annotation().type); auto const& funType = dynamic_cast<FunctionType const*>(calledExpr->annotation().type);
solAssert(funType, ""); solAssert(funType, "");
auto const& functionParams = funDef->parameters();
auto const& arguments = _funCall.arguments();
unsigned firstParam = 0;
if (funType->bound()) if (funType->bound())
{ {
auto const& boundFunction = dynamic_cast<MemberAccess const*>(calledExpr); auto const& boundFunction = dynamic_cast<MemberAccess const*>(calledExpr);
solAssert(boundFunction, ""); solAssert(boundFunction, "");
funArgs.push_back(expr(boundFunction->expression())); funArgs.push_back(expr(boundFunction->expression(), functionParams.front()->type()));
firstParam = 1;
} }
for (auto arg: _funCall.arguments()) solAssert((arguments.size() + firstParam) == functionParams.size(), "");
funArgs.push_back(expr(*arg)); for (unsigned i = 0; i < arguments.size(); ++i)
funArgs.push_back(expr(*arguments.at(i), functionParams.at(i + firstParam)->type()));
initializeFunctionCallParameters(*funDef, funArgs); initializeFunctionCallParameters(*funDef, funArgs);
// The reason why we need to pushCallStack here instead of visit(FunctionDefinition) // The reason why we need to pushCallStack here instead of visit(FunctionDefinition)

View File

@ -508,7 +508,13 @@ smt::SortPointer CHC::sort(FunctionDefinition const& _function)
auto boolSort = make_shared<smt::Sort>(smt::Kind::Bool); auto boolSort = make_shared<smt::Sort>(smt::Kind::Bool);
vector<smt::SortPointer> varSorts; vector<smt::SortPointer> varSorts;
for (auto const& var: _function.parameters() + _function.returnParameters()) for (auto const& var: _function.parameters() + _function.returnParameters())
varSorts.push_back(smt::smtSort(*var->type())); {
// SMT solvers do not support function types as arguments.
if (var->type()->category() == Type::Category::Function)
varSorts.push_back(make_shared<smt::Sort>(smt::Kind::Int));
else
varSorts.push_back(smt::smtSort(*var->type()));
}
return make_shared<smt::FunctionSort>( return make_shared<smt::FunctionSort>(
m_stateSorts + varSorts, m_stateSorts + varSorts,
boolSort boolSort
@ -526,7 +532,13 @@ smt::SortPointer CHC::sort(ASTNode const* _node)
auto boolSort = make_shared<smt::Sort>(smt::Kind::Bool); auto boolSort = make_shared<smt::Sort>(smt::Kind::Bool);
vector<smt::SortPointer> varSorts; vector<smt::SortPointer> varSorts;
for (auto const& var: m_currentFunction->localVariables()) for (auto const& var: m_currentFunction->localVariables())
varSorts.push_back(smt::smtSort(*var->type())); {
// SMT solvers do not support function types as arguments.
if (var->type()->category() == Type::Category::Function)
varSorts.push_back(make_shared<smt::Sort>(smt::Kind::Int));
else
varSorts.push_back(smt::smtSort(*var->type()));
}
return make_shared<smt::FunctionSort>( return make_shared<smt::FunctionSort>(
fSort->domain + varSorts, fSort->domain + varSorts,
boolSort boolSort
@ -540,7 +552,7 @@ unique_ptr<smt::SymbolicFunctionVariable> CHC::createSymbolicBlock(smt::SortPoin
_name, _name,
m_context m_context
); );
m_interface->registerRelation(block->currentValue()); m_interface->registerRelation(block->currentFunctionValue());
return block; return block;
} }
@ -572,7 +584,7 @@ smt::Expression CHC::error()
smt::Expression CHC::error(unsigned _idx) smt::Expression CHC::error(unsigned _idx)
{ {
return m_errorPredicate->valueAtIndex(_idx)({}); return m_errorPredicate->functionValueAtIndex(_idx)({});
} }
unique_ptr<smt::SymbolicFunctionVariable> CHC::createBlock(ASTNode const* _node, string const& _prefix) unique_ptr<smt::SymbolicFunctionVariable> CHC::createBlock(ASTNode const* _node, string const& _prefix)
@ -589,7 +601,7 @@ void CHC::createErrorBlock()
{ {
solAssert(m_errorPredicate, ""); solAssert(m_errorPredicate, "");
m_errorPredicate->increaseIndex(); m_errorPredicate->increaseIndex();
m_interface->registerRelation(m_errorPredicate->currentValue()); m_interface->registerRelation(m_errorPredicate->currentFunctionValue());
} }
void CHC::connectBlocks(smt::Expression const& _from, smt::Expression const& _to, smt::Expression const& _constraints) void CHC::connectBlocks(smt::Expression const& _from, smt::Expression const& _to, smt::Expression const& _constraints)

View File

@ -154,15 +154,15 @@ private:
//@{ //@{
/// Constructor predicate. /// Constructor predicate.
/// Default constructor sets state vars to 0. /// Default constructor sets state vars to 0.
std::unique_ptr<smt::SymbolicVariable> m_constructorPredicate; std::unique_ptr<smt::SymbolicFunctionVariable> m_constructorPredicate;
/// Artificial Interface predicate. /// Artificial Interface predicate.
/// Single entry block for all functions. /// Single entry block for all functions.
std::unique_ptr<smt::SymbolicVariable> m_interfacePredicate; std::unique_ptr<smt::SymbolicFunctionVariable> m_interfacePredicate;
/// Artificial Error predicate. /// Artificial Error predicate.
/// Single error block for all assertions. /// Single error block for all assertions.
std::unique_ptr<smt::SymbolicVariable> m_errorPredicate; std::unique_ptr<smt::SymbolicFunctionVariable> m_errorPredicate;
//@} //@}
/// Variables. /// Variables.

View File

@ -136,9 +136,13 @@ void SMTEncoder::visitFunctionOrModifier()
*modifierInvocation->name()->annotation().referencedDeclaration *modifierInvocation->name()->annotation().referencedDeclaration
); );
vector<smt::Expression> modifierArgsExpr; vector<smt::Expression> modifierArgsExpr;
if (modifierInvocation->arguments()) if (auto const* arguments = modifierInvocation->arguments())
for (auto arg: *modifierInvocation->arguments()) {
modifierArgsExpr.push_back(expr(*arg)); auto const& modifierParams = modifierDef.parameters();
solAssert(modifierParams.size() == arguments->size(), "");
for (unsigned i = 0; i < arguments->size(); ++i)
modifierArgsExpr.push_back(expr(*arguments->at(i), modifierParams.at(i)->type()));
}
initializeFunctionCallParameters(modifierDef, modifierArgsExpr); initializeFunctionCallParameters(modifierDef, modifierArgsExpr);
pushCallStack({&modifierDef, modifierInvocation.get()}); pushCallStack({&modifierDef, modifierInvocation.get()});
modifierDef.body().accept(*this); modifierDef.body().accept(*this);
@ -595,10 +599,10 @@ void SMTEncoder::endVisit(Identifier const& _identifier)
{ {
// Will be translated as part of the node that requested the lvalue. // Will be translated as part of the node that requested the lvalue.
} }
else if (_identifier.annotation().type->category() == Type::Category::Function)
visitFunctionIdentifier(_identifier);
else if (auto decl = identifierToVariable(_identifier)) else if (auto decl = identifierToVariable(_identifier))
defineExpr(_identifier, currentValue(*decl)); defineExpr(_identifier, currentValue(*decl));
else if (_identifier.annotation().type->category() == Type::Category::Function)
visitFunctionIdentifier(_identifier);
else if (_identifier.name() == "now") else if (_identifier.name() == "now")
defineGlobalVariable(_identifier.name(), _identifier); defineGlobalVariable(_identifier.name(), _identifier);
else if (_identifier.name() == "this") else if (_identifier.name() == "this")
@ -699,7 +703,7 @@ void SMTEncoder::endVisit(Return const& _return)
} }
} }
else if (returnParams.size() == 1) else if (returnParams.size() == 1)
m_context.addAssertion(expr(*_return.expression()) == m_context.newValue(*returnParams.front())); m_context.addAssertion(expr(*_return.expression(), returnParams.front()->type()) == m_context.newValue(*returnParams.front()));
} }
} }
@ -1353,7 +1357,7 @@ void SMTEncoder::createExpr(Expression const& _e)
void SMTEncoder::defineExpr(Expression const& _e, smt::Expression _value) void SMTEncoder::defineExpr(Expression const& _e, smt::Expression _value)
{ {
createExpr(_e); createExpr(_e);
solAssert(smt::smtKind(_e.annotation().type->category()) != smt::Kind::Function, "Equality operator applied to type that is not fully supported"); solAssert(_value.sort->kind != smt::Kind::Function, "Equality operator applied to type that is not fully supported");
m_context.addAssertion(expr(_e) == _value); m_context.addAssertion(expr(_e) == _value);
} }

View File

@ -19,6 +19,7 @@
#include <libsolidity/ast/TypeProvider.h> #include <libsolidity/ast/TypeProvider.h>
#include <libsolidity/ast/Types.h> #include <libsolidity/ast/Types.h>
#include <libdevcore/CommonData.h>
#include <memory> #include <memory>
using namespace std; using namespace std;
@ -139,7 +140,28 @@ pair<bool, shared_ptr<SymbolicVariable>> newSymbolicVariable(
else if (isBool(_type.category())) else if (isBool(_type.category()))
var = make_shared<SymbolicBoolVariable>(type, _uniqueName, _context); var = make_shared<SymbolicBoolVariable>(type, _uniqueName, _context);
else if (isFunction(_type.category())) else if (isFunction(_type.category()))
var = make_shared<SymbolicFunctionVariable>(type, _uniqueName, _context); {
auto const& fType = dynamic_cast<FunctionType const*>(type);
auto const& paramsIn = fType->parameterTypes();
auto const& paramsOut = fType->returnParameterTypes();
auto findFunctionParam = [&](auto&& params) {
return find_if(
begin(params),
end(params),
[&](TypePointer _paramType) { return _paramType->category() == solidity::Type::Category::Function; }
);
};
if (
findFunctionParam(paramsIn) != end(paramsIn) ||
findFunctionParam(paramsOut) != end(paramsOut)
)
{
abstract = true;
var = make_shared<SymbolicIntVariable>(TypeProvider::uint256(), type, _uniqueName, _context);
}
else
var = make_shared<SymbolicFunctionVariable>(type, _uniqueName, _context);
}
else if (isInteger(_type.category())) else if (isInteger(_type.category()))
var = make_shared<SymbolicIntVariable>(type, type, _uniqueName, _context); var = make_shared<SymbolicIntVariable>(type, type, _uniqueName, _context);
else if (isFixedBytes(_type.category())) else if (isFixedBytes(_type.category()))

View File

@ -19,7 +19,6 @@
#include <libsolidity/formal/SymbolicTypes.h> #include <libsolidity/formal/SymbolicTypes.h>
#include <libsolidity/ast/AST.h> #include <libsolidity/ast/AST.h>
#include <libsolidity/ast/TypeProvider.h>
using namespace std; using namespace std;
using namespace dev; using namespace dev;
@ -153,16 +152,38 @@ SymbolicFunctionVariable::SymbolicFunctionVariable(
solAssert(m_sort->kind == Kind::Function, ""); solAssert(m_sort->kind == Kind::Function, "");
} }
void SymbolicFunctionVariable::resetDeclaration() Expression SymbolicFunctionVariable::currentValue(solidity::TypePointer const& _targetType) const
{ {
m_declaration = m_context.newVariable(currentName(), m_sort); return m_abstract.currentValue(_targetType);
}
Expression SymbolicFunctionVariable::currentFunctionValue() const
{
return m_declaration;
}
Expression SymbolicFunctionVariable::valueAtIndex(int _index) const
{
return m_abstract.valueAtIndex(_index);
}
Expression SymbolicFunctionVariable::functionValueAtIndex(int _index) const
{
return SymbolicVariable::valueAtIndex(_index);
}
Expression SymbolicFunctionVariable::resetIndex()
{
SymbolicVariable::resetIndex();
return m_abstract.resetIndex();
} }
Expression SymbolicFunctionVariable::increaseIndex() Expression SymbolicFunctionVariable::increaseIndex()
{ {
++(*m_ssa); ++(*m_ssa);
resetDeclaration(); resetDeclaration();
return currentValue(); m_abstract.increaseIndex();
return m_abstract.currentValue();
} }
Expression SymbolicFunctionVariable::operator()(vector<Expression> _arguments) const Expression SymbolicFunctionVariable::operator()(vector<Expression> _arguments) const
@ -170,6 +191,11 @@ Expression SymbolicFunctionVariable::operator()(vector<Expression> _arguments) c
return m_declaration(_arguments); return m_declaration(_arguments);
} }
void SymbolicFunctionVariable::resetDeclaration()
{
m_declaration = m_context.newVariable(currentName(), m_sort);
}
SymbolicMappingVariable::SymbolicMappingVariable( SymbolicMappingVariable::SymbolicMappingVariable(
solidity::TypePointer _type, solidity::TypePointer _type,
string _uniqueName, string _uniqueName,

View File

@ -20,6 +20,7 @@
#include <libsolidity/formal/SolverInterface.h> #include <libsolidity/formal/SolverInterface.h>
#include <libsolidity/formal/SSAVariable.h> #include <libsolidity/formal/SSAVariable.h>
#include <libsolidity/ast/Types.h> #include <libsolidity/ast/Types.h>
#include <libsolidity/ast/TypeProvider.h>
#include <memory> #include <memory>
namespace dev namespace dev
@ -138,7 +139,12 @@ public:
}; };
/** /**
* Specialization of SymbolicVariable for FunctionType * Specialization of SymbolicVariable for FunctionType.
* Besides containing a symbolic function declaration,
* it also has an integer used as abstraction.
* By default, the abstract representation is used when
* values are requested, and the function declaration is
* used when operator() is applied over arguments.
*/ */
class SymbolicFunctionVariable: public SymbolicVariable class SymbolicFunctionVariable: public SymbolicVariable
{ {
@ -154,8 +160,20 @@ public:
EncodingContext& _context EncodingContext& _context
); );
Expression increaseIndex(); Expression currentValue(solidity::TypePointer const& _targetType = TypePointer{}) const override;
Expression operator()(std::vector<Expression> _arguments) const;
// Explicit request the function declaration.
Expression currentFunctionValue() const;
Expression valueAtIndex(int _index) const override;
// Explicit request the function declaration.
Expression functionValueAtIndex(int _index) const;
Expression resetIndex() override;
Expression increaseIndex() override;
Expression operator()(std::vector<Expression> _arguments) const override;
private: private:
/// Creates a new function declaration. /// Creates a new function declaration.
@ -163,6 +181,14 @@ private:
/// Stores the current function declaration. /// Stores the current function declaration.
Expression m_declaration; Expression m_declaration;
/// Abstract representation.
SymbolicIntVariable m_abstract{
TypeProvider::uint256(),
TypeProvider::uint256(),
m_uniqueName + "_abstract",
m_context
};
}; };
/** /**

View File

@ -43,6 +43,7 @@
#include <libsolidity/interface/ABI.h> #include <libsolidity/interface/ABI.h>
#include <libsolidity/interface/Natspec.h> #include <libsolidity/interface/Natspec.h>
#include <libsolidity/interface/GasEstimator.h> #include <libsolidity/interface/GasEstimator.h>
#include <libsolidity/interface/StorageLayout.h>
#include <libsolidity/interface/Version.h> #include <libsolidity/interface/Version.h>
#include <libsolidity/parsing/Parser.h> #include <libsolidity/parsing/Parser.h>
@ -286,7 +287,7 @@ bool CompilerStack::analyze()
noErrors = false; noErrors = false;
m_globalContext = make_shared<GlobalContext>(); m_globalContext = make_shared<GlobalContext>();
NameAndTypeResolver resolver(*m_globalContext, m_scopes, m_errorReporter); NameAndTypeResolver resolver(*m_globalContext, m_evmVersion, m_scopes, m_errorReporter);
for (Source const* source: m_sourceOrder) for (Source const* source: m_sourceOrder)
if (!resolver.registerDeclarations(*source->ast)) if (!resolver.registerDeclarations(*source->ast))
return false; return false;
@ -684,6 +685,28 @@ Json::Value const& CompilerStack::contractABI(Contract const& _contract) const
return *_contract.abi; return *_contract.abi;
} }
Json::Value const& CompilerStack::storageLayout(string const& _contractName) const
{
if (m_stackState < AnalysisPerformed)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
return storageLayout(contract(_contractName));
}
Json::Value const& CompilerStack::storageLayout(Contract const& _contract) const
{
if (m_stackState < AnalysisPerformed)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
solAssert(_contract.contract, "");
// caches the result
if (!_contract.storageLayout)
_contract.storageLayout.reset(new Json::Value(StorageLayout().generate(*_contract.contract)));
return *_contract.storageLayout;
}
Json::Value const& CompilerStack::natspecUser(string const& _contractName) const Json::Value const& CompilerStack::natspecUser(string const& _contractName) const
{ {
if (m_stackState < AnalysisPerformed) if (m_stackState < AnalysisPerformed)

View File

@ -287,6 +287,10 @@ public:
/// Prerequisite: Successful call to parse or compile. /// Prerequisite: Successful call to parse or compile.
Json::Value const& contractABI(std::string const& _contractName) const; Json::Value const& contractABI(std::string const& _contractName) const;
/// @returns a JSON representing the storage layout of the contract.
/// Prerequisite: Successful call to parse or compile.
Json::Value const& storageLayout(std::string const& _contractName) const;
/// @returns a JSON representing the contract's user documentation. /// @returns a JSON representing the contract's user documentation.
/// Prerequisite: Successful call to parse or compile. /// Prerequisite: Successful call to parse or compile.
Json::Value const& natspecUser(std::string const& _contractName) const; Json::Value const& natspecUser(std::string const& _contractName) const;
@ -334,6 +338,7 @@ private:
eth::LinkerObject eWasmObject; ///< Experimental eWasm code eth::LinkerObject eWasmObject; ///< Experimental eWasm code
mutable std::unique_ptr<std::string const> metadata; ///< The metadata json that will be hashed into the chain. mutable std::unique_ptr<std::string const> metadata; ///< The metadata json that will be hashed into the chain.
mutable std::unique_ptr<Json::Value const> abi; mutable std::unique_ptr<Json::Value const> abi;
mutable std::unique_ptr<Json::Value const> storageLayout;
mutable std::unique_ptr<Json::Value const> userDocumentation; mutable std::unique_ptr<Json::Value const> userDocumentation;
mutable std::unique_ptr<Json::Value const> devDocumentation; mutable std::unique_ptr<Json::Value const> devDocumentation;
mutable std::unique_ptr<std::string const> sourceMapping; mutable std::unique_ptr<std::string const> sourceMapping;
@ -397,6 +402,10 @@ private:
/// This will generate the JSON object and store it in the Contract object if it is not present yet. /// This will generate the JSON object and store it in the Contract object if it is not present yet.
Json::Value const& contractABI(Contract const&) const; Json::Value const& contractABI(Contract const&) const;
/// @returns the storage layout of the contract as a JSON object.
/// This will generate the JSON object and store it in the Contract object if it is not present yet.
Json::Value const& storageLayout(Contract const&) const;
/// @returns the Natspec User documentation as a JSON object. /// @returns the Natspec User documentation as a JSON object.
/// This will generate the JSON object and store it in the Contract object if it is not present yet. /// This will generate the JSON object and store it in the Contract object if it is not present yet.
Json::Value const& natspecUser(Contract const&) const; Json::Value const& natspecUser(Contract const&) const;

View File

@ -927,10 +927,12 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting
string file = contractName.substr(0, colon); string file = contractName.substr(0, colon);
string name = contractName.substr(colon + 1); string name = contractName.substr(colon + 1);
// ABI, documentation and metadata // ABI, storage layout, documentation and metadata
Json::Value contractData(Json::objectValue); Json::Value contractData(Json::objectValue);
if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "abi", wildcardMatchesExperimental)) if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "abi", wildcardMatchesExperimental))
contractData["abi"] = compilerStack.contractABI(contractName); contractData["abi"] = compilerStack.contractABI(contractName);
if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "storageLayout", false))
contractData["storageLayout"] = compilerStack.storageLayout(contractName);
if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "metadata", wildcardMatchesExperimental)) if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "metadata", wildcardMatchesExperimental))
contractData["metadata"] = compilerStack.metadata(contractName); contractData["metadata"] = compilerStack.metadata(contractName);
if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "userdoc", wildcardMatchesExperimental)) if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "userdoc", wildcardMatchesExperimental))

View File

@ -0,0 +1,119 @@
/*
This file is part of solidity.
solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/
#include <libsolidity/interface/StorageLayout.h>
#include <libsolidity/ast/TypeProvider.h>
using namespace std;
using namespace dev;
using namespace dev::solidity;
Json::Value StorageLayout::generate(ContractDefinition const& _contractDef)
{
solAssert(!m_contract, "");
m_contract = &_contractDef;
m_types.clear();
auto typeType = dynamic_cast<TypeType const*>(_contractDef.type());
solAssert(typeType, "");
auto contractType = dynamic_cast<ContractType const*>(typeType->actualType());
solAssert(contractType, "");
Json::Value variables(Json::arrayValue);
for (auto [var, slot, offset]: contractType->stateVariables())
variables.append(generate(*var, slot, offset));
Json::Value layout;
layout["storage"] = move(variables);
layout["types"] = move(m_types);
return layout;
}
Json::Value StorageLayout::generate(VariableDeclaration const& _var, u256 const& _slot, unsigned _offset)
{
Json::Value varEntry;
TypePointer varType = _var.type();
varEntry["label"] = _var.name();
varEntry["astId"] = int(_var.id());
varEntry["contract"] = m_contract->fullyQualifiedName();
varEntry["slot"] = _slot.str();
varEntry["offset"] = _offset;
varEntry["type"] = typeKeyName(varType);
generate(varType);
return varEntry;
}
void StorageLayout::generate(TypePointer _type)
{
if (m_types.isMember(typeKeyName(_type)))
return;
// Register it now to cut recursive visits.
Json::Value& typeInfo = m_types[typeKeyName(_type)];
typeInfo["label"] = _type->toString(true);
typeInfo["numberOfBytes"] = u256(_type->storageBytes() * _type->storageSize()).str();
if (auto structType = dynamic_cast<StructType const*>(_type))
{
Json::Value members(Json::arrayValue);
auto const& structDef = structType->structDefinition();
for (auto const& member: structDef.members())
{
auto const& offsets = structType->storageOffsetsOfMember(member->name());
members.append(generate(*member, offsets.first, offsets.second));
}
typeInfo["members"] = move(members);
typeInfo["encoding"] = "inplace";
}
else if (auto mappingType = dynamic_cast<MappingType const*>(_type))
{
typeInfo["key"] = typeKeyName(mappingType->keyType());
typeInfo["value"] = typeKeyName(mappingType->valueType());
generate(mappingType->keyType());
generate(mappingType->valueType());
typeInfo["encoding"] = "mapping";
}
else if (auto arrayType = dynamic_cast<ArrayType const*>(_type))
{
if (arrayType->isByteArray())
typeInfo["encoding"] = "bytes";
else
{
typeInfo["base"] = typeKeyName(arrayType->baseType());
generate(arrayType->baseType());
typeInfo["encoding"] = arrayType->isDynamicallySized() ? "dynamic_array" : "inplace";
}
}
else
{
solAssert(_type->isValueType(), "");
typeInfo["encoding"] = "inplace";
}
solAssert(typeInfo.isMember("encoding"), "");
}
string StorageLayout::typeKeyName(TypePointer _type)
{
if (auto refType = dynamic_cast<ReferenceType const*>(_type))
return TypeProvider::withLocationIfReference(refType->location(), _type)->richIdentifier();
return _type->richIdentifier();
}

View File

@ -0,0 +1,58 @@
/*
This file is part of solidity.
solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Generates the storage layout of a contract.
*/
#pragma once
#include <libsolidity/ast/AST.h>
#include <libsolidity/ast/Types.h>
#include <json/json.h>
namespace dev
{
namespace solidity
{
class StorageLayout
{
public:
/// Generates the storage layout of the contract
/// @param _contractDef The contract definition
/// @return A JSON representation of the contract's storage layout.
Json::Value generate(ContractDefinition const& _contractDef);
private:
/// Generates the JSON information for a variable and its storage location.
Json::Value generate(VariableDeclaration const& _var, u256 const& _slot, unsigned _offset);
/// Generates the JSON information for @param _type
void generate(TypePointer _type);
/// The key for the JSON object describing a type.
std::string typeKeyName(TypePointer _type);
Json::Value m_types;
/// Current analyzed contract
ContractDefinition const* m_contract = nullptr;
};
}
}

View File

@ -5,4 +5,4 @@
"compiler-fixed": "off", "compiler-fixed": "off",
"no-inline-assembly": "off" "no-inline-assembly": "off"
} }
} }

View File

@ -0,0 +1,5 @@
require_overload/input.sol:4:9: Error: No matching declaration found after argument-dependent lookup.
require(this);
^-----^
Candidate: function require(bool)
Candidate: function require(bool, string memory)

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,6 @@
pragma solidity >=0.0;
contract C {
function f() public pure {
require(this);
}
}

View File

@ -0,0 +1,16 @@
{
"language": "Solidity",
"sources": {
"fileA": {
"content": "contract A { bytes s1 = \"test\"; bytes s2; }"
}
},
"settings": {
"outputSelection": {
"fileA": {
"A": [ "storageLayout" ],
"": [ "storageLayout" ]
}
}
}
}

View File

@ -0,0 +1,4 @@
{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":3,"contract":"fileA:A","label":"s1","offset":0,"slot":"0","type":"t_bytes_storage"},{"astId":5,"contract":"fileA:A","label":"s2","offset":0,"slot":"1","type":"t_bytes_storage"}],"types":{"t_bytes_storage":{"encoding":"bytes","label":"bytes","numberOfBytes":"32"}}}}}},"errors":[{"component":"general","formattedMessage":"fileA:1:1: Warning: Source file does not specify required compiler version!
contract A { bytes s1 = \"test\"; bytes s2; }
^-----------------------------------------^
","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":43,"file":"fileA","start":0},"type":"Warning"}],"sources":{"fileA":{"id":0}}}

View File

@ -0,0 +1,16 @@
{
"language": "Solidity",
"sources": {
"fileA": {
"content": "contract A { uint[] array1; bool[] array2; }"
}
},
"settings": {
"outputSelection": {
"fileA": {
"A": [ "storageLayout" ],
"": [ "storageLayout" ]
}
}
}
}

View File

@ -0,0 +1,4 @@
{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":3,"contract":"fileA:A","label":"array1","offset":0,"slot":"0","type":"t_array(t_uint256)dyn_storage"},{"astId":6,"contract":"fileA:A","label":"array2","offset":0,"slot":"1","type":"t_array(t_bool)dyn_storage"}],"types":{"t_array(t_bool)dyn_storage":{"base":"t_bool","encoding":"dynamic_array","label":"bool[]","numberOfBytes":"32"},"t_array(t_uint256)dyn_storage":{"base":"t_uint256","encoding":"dynamic_array","label":"uint256[]","numberOfBytes":"32"},"t_bool":{"encoding":"inplace","label":"bool","numberOfBytes":"1"},"t_uint256":{"encoding":"inplace","label":"uint256","numberOfBytes":"32"}}}}}},"errors":[{"component":"general","formattedMessage":"fileA:1:1: Warning: Source file does not specify required compiler version!
contract A { uint[] array1; bool[] array2; }
^------------------------------------------^
","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":44,"file":"fileA","start":0},"type":"Warning"}],"sources":{"fileA":{"id":0}}}

View File

@ -0,0 +1,16 @@
{
"language": "Solidity",
"sources": {
"fileA": {
"content": "contract A { struct S { uint128 a; uint128 b; uint[2] staticArray; uint[] dynArray; } uint x; uint y; S s; address addr; mapping (uint => mapping (address => bool)) map; uint[] array; string s1; bytes b1; }"
}
},
"settings": {
"outputSelection": {
"fileA": {
"A": [ "storageLayout" ],
"": [ "storageLayout" ]
}
}
}
}

View File

@ -0,0 +1,4 @@
{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":14,"contract":"fileA:A","label":"x","offset":0,"slot":"0","type":"t_uint256"},{"astId":16,"contract":"fileA:A","label":"y","offset":0,"slot":"1","type":"t_uint256"},{"astId":18,"contract":"fileA:A","label":"s","offset":0,"slot":"2","type":"t_struct(S)12_storage"},{"astId":20,"contract":"fileA:A","label":"addr","offset":0,"slot":"6","type":"t_address"},{"astId":26,"contract":"fileA:A","label":"map","offset":0,"slot":"7","type":"t_mapping(t_uint256,t_mapping(t_address,t_bool))"},{"astId":29,"contract":"fileA:A","label":"array","offset":0,"slot":"8","type":"t_array(t_uint256)dyn_storage"},{"astId":31,"contract":"fileA:A","label":"s1","offset":0,"slot":"9","type":"t_string_storage"},{"astId":33,"contract":"fileA:A","label":"b1","offset":0,"slot":"10","type":"t_bytes_storage"}],"types":{"t_address":{"encoding":"inplace","label":"address","numberOfBytes":"20"},"t_array(t_uint256)2_storage":{"base":"t_uint256","encoding":"inplace","label":"uint256[2]","numberOfBytes":"64"},"t_array(t_uint256)dyn_storage":{"base":"t_uint256","encoding":"dynamic_array","label":"uint256[]","numberOfBytes":"32"},"t_bool":{"encoding":"inplace","label":"bool","numberOfBytes":"1"},"t_bytes_storage":{"encoding":"bytes","label":"bytes","numberOfBytes":"32"},"t_mapping(t_address,t_bool)":{"encoding":"mapping","key":"t_address","label":"mapping(address => bool)","numberOfBytes":"32","value":"t_bool"},"t_mapping(t_uint256,t_mapping(t_address,t_bool))":{"encoding":"mapping","key":"t_uint256","label":"mapping(uint256 => mapping(address => bool))","numberOfBytes":"32","value":"t_mapping(t_address,t_bool)"},"t_string_storage":{"encoding":"bytes","label":"string","numberOfBytes":"32"},"t_struct(S)12_storage":{"encoding":"inplace","label":"struct A.S","members":[{"astId":2,"contract":"fileA:A","label":"a","offset":0,"slot":"0","type":"t_uint128"},{"astId":4,"contract":"fileA:A","label":"b","offset":16,"slot":"0","type":"t_uint128"},{"astId":8,"contract":"fileA:A","label":"staticArray","offset":0,"slot":"1","type":"t_array(t_uint256)2_storage"},{"astId":11,"contract":"fileA:A","label":"dynArray","offset":0,"slot":"3","type":"t_array(t_uint256)dyn_storage"}],"numberOfBytes":"128"},"t_uint128":{"encoding":"inplace","label":"uint128","numberOfBytes":"16"},"t_uint256":{"encoding":"inplace","label":"uint256","numberOfBytes":"32"}}}}}},"errors":[{"component":"general","formattedMessage":"fileA:1:1: Warning: Source file does not specify required compiler version!
contract A { struct S { uint128 a; ... int[] array; string s1; bytes b1; }
^-------------------------------------------------------------------------^
","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":206,"file":"fileA","start":0},"type":"Warning"}],"sources":{"fileA":{"id":0}}}

View File

@ -0,0 +1,16 @@
{
"language": "Solidity",
"sources": {
"fileA": {
"content": "contract A { uint x; uint y; mapping (uint => mapping (address => bool)) map; }"
}
},
"settings": {
"outputSelection": {
"fileA": {
"A": [ "storageLayout" ],
"": [ "storageLayout" ]
}
}
}
}

View File

@ -0,0 +1,4 @@
{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":2,"contract":"fileA:A","label":"x","offset":0,"slot":"0","type":"t_uint256"},{"astId":4,"contract":"fileA:A","label":"y","offset":0,"slot":"1","type":"t_uint256"},{"astId":10,"contract":"fileA:A","label":"map","offset":0,"slot":"2","type":"t_mapping(t_uint256,t_mapping(t_address,t_bool))"}],"types":{"t_address":{"encoding":"inplace","label":"address","numberOfBytes":"20"},"t_bool":{"encoding":"inplace","label":"bool","numberOfBytes":"1"},"t_mapping(t_address,t_bool)":{"encoding":"mapping","key":"t_address","label":"mapping(address => bool)","numberOfBytes":"32","value":"t_bool"},"t_mapping(t_uint256,t_mapping(t_address,t_bool))":{"encoding":"mapping","key":"t_uint256","label":"mapping(uint256 => mapping(address => bool))","numberOfBytes":"32","value":"t_mapping(t_address,t_bool)"},"t_uint256":{"encoding":"inplace","label":"uint256","numberOfBytes":"32"}}}}}},"errors":[{"component":"general","formattedMessage":"fileA:1:1: Warning: Source file does not specify required compiler version!
contract A { uint x; uint y; mapping (uint => mapping (address => bool)) map; }
^-----------------------------------------------------------------------------^
","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":79,"file":"fileA","start":0},"type":"Warning"}],"sources":{"fileA":{"id":0}}}

View File

@ -0,0 +1,16 @@
{
"language": "Solidity",
"sources": {
"fileA": {
"content": "contract A { }"
}
},
"settings": {
"outputSelection": {
"fileA": {
"A": [ "storageLayout" ],
"": [ "storageLayout" ]
}
}
}
}

View File

@ -0,0 +1,4 @@
{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[],"types":null}}}},"errors":[{"component":"general","formattedMessage":"fileA:1:1: Warning: Source file does not specify required compiler version!
contract A { }
^------------^
","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":14,"file":"fileA","start":0},"type":"Warning"}],"sources":{"fileA":{"id":0}}}

View File

@ -0,0 +1,19 @@
{
"language": "Solidity",
"sources": {
"fileA": {
"content": "contract A { }"
},
"fileB": {
"content": "contract A { uint x; uint y; }"
}
},
"settings": {
"outputSelection": {
"fileA": {
"A": [ "storageLayout" ],
"": [ "storageLayout" ]
}
}
}
}

View File

@ -0,0 +1,4 @@
{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[],"types":null}}}},"errors":[{"component":"general","formattedMessage":"fileA:1:1: Warning: Source file does not specify required compiler version!
contract A { }
^------------^
","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":14,"file":"fileA","start":0},"type":"Warning"}],"sources":{"fileA":{"id":0},"fileB":{"id":1}}}

View File

@ -0,0 +1,16 @@
{
"language": "Solidity",
"sources": {
"fileA": {
"content": "contract A { string s1 = \"test\"; string s2; }"
}
},
"settings": {
"outputSelection": {
"fileA": {
"A": [ "storageLayout" ],
"": [ "storageLayout" ]
}
}
}
}

View File

@ -0,0 +1,4 @@
{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":3,"contract":"fileA:A","label":"s1","offset":0,"slot":"0","type":"t_string_storage"},{"astId":5,"contract":"fileA:A","label":"s2","offset":0,"slot":"1","type":"t_string_storage"}],"types":{"t_string_storage":{"encoding":"bytes","label":"string","numberOfBytes":"32"}}}}}},"errors":[{"component":"general","formattedMessage":"fileA:1:1: Warning: Source file does not specify required compiler version!
contract A { string s1 = \"test\"; string s2; }
^-------------------------------------------^
","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":45,"file":"fileA","start":0},"type":"Warning"}],"sources":{"fileA":{"id":0}}}

View File

@ -0,0 +1,16 @@
{
"language": "Solidity",
"sources": {
"fileA": {
"content": "contract A { struct S { uint a; uint b; uint[2] staticArray; uint[] dynArray; } uint x; uint y; S s; address addr; }"
}
},
"settings": {
"outputSelection": {
"fileA": {
"A": [ "storageLayout" ],
"": [ "storageLayout" ]
}
}
}
}

View File

@ -0,0 +1,4 @@
{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":14,"contract":"fileA:A","label":"x","offset":0,"slot":"0","type":"t_uint256"},{"astId":16,"contract":"fileA:A","label":"y","offset":0,"slot":"1","type":"t_uint256"},{"astId":18,"contract":"fileA:A","label":"s","offset":0,"slot":"2","type":"t_struct(S)12_storage"},{"astId":20,"contract":"fileA:A","label":"addr","offset":0,"slot":"7","type":"t_address"}],"types":{"t_address":{"encoding":"inplace","label":"address","numberOfBytes":"20"},"t_array(t_uint256)2_storage":{"base":"t_uint256","encoding":"inplace","label":"uint256[2]","numberOfBytes":"64"},"t_array(t_uint256)dyn_storage":{"base":"t_uint256","encoding":"dynamic_array","label":"uint256[]","numberOfBytes":"32"},"t_struct(S)12_storage":{"encoding":"inplace","label":"struct A.S","members":[{"astId":2,"contract":"fileA:A","label":"a","offset":0,"slot":"0","type":"t_uint256"},{"astId":4,"contract":"fileA:A","label":"b","offset":0,"slot":"1","type":"t_uint256"},{"astId":8,"contract":"fileA:A","label":"staticArray","offset":0,"slot":"2","type":"t_array(t_uint256)2_storage"},{"astId":11,"contract":"fileA:A","label":"dynArray","offset":0,"slot":"4","type":"t_array(t_uint256)dyn_storage"}],"numberOfBytes":"160"},"t_uint256":{"encoding":"inplace","label":"uint256","numberOfBytes":"32"}}}}}},"errors":[{"component":"general","formattedMessage":"fileA:1:1: Warning: Source file does not specify required compiler version!
contract A { struct S { uint a; uint b; uint[2] staticArray; uint[] dynArray; } uint x; uint y; S s; address addr; }
^------------------------------------------------------------------------------------------------------------------^
","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":116,"file":"fileA","start":0},"type":"Warning"}],"sources":{"fileA":{"id":0}}}

View File

@ -0,0 +1,16 @@
{
"language": "Solidity",
"sources": {
"fileA": {
"content": "contract A { struct S { uint128 a; uint128 b; uint[2] staticArray; uint[] dynArray; } uint x; uint y; S s; address addr; }"
}
},
"settings": {
"outputSelection": {
"fileA": {
"A": [ "storageLayout" ],
"": [ "storageLayout" ]
}
}
}
}

View File

@ -0,0 +1,4 @@
{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":14,"contract":"fileA:A","label":"x","offset":0,"slot":"0","type":"t_uint256"},{"astId":16,"contract":"fileA:A","label":"y","offset":0,"slot":"1","type":"t_uint256"},{"astId":18,"contract":"fileA:A","label":"s","offset":0,"slot":"2","type":"t_struct(S)12_storage"},{"astId":20,"contract":"fileA:A","label":"addr","offset":0,"slot":"6","type":"t_address"}],"types":{"t_address":{"encoding":"inplace","label":"address","numberOfBytes":"20"},"t_array(t_uint256)2_storage":{"base":"t_uint256","encoding":"inplace","label":"uint256[2]","numberOfBytes":"64"},"t_array(t_uint256)dyn_storage":{"base":"t_uint256","encoding":"dynamic_array","label":"uint256[]","numberOfBytes":"32"},"t_struct(S)12_storage":{"encoding":"inplace","label":"struct A.S","members":[{"astId":2,"contract":"fileA:A","label":"a","offset":0,"slot":"0","type":"t_uint128"},{"astId":4,"contract":"fileA:A","label":"b","offset":16,"slot":"0","type":"t_uint128"},{"astId":8,"contract":"fileA:A","label":"staticArray","offset":0,"slot":"1","type":"t_array(t_uint256)2_storage"},{"astId":11,"contract":"fileA:A","label":"dynArray","offset":0,"slot":"3","type":"t_array(t_uint256)dyn_storage"}],"numberOfBytes":"128"},"t_uint128":{"encoding":"inplace","label":"uint128","numberOfBytes":"16"},"t_uint256":{"encoding":"inplace","label":"uint256","numberOfBytes":"32"}}}}}},"errors":[{"component":"general","formattedMessage":"fileA:1:1: Warning: Source file does not specify required compiler version!
contract A { struct S { uint128 a; uint128 b; uint[2] staticArray; uint[] dynArray; } uint x; uint y; S s; address addr; }
^------------------------------------------------------------------------------------------------------------------------^
","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":122,"file":"fileA","start":0},"type":"Warning"}],"sources":{"fileA":{"id":0}}}

View File

@ -0,0 +1,16 @@
{
"language": "Solidity",
"sources": {
"fileA": {
"content": "contract A { uint x; uint y; address addr; uint[2] array; }"
}
},
"settings": {
"outputSelection": {
"fileA": {
"A": [ "storageLayout" ],
"": [ "storageLayout" ]
}
}
}
}

View File

@ -0,0 +1,4 @@
{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":2,"contract":"fileA:A","label":"x","offset":0,"slot":"0","type":"t_uint256"},{"astId":4,"contract":"fileA:A","label":"y","offset":0,"slot":"1","type":"t_uint256"},{"astId":6,"contract":"fileA:A","label":"addr","offset":0,"slot":"2","type":"t_address"},{"astId":10,"contract":"fileA:A","label":"array","offset":0,"slot":"3","type":"t_array(t_uint256)2_storage"}],"types":{"t_address":{"encoding":"inplace","label":"address","numberOfBytes":"20"},"t_array(t_uint256)2_storage":{"base":"t_uint256","encoding":"inplace","label":"uint256[2]","numberOfBytes":"64"},"t_uint256":{"encoding":"inplace","label":"uint256","numberOfBytes":"32"}}}}}},"errors":[{"component":"general","formattedMessage":"fileA:1:1: Warning: Source file does not specify required compiler version!
contract A { uint x; uint y; address addr; uint[2] array; }
^---------------------------------------------------------^
","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":59,"file":"fileA","start":0},"type":"Warning"}],"sources":{"fileA":{"id":0}}}

View File

@ -0,0 +1,16 @@
{
"language": "Solidity",
"sources": {
"fileA": {
"content": "contract A { uint64 x; uint128 y; uint128 z; address addr; uint[2] array; }"
}
},
"settings": {
"outputSelection": {
"fileA": {
"A": [ "storageLayout" ],
"": [ "storageLayout" ]
}
}
}
}

View File

@ -0,0 +1,4 @@
{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":2,"contract":"fileA:A","label":"x","offset":0,"slot":"0","type":"t_uint64"},{"astId":4,"contract":"fileA:A","label":"y","offset":8,"slot":"0","type":"t_uint128"},{"astId":6,"contract":"fileA:A","label":"z","offset":0,"slot":"1","type":"t_uint128"},{"astId":8,"contract":"fileA:A","label":"addr","offset":0,"slot":"2","type":"t_address"},{"astId":12,"contract":"fileA:A","label":"array","offset":0,"slot":"3","type":"t_array(t_uint256)2_storage"}],"types":{"t_address":{"encoding":"inplace","label":"address","numberOfBytes":"20"},"t_array(t_uint256)2_storage":{"base":"t_uint256","encoding":"inplace","label":"uint256[2]","numberOfBytes":"64"},"t_uint128":{"encoding":"inplace","label":"uint128","numberOfBytes":"16"},"t_uint256":{"encoding":"inplace","label":"uint256","numberOfBytes":"32"},"t_uint64":{"encoding":"inplace","label":"uint64","numberOfBytes":"8"}}}}}},"errors":[{"component":"general","formattedMessage":"fileA:1:1: Warning: Source file does not specify required compiler version!
contract A { uint64 x; uint128 y; uint128 z; address addr; uint[2] array; }
^-------------------------------------------------------------------------^
","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":75,"file":"fileA","start":0},"type":"Warning"}],"sources":{"fileA":{"id":0}}}

View File

@ -1,9 +1,14 @@
contract X {}
library test { library test {
struct StructType { uint a; } struct StructType { uint a; }
function f(StructType storage b, uint[] storage c, test d) public returns (uint[] memory e, StructType storage f) { f = f; } function f(StructType storage b, uint[] storage c, X d) public returns (uint[] memory e, StructType storage f) { f = f; }
function f1(uint[] memory c, test d) public pure returns (uint[] memory e) { } function f1(uint[] memory c, X d) public pure returns (uint[] memory e) { }
} }
// ---- // ----
// :X
// []
//
//
// :test // :test
// [ // [
// { // {
@ -15,9 +20,9 @@ library test {
// "type": "uint256[]" // "type": "uint256[]"
// }, // },
// { // {
// "internalType": "library test", // "internalType": "contract X",
// "name": "d", // "name": "d",
// "type": "test" // "type": "X"
// } // }
// ], // ],
// "name": "f1", // "name": "f1",

View File

@ -64,7 +64,7 @@ eth::AssemblyItems compileContract(std::shared_ptr<CharStream> _sourceCode)
map<ASTNode const*, shared_ptr<DeclarationContainer>> scopes; map<ASTNode const*, shared_ptr<DeclarationContainer>> scopes;
GlobalContext globalContext; GlobalContext globalContext;
NameAndTypeResolver resolver(globalContext, scopes, errorReporter); NameAndTypeResolver resolver(globalContext, dev::test::Options::get().evmVersion(), scopes, errorReporter);
solAssert(Error::containsOnlyWarnings(errorReporter.errors()), ""); solAssert(Error::containsOnlyWarnings(errorReporter.errors()), "");
resolver.registerDeclarations(*sourceUnit); resolver.registerDeclarations(*sourceUnit);
for (ASTPointer<ASTNode> const& node: sourceUnit->nodes()) for (ASTPointer<ASTNode> const& node: sourceUnit->nodes())

View File

@ -78,15 +78,34 @@ TestCase::TestResult SemanticTest::run(ostream& _stream, string const& _linePref
for (auto& test: m_tests) for (auto& test: m_tests)
test.reset(); test.reset();
map<string, dev::test::Address> libraries;
bool constructed = false;
for (auto& test: m_tests) for (auto& test: m_tests)
{ {
if (&test == &m_tests.front()) if (constructed)
if (test.call().isConstructor) {
deploy("", test.call().value, test.call().arguments.rawBytes()); soltestAssert(!test.call().isLibrary, "Libraries have to be deployed before any other call.");
else soltestAssert(!test.call().isConstructor, "Constructor has to be the first function call expect for library deployments.");
soltestAssert(deploy("", 0, bytes()), "Failed to deploy contract."); }
else if (test.call().isLibrary)
{
soltestAssert(
deploy(test.call().signature, 0, {}, libraries) && m_transactionSuccessful,
"Failed to deploy library " + test.call().signature
);
libraries[test.call().signature] = m_contractAddress;
continue;
}
else else
soltestAssert(!test.call().isConstructor, "Constructor has to be the first function call."); {
if (test.call().isConstructor)
deploy("", test.call().value, test.call().arguments.rawBytes(), libraries);
else
soltestAssert(deploy("", 0, bytes(), libraries), "Failed to deploy contract.");
constructed = true;
}
if (test.call().isConstructor) if (test.call().isConstructor)
{ {
@ -171,8 +190,8 @@ void SemanticTest::parseExpectations(istream& _stream)
std::move(functionCalls.begin(), functionCalls.end(), back_inserter(m_tests)); std::move(functionCalls.begin(), functionCalls.end(), back_inserter(m_tests));
} }
bool SemanticTest::deploy(string const& _contractName, u256 const& _value, bytes const& _arguments) bool SemanticTest::deploy(string const& _contractName, u256 const& _value, bytes const& _arguments, map<string, dev::test::Address> const& _libraries)
{ {
auto output = compileAndRunWithoutCheck(m_source, _value, _contractName, _arguments); auto output = compileAndRunWithoutCheck(m_source, _value, _contractName, _arguments, _libraries);
return !output.empty() && m_transactionSuccessful; return !output.empty() && m_transactionSuccessful;
} }

View File

@ -60,7 +60,7 @@ public:
/// Compiles and deploys currently held source. /// Compiles and deploys currently held source.
/// Returns true if deployment was successful, false otherwise. /// Returns true if deployment was successful, false otherwise.
bool deploy(std::string const& _contractName, u256 const& _value, bytes const& _arguments); bool deploy(std::string const& _contractName, u256 const& _value, bytes const& _arguments, std::map<std::string, dev::test::Address> const& _libraries = {});
private: private:
std::string m_source; std::string m_source;

View File

@ -118,7 +118,7 @@ bytes compileFirstExpression(
ErrorReporter errorReporter(errors); ErrorReporter errorReporter(errors);
GlobalContext globalContext; GlobalContext globalContext;
map<ASTNode const*, shared_ptr<DeclarationContainer>> scopes; map<ASTNode const*, shared_ptr<DeclarationContainer>> scopes;
NameAndTypeResolver resolver(globalContext, scopes, errorReporter); NameAndTypeResolver resolver(globalContext, dev::test::Options::get().evmVersion(), scopes, errorReporter);
resolver.registerDeclarations(*sourceUnit); resolver.registerDeclarations(*sourceUnit);
vector<ContractDefinition const*> inheritanceHierarchy; vector<ContractDefinition const*> inheritanceHierarchy;

View File

@ -0,0 +1,56 @@
library L {
function f(uint256 v) external pure returns (uint) {
return v * v;
}
function g(uint256 v) external returns (uint) {
return v * v;
}
}
contract C {
function addr() public view returns (bool) {
return address(L) == address(0);
}
function g(uint256 v) public view returns (uint256) {
return L.f(v);
}
function h(uint256 v) public returns (uint256) {
(bool success, bytes memory result) = address(L).delegatecall(abi.encodeWithSignature("f(uint256)", v));
assert(success);
return abi.decode(result, (uint256));
}
function i(uint256 v) public returns (uint256) {
(bool success, bytes memory result) = address(L).call(abi.encodeWithSignature("f(uint256)", v));
assert(success);
return abi.decode(result, (uint256));
}
function j(uint256 v) public returns (uint256) {
(bool success, bytes memory result) = address(L).delegatecall(abi.encodeWithSignature("g(uint256)", v));
assert(success);
return abi.decode(result, (uint256));
}
function k(uint256 v) public returns (uint256) {
(bool success, bytes memory result) = address(L).call(abi.encodeWithSignature("g(uint256)", v));
assert(success);
return abi.decode(result, (uint256));
}
}
// ====
// EVMVersion: >=byzantium
// ----
// library: L
// addr() -> false
// g(uint256): 1 -> 1
// g(uint256): 2 -> 4
// g(uint256): 4 -> 16
// h(uint256): 1 -> 1
// h(uint256): 2 -> 4
// h(uint256): 4 -> 16
// i(uint256): 1 -> 1
// i(uint256): 2 -> 4
// i(uint256): 4 -> 16
// j(uint256): 1 -> 1
// j(uint256): 2 -> 4
// j(uint256): 4 -> 16
// k(uint256): 1 -> FAILURE
// k(uint256): 2 -> FAILURE
// k(uint256): 4 -> FAILURE

View File

@ -0,0 +1,24 @@
library L {
function f(uint256 a, uint256 b) external {
assert(a * a == b);
}
}
contract C {
function addr() public view returns (bool) {
return address(L) == address(0);
}
function g(uint256 a, uint256 b) public returns (bool) {
(bool success,) = address(L).delegatecall(abi.encodeWithSignature("f(uint256,uint256)", a, b));
return success;
}
}
// ----
// library: L
// g(uint256,uint256): 1, 1 -> true
// g(uint256,uint256): 1, 2 -> false
// g(uint256,uint256): 2, 3 -> false
// g(uint256,uint256): 2, 4 -> true
// g(uint256,uint256): 2, 5 -> false
// g(uint256,uint256): 4, 15 -> false
// g(uint256,uint256): 4, 16 -> true
// g(uint256,uint256): 4, 17 -> false

View File

@ -0,0 +1,13 @@
library L {
function f(uint256 v) external returns (uint256) { return v*v; }
}
contract C {
function g(uint256 v) external returns (uint256) {
return L.f(v);
}
}
// ----
// library: L
// g(uint256): 1 -> 1
// g(uint256): 2 -> 4
// g(uint256): 4 -> 16

View File

@ -0,0 +1,12 @@
library L {
function f(uint256 v) internal returns (uint256) { return v*v; }
}
contract C {
function g(uint256 v) external returns (uint256) {
return L.f(v);
}
}
// ----
// g(uint256): 1 -> 1
// g(uint256): 2 -> 4
// g(uint256): 4 -> 16

View File

@ -12,17 +12,33 @@ contract C {
x = true; x = true;
require(true); require(true);
} }
/* Not properly supported by test system yet
function f2(bool a) public pure returns (bool x) { function f2(bool a) public pure returns (bool x) {
x = a; x = a;
string memory message; string memory message;
message = "fancy message!";
require(a, message); require(a, message);
}*/ }
function f3(bool a) public pure returns (bool x) {
x = a;
require(a, "msg");
}
function f4(bool a) public pure returns (bool x) {
x = a;
string memory message;
require(a, message);
}
} }
// ==== // ====
// compileViaYul: true // compileViaYul: also
// EVMVersion: >=byzantium
// ---- // ----
// f(bool): true -> true // f(bool): true -> true
// f(bool): false -> FAILURE // f(bool): false -> FAILURE
// fail() -> FAILURE // fail() -> FAILURE
// succeed() -> true // succeed() -> true
// f2(bool): true -> true
// f2(bool): false -> FAILURE, hex"08c379a0", 0x20, 14, "fancy message!"
// f3(bool): true -> true
// f3(bool): false -> FAILURE, hex"08c379a0", 0x20, 3, "msg"
// f4(bool): true -> true
// f4(bool): false -> FAILURE, hex"08c379a0", 0x20, 0

View File

@ -0,0 +1,10 @@
pragma experimental SMTChecker;
contract C {
function f(address a, function(uint) external g) internal pure {
address b = address(g);
assert(a == b);
}
}
// ----
// Warning: (128-138): Type conversion is not yet fully supported and might yield false positives.
// Warning: (142-156): Assertion violation happens here

View File

@ -0,0 +1,8 @@
pragma experimental SMTChecker;
contract C {
function f(function(uint) external returns (uint) g, function(uint) external returns (uint) h) public {
assert(g(2) == h(2));
}
}
// ----
// Warning: (155-175): Assertion violation happens here

View File

@ -0,0 +1,13 @@
pragma experimental SMTChecker;
contract C {
function f(function(uint) returns (uint) g, function(uint) returns (uint) h) internal {
assert(g(2) == h(2));
assert(g == h);
}
}
// ----
// Warning: (146-150): Assertion checker does not yet implement this type of function call.
// Warning: (154-158): Assertion checker does not yet implement this type of function call.
// Warning: (170-176): Assertion checker does not yet implement the type function (uint256) returns (uint256) for comparisons
// Warning: (139-159): Assertion violation happens here
// Warning: (163-177): Assertion violation happens here

View File

@ -0,0 +1,14 @@
pragma experimental SMTChecker;
contract B {
function f() pure public {
g("0123456");
}
function g(bytes7 a) pure public {
assert(a == "0123456");
assert(a == "1234567");
}
}
// ----
// Warning: (162-184): Assertion violation happens here
// Warning: (136-158): Assertion violation happens here
// Warning: (162-184): Assertion violation happens here

View File

@ -0,0 +1,11 @@
pragma experimental SMTChecker;
contract B {
function f() mod2("0123456") pure public { }
modifier mod2(bytes7 a) {
assert(a == "0123456");
assert(a == "1234567");
_;
}
}
// ----
// Warning: (152-174): Assertion violation happens here

View File

@ -0,0 +1,12 @@
pragma experimental SMTChecker;
contract C {
function g() public pure returns (bytes32 val) { return "abc"; }
function f1() public pure returns (bytes32 val) { return g(); }
function a() public pure {
assert(f1() == "abc");
assert(f1() == "cde");
}
}
// ----
// Warning: (238-259): Assertion violation happens here

View File

@ -0,0 +1,15 @@
pragma experimental SMTChecker;
contract C {
function h() public pure returns (bytes32 val, bytes3 val2) { return ("abc", "def"); }
function g() public pure returns (bytes32 val) { return "abc"; }
function f1() public pure returns (bytes32 val) { return g(); }
function f2() public pure returns (bytes32 val, bytes3 val2) { return h(); }
function a() public pure {
(bytes32 v1, bytes3 v2) = f2();
assert(v1 == "abc");
assert(v2 == "cde");
}
}
// ----
// Warning: (442-461): Assertion violation happens here

View File

@ -0,0 +1,5 @@
pragma experimental SMTChecker;
contract C {
function f(function(uint) external g) public {
}
}

View File

@ -0,0 +1,13 @@
pragma experimental SMTChecker;
contract C {
function(uint) m_g;
function f(function(uint) internal g) internal {
g(2);
}
function h() public {
f(m_g);
}
}
// ----
// Warning: (121-125): Assertion checker does not yet implement this type of function call.
// Warning: (121-125): Assertion checker does not yet implement this type of function call.

View File

@ -0,0 +1,11 @@
pragma experimental SMTChecker;
contract C {
function f(function(uint) external payable g) internal {
g.selector;
g.gas(2).value(3)(4);
}
}
// ----
// Warning: (108-118): Assertion checker does not yet support this expression.
// Warning: (122-130): Assertion checker does not yet implement this type of function call.
// Warning: (122-139): Assertion checker does not yet implement this type of function call.

View File

@ -0,0 +1,22 @@
pragma experimental SMTChecker;
contract C {
function(uint) m_g;
function f1(function(uint) internal g1) internal {
g1(2);
}
function f2(function(function(uint) internal) internal g2) internal {
g2(m_g);
}
function h() public {
f2(f1);
}
}
// ----
// Warning: (123-128): Assertion checker does not yet implement this type of function call.
// Warning: (152-197): Assertion checker does not yet support the type of this variable.
// Warning: (212-214): Assertion checker does not yet implement type function (function (uint256))
// Warning: (212-219): Assertion checker does not yet implement this type of function call.
// Warning: (255-257): Internal error: Expression undefined for SMT solver.
// Warning: (255-257): Assertion checker does not yet implement type function (function (uint256))
// Warning: (212-214): Assertion checker does not yet implement type function (function (uint256))
// Warning: (212-219): Assertion checker does not yet implement this type of function call.

View File

@ -0,0 +1,27 @@
pragma experimental SMTChecker;
contract C {
function(uint) m_g;
function r() internal view returns (function(uint)) {
return m_g;
}
function f1(function(uint) internal g1) internal {
g1(2);
}
function f2(function(function(uint) internal) internal g2) internal {
g2(r());
}
function h() public {
f2(f1);
}
}
// ----
// Warning: (195-200): Assertion checker does not yet implement this type of function call.
// Warning: (224-269): Assertion checker does not yet support the type of this variable.
// Warning: (284-286): Assertion checker does not yet implement type function (function (uint256))
// Warning: (287-288): Assertion checker does not yet support this global variable.
// Warning: (284-291): Assertion checker does not yet implement this type of function call.
// Warning: (327-329): Internal error: Expression undefined for SMT solver.
// Warning: (327-329): Assertion checker does not yet implement type function (function (uint256))
// Warning: (284-286): Assertion checker does not yet implement type function (function (uint256))
// Warning: (287-288): Assertion checker does not yet support this global variable.
// Warning: (284-291): Assertion checker does not yet implement this type of function call.

View File

@ -1,12 +1,12 @@
contract C { contract C {
function f() pure external { function f() pure external returns (uint id) {
assembly { assembly {
pop(chainid()) id := chainid()
} }
} }
function g() view external { function g() view external returns (uint sb) {
assembly { assembly {
pop(selfbalance()) sb := selfbalance()
} }
} }
} }

View File

@ -0,0 +1,17 @@
contract C {
function f() pure external returns (uint id) {
assembly {
id := chainid()
}
}
function g() view external returns (uint sb) {
assembly {
sb := selfbalance()
}
}
}
// ====
// EVMVersion: =petersburg
// ----
// TypeError: (101-110): The "chainid" instruction is only available for Istanbul-compatible VMs (you are currently compiling for "petersburg").
// TypeError: (215-228): The "selfbalance" instruction is only available for Istanbul-compatible VMs (you are currently compiling for "petersburg").

View File

@ -6,4 +6,5 @@ contract test {
} }
} }
// ---- // ----
// TypeError: (87-90): The type of a variable cannot be a library.
// TypeError: (100-103): Member "l" not found or not visible after argument-dependent lookup in library L. // TypeError: (100-103): Member "l" not found or not visible after argument-dependent lookup in library L.

View File

@ -0,0 +1,14 @@
library X { }
contract Y {
X abc;
function foo(X param) private view
{
X ofg;
ofg = abc;
}
}
// ----
// TypeError: (29-34): The type of a variable cannot be a library.
// TypeError: (50-57): The type of a variable cannot be a library.
// TypeError: (77-82): The type of a variable cannot be a library.

View File

@ -0,0 +1,8 @@
library L {
}
contract C {
function f() public pure returns (address) {
return address(L);
}
}
// ----

View File

@ -0,0 +1,9 @@
library L {
}
contract C {
function f() public pure returns (address payable) {
return address(L);
}
}
// ----
// TypeError: (99-109): Return argument type address is not implicitly convertible to expected type (type of first return variable) address payable.

View File

@ -58,6 +58,7 @@ namespace test
K(Boolean, "boolean", 0) \ K(Boolean, "boolean", 0) \
/* special keywords */ \ /* special keywords */ \
K(Left, "left", 0) \ K(Left, "left", 0) \
K(Library, "library", 0) \
K(Right, "right", 0) \ K(Right, "right", 0) \
K(Failure, "FAILURE", 0) \ K(Failure, "FAILURE", 0) \
@ -268,6 +269,8 @@ struct FunctionCall
/// Marks this function call as "short-handed", meaning /// Marks this function call as "short-handed", meaning
/// no `->` declared. /// no `->` declared.
bool omitsArrow = true; bool omitsArrow = true;
/// Marks a library deployment call.
bool isLibrary = false;
}; };
} }

View File

@ -75,44 +75,56 @@ vector<dev::solidity::test::FunctionCall> TestFileParser::parseFunctionCalls(siz
try try
{ {
tie(call.signature, call.useCallWithoutSignature) = parseFunctionSignature(); if (accept(Token::Library, true))
if (accept(Token::Comma, true))
call.value = parseFunctionCallValue();
if (accept(Token::Colon, true))
call.arguments = parseFunctionCallArguments();
if (accept(Token::Newline, true))
{ {
call.displayMode = FunctionCall::DisplayMode::MultiLine; expect(Token::Colon);
m_lineNumber++; call.signature = m_scanner.currentLiteral();
} expect(Token::Identifier);
call.isLibrary = true;
call.arguments.comment = parseComment(); call.expectations.failure = false;
if (accept(Token::Newline, true))
{
call.displayMode = FunctionCall::DisplayMode::MultiLine;
m_lineNumber++;
}
if (accept(Token::Arrow, true))
{
call.omitsArrow = false;
call.expectations = parseFunctionCallExpectations();
if (accept(Token::Newline, true))
m_lineNumber++;
} }
else else
{ {
call.expectations.failure = false; tie(call.signature, call.useCallWithoutSignature) = parseFunctionSignature();
call.displayMode = FunctionCall::DisplayMode::SingleLine; if (accept(Token::Comma, true))
call.value = parseFunctionCallValue();
if (accept(Token::Colon, true))
call.arguments = parseFunctionCallArguments();
if (accept(Token::Newline, true))
{
call.displayMode = FunctionCall::DisplayMode::MultiLine;
m_lineNumber++;
}
call.arguments.comment = parseComment();
if (accept(Token::Newline, true))
{
call.displayMode = FunctionCall::DisplayMode::MultiLine;
m_lineNumber++;
}
if (accept(Token::Arrow, true))
{
call.omitsArrow = false;
call.expectations = parseFunctionCallExpectations();
if (accept(Token::Newline, true))
m_lineNumber++;
}
else
{
call.expectations.failure = false;
call.displayMode = FunctionCall::DisplayMode::SingleLine;
}
call.expectations.comment = parseComment();
if (call.signature == "constructor()")
call.isConstructor = true;
} }
call.expectations.comment = parseComment();
if (call.signature == "constructor()")
call.isConstructor = true;
calls.emplace_back(std::move(call)); calls.emplace_back(std::move(call));
} }
catch (Error const& _e) catch (Error const& _e)
@ -456,6 +468,7 @@ void TestFileParser::Scanner::scanNextToken()
if (_literal == "false") return TokenDesc{Token::Boolean, _literal}; if (_literal == "false") return TokenDesc{Token::Boolean, _literal};
if (_literal == "ether") return TokenDesc{Token::Ether, _literal}; if (_literal == "ether") return TokenDesc{Token::Ether, _literal};
if (_literal == "left") return TokenDesc{Token::Left, _literal}; if (_literal == "left") return TokenDesc{Token::Left, _literal};
if (_literal == "library") return TokenDesc{Token::Library, _literal};
if (_literal == "right") return TokenDesc{Token::Right, _literal}; if (_literal == "right") return TokenDesc{Token::Right, _literal};
if (_literal == "hex") return TokenDesc{Token::Hex, _literal}; if (_literal == "hex") return TokenDesc{Token::Hex, _literal};
if (_literal == "FAILURE") return TokenDesc{Token::Failure, _literal}; if (_literal == "FAILURE") return TokenDesc{Token::Failure, _literal};

View File

@ -57,7 +57,9 @@ void testFunctionCall(
u256 _value = 0, u256 _value = 0,
string _argumentComment = "", string _argumentComment = "",
string _expectationComment = "", string _expectationComment = "",
vector<string> _rawArguments = vector<string>{} vector<string> _rawArguments = vector<string>{},
bool _isConstructor = false,
bool _isLibrary = false
) )
{ {
BOOST_REQUIRE_EQUAL(_call.expectations.failure, _failure); BOOST_REQUIRE_EQUAL(_call.expectations.failure, _failure);
@ -79,6 +81,9 @@ void testFunctionCall(
++index; ++index;
} }
} }
BOOST_REQUIRE_EQUAL(_call.isConstructor, _isConstructor);
BOOST_REQUIRE_EQUAL(_call.isLibrary, _isLibrary);
} }
BOOST_AUTO_TEST_SUITE(TestFileParserTest) BOOST_AUTO_TEST_SUITE(TestFileParserTest)
@ -883,6 +888,51 @@ BOOST_AUTO_TEST_CASE(call_unexpected_character)
BOOST_REQUIRE_THROW(parse(source), langutil::Error); BOOST_REQUIRE_THROW(parse(source), langutil::Error);
} }
BOOST_AUTO_TEST_CASE(constructor)
{
char const* source = R"(
// constructor()
)";
auto const calls = parse(source);
BOOST_REQUIRE_EQUAL(calls.size(), 1);
testFunctionCall(
calls.at(0),
Mode::SingleLine,
"constructor()",
false,
{},
{},
0,
"",
"",
{},
true
);
}
BOOST_AUTO_TEST_CASE(library)
{
char const* source = R"(
// library: L
)";
auto const calls = parse(source);
BOOST_REQUIRE_EQUAL(calls.size(), 1);
testFunctionCall(
calls.at(0),
Mode::SingleLine,
"L",
false,
{},
{},
0,
"",
"",
{},
false,
true
);
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
} }

View File

@ -55,6 +55,12 @@ string TestFunctionCall::format(
string newline = formatToken(Token::Newline); string newline = formatToken(Token::Newline);
string failure = formatToken(Token::Failure); string failure = formatToken(Token::Failure);
if (m_call.isLibrary)
{
stream << _linePrefix << newline << ws << "library:" << ws << m_call.signature;
return;
}
/// Formats the function signature. This is the same independent from the display-mode. /// Formats the function signature. This is the same independent from the display-mode.
stream << _linePrefix << newline << ws << m_call.signature; stream << _linePrefix << newline << ws << m_call.signature;
if (m_call.value > u256(0)) if (m_call.value > u256(0))