aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt2
-rw-r--r--Changelog.md31
-rw-r--r--docs/abi-spec.rst3
-rw-r--r--docs/assembly.rst27
-rw-r--r--docs/bugs.json19
-rw-r--r--docs/bugs_by_version.json69
-rw-r--r--docs/contracts.rst4
-rw-r--r--docs/control-structures.rst3
-rw-r--r--docs/frequently-asked-questions.rst2
-rw-r--r--docs/installing-solidity.rst14
-rw-r--r--docs/introduction-to-smart-contracts.rst4
-rw-r--r--docs/miscellaneous.rst2
-rw-r--r--docs/solidity-by-example.rst6
-rw-r--r--docs/types.rst5
-rw-r--r--libdevcore/CommonData.h7
-rw-r--r--libdevcore/IndentedWriter.cpp65
-rw-r--r--libdevcore/IndentedWriter.h67
-rw-r--r--libdevcore/Whiskers.cpp2
-rw-r--r--libsolidity/analysis/DeclarationContainer.h1
-rw-r--r--libsolidity/analysis/NameAndTypeResolver.cpp10
-rw-r--r--libsolidity/analysis/NameAndTypeResolver.h2
-rw-r--r--libsolidity/analysis/StaticAnalyzer.cpp42
-rw-r--r--libsolidity/analysis/StaticAnalyzer.h3
-rw-r--r--libsolidity/analysis/SyntaxChecker.cpp43
-rw-r--r--libsolidity/analysis/SyntaxChecker.h2
-rw-r--r--libsolidity/analysis/TypeChecker.cpp98
-rw-r--r--libsolidity/analysis/TypeChecker.h3
-rw-r--r--libsolidity/ast/AST.cpp11
-rw-r--r--libsolidity/ast/AST.h52
-rw-r--r--libsolidity/ast/ASTAnnotations.h7
-rw-r--r--libsolidity/ast/ASTEnums.h52
-rw-r--r--libsolidity/ast/ASTForward.h1
-rw-r--r--libsolidity/ast/ASTJsonConverter.cpp29
-rw-r--r--libsolidity/ast/ASTJsonConverter.h1
-rw-r--r--libsolidity/ast/ExperimentalFeatures.h50
-rw-r--r--libsolidity/ast/Types.cpp152
-rw-r--r--libsolidity/ast/Types.h30
-rw-r--r--libsolidity/codegen/ABIFunctions.cpp1074
-rw-r--r--libsolidity/codegen/ABIFunctions.h172
-rw-r--r--libsolidity/codegen/CompilerContext.cpp5
-rw-r--r--libsolidity/codegen/CompilerContext.h11
-rw-r--r--libsolidity/codegen/CompilerUtils.cpp35
-rw-r--r--libsolidity/codegen/CompilerUtils.h8
-rw-r--r--libsolidity/codegen/ContractCompiler.cpp13
-rw-r--r--libsolidity/codegen/ExpressionCompiler.cpp17
-rw-r--r--libsolidity/interface/ABI.cpp3
-rw-r--r--libsolidity/interface/CompilerStack.cpp38
-rw-r--r--libsolidity/interface/ErrorReporter.cpp10
-rw-r--r--libsolidity/interface/ErrorReporter.h6
-rw-r--r--libsolidity/interface/SourceReferenceFormatter.cpp5
-rw-r--r--libsolidity/parsing/Parser.cpp148
-rw-r--r--libsolidity/parsing/Parser.h11
-rw-r--r--libsolidity/parsing/Token.h3
-rw-r--r--solc/jsonCompiler.cpp5
-rw-r--r--solc/main.cpp2
-rw-r--r--test/boostTest.cpp1
-rw-r--r--test/libdevcore/IndentedWriter.cpp75
-rw-r--r--test/libsolidity/ABIEncoderTests.cpp405
-rw-r--r--test/libsolidity/Imports.cpp9
-rw-r--r--test/libsolidity/Metadata.cpp30
-rw-r--r--test/libsolidity/SolidityABIJSON.cpp54
-rw-r--r--test/libsolidity/SolidityEndToEndTest.cpp316
-rw-r--r--test/libsolidity/SolidityNameAndTypeResolution.cpp641
-rw-r--r--test/libsolidity/SolidityParser.cpp72
64 files changed, 3573 insertions, 517 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index b29bc414..a11c56ee 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -8,7 +8,7 @@ include(EthPolicy)
eth_policy()
# project name and version should be set after cmake_policy CMP0048
-set(PROJECT_VERSION "0.4.14")
+set(PROJECT_VERSION "0.4.16")
project(solidity VERSION ${PROJECT_VERSION})
# Let's find our dependencies
diff --git a/Changelog.md b/Changelog.md
index 18fd00ea..9bc0b354 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -1,7 +1,35 @@
-### 0.4.14 (unreleased)
+### 0.4.16 (unreleased)
+
+Features:
+ * ABI JSON: Include new field ``statemutability`` with values ``view``, ``nonpayable`` and ``payable``.
+ * Parser: Display previous visibility specifier in error if multiple are found.
+ * Syntax Checker: Support ``pragma experimental <feature>;`` to turn on experimental features.
+ * Static Analyzer: Warn about large storage structures.
+ * Metadata: Store experimental flag in metadata CBOR.
+ * Type Checker: More detailed error message for invalid overrides.
+
+Bugfixes:
+ * Parser: Enforce commas between array and tuple elements.
+ * Parser: Limit maximum recursion depth.
+
+### 0.4.15 (2017-08-08)
+
+Features:
+ * Type Checker: Show unimplemented function if trying to instantiate an abstract class.
+
+Bugfixes:
+ * Code Generator: ``.delegatecall()`` should always return execution outcome.
+ * Code Generator: Provide "new account gas" for low-level ``callcode`` and ``delegatecall``.
+ * Type Checker: Constructors must be implemented if declared.
+ * Type Checker: Disallow the ``.gas()`` modifier on ``ecrecover``, ``sha256`` and ``ripemd160``.
+ * Type Checker: Do not mark overloaded functions as shadowing other functions.
+ * Type Checker: Internal library functions must be implemented if declared.
+
+### 0.4.14 (2017-07-31)
Features:
* C API (``jsonCompiler``): Export the ``license`` method.
+ * Code Generator: Optimise the fallback function, by removing a useless jump.
* Inline Assembly: Show useful error message if trying to access calldata variables.
* Inline Assembly: Support variable declaration without initial value (defaults to 0).
* Metadata: Only include files which were used to compile the given contract.
@@ -12,6 +40,7 @@ Features:
* Type checker: Warn when existing symbols, including builtins, are overwritten.
Bugfixes:
+ * Code Generator: Properly clear return memory area for ecrecover.
* Type Checker: Fix crash for some assignment to non-lvalue.
* Type Checker: Fix invalid "specify storage keyword" warning for reference members of structs.
* Type Checker: Mark modifiers as internal.
diff --git a/docs/abi-spec.rst b/docs/abi-spec.rst
index 2cf57427..7a1a9af0 100644
--- a/docs/abi-spec.rst
+++ b/docs/abi-spec.rst
@@ -294,7 +294,8 @@ The JSON format for a contract's interface is given by an array of function and/
* `type`: the canonical type of the parameter.
- `outputs`: an array of objects similar to `inputs`, can be omitted if function doesn't return anything;
- `constant`: `true` if function is :ref:`specified to not modify blockchain state <constant-functions>`);
-- `payable`: `true` if function accepts ether, defaults to `false`.
+- `payable`: `true` if function accepts ether, defaults to `false`;
+- `statemutability`: a string with one of the following values: `view` (same as `constant` above), `nonpayable` and `payable` (same as `payable` above).
`type` can be omitted, defaulting to `"function"`.
diff --git a/docs/assembly.rst b/docs/assembly.rst
index a7cddb18..6495699f 100644
--- a/docs/assembly.rst
+++ b/docs/assembly.rst
@@ -96,6 +96,31 @@ you really know what you are doing.
}
}
}
+
+ // Same as above, but accomplish the entire code within inline assembly.
+ function sumPureAsm(uint[] _data) returns (uint o_sum) {
+ assembly {
+ // Load the length (first 32 bytes)
+ let len := mload(_data)
+
+ // Skip over the length field.
+ //
+ // Keep temporary variable so it can be incremented in place.
+ //
+ // NOTE: incrementing _data would result in an unusable
+ // _data variable after this assembly block
+ let data := add(_data, 0x20)
+
+ // Iterate until the bound is not met.
+ for
+ { let end := add(data, len) }
+ lt(data, end)
+ { data := add(data, 0x20) }
+ {
+ o_sum := add(o_sum, mload(data))
+ }
+ }
+ }
}
@@ -125,7 +150,7 @@ following list can be used as a reference of its opcodes.
If an opcode takes arguments (always from the top of the stack), they are given in parentheses.
Note that the order of arguments can be seen to be reversed in non-functional style (explained below).
Opcodes marked with ``-`` do not push an item onto the stack, those marked with ``*`` are
-special and all others push exactly one item onte the stack.
+special and all others push exactly one item onto the stack.
In the following, ``mem[a...b)`` signifies the bytes of memory starting at position ``a`` up to
(excluding) position ``b`` and ``storage[p]`` signifies the storage contents at position ``p``.
diff --git a/docs/bugs.json b/docs/bugs.json
index a0c0e7c4..ac322a48 100644
--- a/docs/bugs.json
+++ b/docs/bugs.json
@@ -1,7 +1,22 @@
[
{
+ "name": "DelegateCallReturnValue",
+ "summary": "The low-level .delegatecall() does not return the execution outcome, but converts the value returned by the functioned called to a boolean instead.",
+ "description": "The return value of the low-level .delegatecall() function is taken from a position in memory, where the call data or the return data resides. This value is interpreted as a boolean and put onto the stack. This means if the called function returns at least 32 zero bytes, .delegatecall() returns false even if the call was successuful.",
+ "introduced": "0.3.0",
+ "fixed": "0.4.15",
+ "severity": "low"
+ },
+ {
+ "name": "ECRecoverMalformedInput",
+ "summary": "The ecrecover() builtin can return garbage for malformed input.",
+ "description": "The ecrecover precompile does not properly signal failure for malformed input (especially in the 'v' argument) and thus the Solidity function can return data that was previously present in the return area in memory.",
+ "fixed": "0.4.14",
+ "severity": "medium"
+ },
+ {
"name": "SkipEmptyStringLiteral",
- "summary": "If \"\" is used in a function call, the following function arguments will not be correctly passed to the function.",
+ "summary": "If \"\" is used in a function call, the following function arguments will not be correctly passed to the function.",
"description": "If the empty string literal \"\" is used as an argument in a function call, it is skipped by the encoder. This has the effect that the encoding of all arguments following this is shifted left by 32 bytes and thus the function call data is corrupted.",
"fixed": "0.4.12",
"severity": "low"
@@ -107,4 +122,4 @@
"severity": "high",
"fixed": "0.3.0"
}
-] \ No newline at end of file
+]
diff --git a/docs/bugs_by_version.json b/docs/bugs_by_version.json
index d6802eec..33f7bae9 100644
--- a/docs/bugs_by_version.json
+++ b/docs/bugs_by_version.json
@@ -1,6 +1,7 @@
{
"0.1.0": {
"bugs": [
+ "ECRecoverMalformedInput",
"SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored",
@@ -16,6 +17,7 @@
},
"0.1.1": {
"bugs": [
+ "ECRecoverMalformedInput",
"SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored",
@@ -31,6 +33,7 @@
},
"0.1.2": {
"bugs": [
+ "ECRecoverMalformedInput",
"SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored",
@@ -46,6 +49,7 @@
},
"0.1.3": {
"bugs": [
+ "ECRecoverMalformedInput",
"SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored",
@@ -61,6 +65,7 @@
},
"0.1.4": {
"bugs": [
+ "ECRecoverMalformedInput",
"SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored",
@@ -76,6 +81,7 @@
},
"0.1.5": {
"bugs": [
+ "ECRecoverMalformedInput",
"SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored",
@@ -91,6 +97,7 @@
},
"0.1.6": {
"bugs": [
+ "ECRecoverMalformedInput",
"SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored",
@@ -107,6 +114,7 @@
},
"0.1.7": {
"bugs": [
+ "ECRecoverMalformedInput",
"SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored",
@@ -123,6 +131,7 @@
},
"0.2.0": {
"bugs": [
+ "ECRecoverMalformedInput",
"SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored",
@@ -139,6 +148,7 @@
},
"0.2.1": {
"bugs": [
+ "ECRecoverMalformedInput",
"SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored",
@@ -155,6 +165,7 @@
},
"0.2.2": {
"bugs": [
+ "ECRecoverMalformedInput",
"SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored",
@@ -171,6 +182,8 @@
},
"0.3.0": {
"bugs": [
+ "DelegateCallReturnValue",
+ "ECRecoverMalformedInput",
"SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored",
@@ -186,6 +199,8 @@
},
"0.3.1": {
"bugs": [
+ "DelegateCallReturnValue",
+ "ECRecoverMalformedInput",
"SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored",
@@ -200,6 +215,8 @@
},
"0.3.2": {
"bugs": [
+ "DelegateCallReturnValue",
+ "ECRecoverMalformedInput",
"SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored",
@@ -214,6 +231,8 @@
},
"0.3.3": {
"bugs": [
+ "DelegateCallReturnValue",
+ "ECRecoverMalformedInput",
"SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored",
@@ -227,6 +246,8 @@
},
"0.3.4": {
"bugs": [
+ "DelegateCallReturnValue",
+ "ECRecoverMalformedInput",
"SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored",
@@ -240,6 +261,8 @@
},
"0.3.5": {
"bugs": [
+ "DelegateCallReturnValue",
+ "ECRecoverMalformedInput",
"SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored",
@@ -253,6 +276,8 @@
},
"0.3.6": {
"bugs": [
+ "DelegateCallReturnValue",
+ "ECRecoverMalformedInput",
"SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored",
@@ -264,6 +289,8 @@
},
"0.4.0": {
"bugs": [
+ "DelegateCallReturnValue",
+ "ECRecoverMalformedInput",
"SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored",
@@ -275,6 +302,8 @@
},
"0.4.1": {
"bugs": [
+ "DelegateCallReturnValue",
+ "ECRecoverMalformedInput",
"SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored",
@@ -286,6 +315,8 @@
},
"0.4.10": {
"bugs": [
+ "DelegateCallReturnValue",
+ "ECRecoverMalformedInput",
"SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction"
],
@@ -293,20 +324,40 @@
},
"0.4.11": {
"bugs": [
+ "DelegateCallReturnValue",
+ "ECRecoverMalformedInput",
"SkipEmptyStringLiteral"
],
"released": "2017-05-03"
},
"0.4.12": {
- "bugs": [],
+ "bugs": [
+ "DelegateCallReturnValue",
+ "ECRecoverMalformedInput"
+ ],
"released": "2017-07-03"
},
"0.4.13": {
- "bugs": [],
+ "bugs": [
+ "DelegateCallReturnValue",
+ "ECRecoverMalformedInput"
+ ],
"released": "2017-07-06"
},
+ "0.4.14": {
+ "bugs": [
+ "DelegateCallReturnValue"
+ ],
+ "released": "2017-07-31"
+ },
+ "0.4.15": {
+ "bugs": [],
+ "released": "2017-08-08"
+ },
"0.4.2": {
"bugs": [
+ "DelegateCallReturnValue",
+ "ECRecoverMalformedInput",
"SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored",
@@ -317,6 +368,8 @@
},
"0.4.3": {
"bugs": [
+ "DelegateCallReturnValue",
+ "ECRecoverMalformedInput",
"SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored",
@@ -326,6 +379,8 @@
},
"0.4.4": {
"bugs": [
+ "DelegateCallReturnValue",
+ "ECRecoverMalformedInput",
"SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored"
@@ -334,6 +389,8 @@
},
"0.4.5": {
"bugs": [
+ "DelegateCallReturnValue",
+ "ECRecoverMalformedInput",
"SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored",
@@ -343,6 +400,8 @@
},
"0.4.6": {
"bugs": [
+ "DelegateCallReturnValue",
+ "ECRecoverMalformedInput",
"SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored"
@@ -351,6 +410,8 @@
},
"0.4.7": {
"bugs": [
+ "DelegateCallReturnValue",
+ "ECRecoverMalformedInput",
"SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction"
],
@@ -358,6 +419,8 @@
},
"0.4.8": {
"bugs": [
+ "DelegateCallReturnValue",
+ "ECRecoverMalformedInput",
"SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction"
],
@@ -365,6 +428,8 @@
},
"0.4.9": {
"bugs": [
+ "DelegateCallReturnValue",
+ "ECRecoverMalformedInput",
"SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction"
],
diff --git a/docs/contracts.rst b/docs/contracts.rst
index 4c3d4059..93671691 100644
--- a/docs/contracts.rst
+++ b/docs/contracts.rst
@@ -10,7 +10,7 @@ variables. Calling a function on a different contract (instance) will perform
an EVM function call and thus switch the context such that state variables are
inaccessible.
-.. index:: ! contract;creation
+.. index:: ! contract;creation, constructor
******************
Creating Contracts
@@ -246,7 +246,7 @@ In the following example, ``D``, can call ``c.getData()`` to retrieve the value
}
.. index:: ! getter;function, ! function;getter
-.. _getter_functions:
+.. _getter-functions:
Getter Functions
================
diff --git a/docs/control-structures.rst b/docs/control-structures.rst
index a7af69f5..796e9238 100644
--- a/docs/control-structures.rst
+++ b/docs/control-structures.rst
@@ -393,6 +393,9 @@ When exceptions happen in a sub-call, they "bubble up" (i.e. exceptions are reth
and the low-level functions ``call``, ``delegatecall`` and ``callcode`` -- those return ``false`` in case
of an exception instead of "bubbling up".
+.. warning::
+ The low-level ``call``, ``delegatecall`` and ``callcode`` will return success if the calling account is non-existent, as part of the design of EVM. Existence must be checked prior to calling if desired.
+
Catching exceptions is not yet possible.
In the following example, you can see how ``require`` can be used to easily check conditions on inputs
diff --git a/docs/frequently-asked-questions.rst b/docs/frequently-asked-questions.rst
index 73210991..5f1a981e 100644
--- a/docs/frequently-asked-questions.rst
+++ b/docs/frequently-asked-questions.rst
@@ -658,7 +658,7 @@ Not yet, as this requires two levels of dynamic arrays (``string`` is a dynamic
If you issue a call for an array, it is possible to retrieve the whole array? Or must you write a helper function for that?
===========================================================================================================================
-The automatic getter function for a public state variable of array type only returns
+The automatic :ref:`getter function<getter-functions>` for a public state variable of array type only returns
individual elements. If you want to return the complete array, you have to
manually write a function to do that.
diff --git a/docs/installing-solidity.rst b/docs/installing-solidity.rst
index e07561c5..ddc5c850 100644
--- a/docs/installing-solidity.rst
+++ b/docs/installing-solidity.rst
@@ -56,7 +56,7 @@ repository contains potentially unstable changes in the develop branch.
docker run ethereum/solc:stable solc --version
-Currenty, the docker image only contains the compiler executable,
+Currently, the docker image only contains the compiler executable,
so you have to do some additional work to link in the source and
output directories.
@@ -83,7 +83,15 @@ If you want to use the cutting edge developer version:
sudo apt-get update
sudo apt-get install solc
-We are also releasing a `snap package <https://snapcraft.io/>`_, which is installable in all the `supported Linux distros <https://snapcraft.io/docs/core/install>`_. To help testing the unstable solc with the most recent changes from the development branch:
+We are also releasing a `snap package <https://snapcraft.io/>`_, which is installable in all the `supported Linux distros <https://snapcraft.io/docs/core/install>`_. To install the latest stable version of solc:
+
+.. code:: bash
+
+ sudo snap install solc
+
+Or if you want to help testing the unstable solc with the most recent changes from the development branch:
+
+.. code:: bash
sudo snap install solc --edge
@@ -127,7 +135,7 @@ Gentoo Linux also provides a solidity package that can be installed using ``emer
.. code:: bash
- demerge ev-lang/solidity
+ emerge dev-lang/solidity
.. _building-from-source:
diff --git a/docs/introduction-to-smart-contracts.rst b/docs/introduction-to-smart-contracts.rst
index dc7c6cc9..1a3cf638 100644
--- a/docs/introduction-to-smart-contracts.rst
+++ b/docs/introduction-to-smart-contracts.rst
@@ -35,7 +35,7 @@ Solidity version 0.4.0 or anything newer that does not break functionality
(up to, but not including, version 0.5.0). This is to ensure that the
contract does not suddenly behave differently with a new compiler version. The keyword ``pragma`` is called that way because, in general,
pragmas are instructions for the compiler about how to treat the
-source code (e.g. `pragma once <https://en.wikipedia.org/wiki/Pragma_once>`_). .
+source code (e.g. `pragma once <https://en.wikipedia.org/wiki/Pragma_once>`_).
A contract in the sense of Solidity is a collection of code (its *functions*) and
data (its *state*) that resides at a specific address on the Ethereum
@@ -133,7 +133,7 @@ too far, though, as it is neither possible to obtain a list of all keys of
a mapping, nor a list of all values. So either keep in mind (or
better, keep a list or use a more advanced data type) what you
added to the mapping or use it in a context where this is not needed,
-like this one. The getter function created by the ``public`` keyword
+like this one. The :ref:`getter function<getter-functions>` created by the ``public`` keyword
is a bit more complex in this case. It roughly looks like the
following::
diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst
index e364bee7..199182d3 100644
--- a/docs/miscellaneous.rst
+++ b/docs/miscellaneous.rst
@@ -494,7 +494,7 @@ Function Visibility Specifiers
return true;
}
-- ``public``: visible externally and internally (creates getter function for storage/state variables)
+- ``public``: visible externally and internally (creates a :ref:`getter function<getter-functions>` for storage/state variables)
- ``private``: only visible in the current contract
- ``external``: only visible externally (only for functions) - i.e. can only be message-called (via ``this.func``)
- ``internal``: only visible internally
diff --git a/docs/solidity-by-example.rst b/docs/solidity-by-example.rst
index 71d27192..dde4495b 100644
--- a/docs/solidity-by-example.rst
+++ b/docs/solidity-by-example.rst
@@ -277,9 +277,9 @@ activate themselves.
if (highestBidder != 0) {
// Sending back the money by simply using
// highestBidder.send(highestBid) is a security risk
- // because it can be prevented by the caller by e.g.
- // raising the call stack to 1023. It is always safer
- // to let the recipients withdraw their money themselves.
+ // because it could execute an untrusted contract.
+ // It is always safer to let the recipients
+ // withdraw their money themselves.
pendingReturns[highestBidder] += highestBid;
}
highestBidder = msg.sender;
diff --git a/docs/types.rst b/docs/types.rst
index dd9c6269..403d032c 100644
--- a/docs/types.rst
+++ b/docs/types.rst
@@ -321,7 +321,7 @@ can be assigned from functions and function parameters of function type
can be used to pass functions to and return functions from function calls.
Function types come in two flavours - *internal* and *external* functions:
-Internal functions can only be used inside the current contract (more specifically,
+Internal functions can only be called inside the current contract (more specifically,
inside the current code unit, which also includes internal library functions
and inherited functions) because they cannot be executed outside of the
context of the current contract. Calling an internal function is realized
@@ -340,7 +340,8 @@ function type should not return anything, the whole ``returns (<return types>)``
part has to be omitted.
By default, function types are internal, so the ``internal`` keyword can be
-omitted.
+omitted. In contrast, contract functions themselves are public by default,
+only when used as the name of a type, the default is internal.
There are two ways to access a function in the current contract: Either directly
by its name, ``f``, or using ``this.f``. The former will result in an internal
diff --git a/libdevcore/CommonData.h b/libdevcore/CommonData.h
index ab4bfe68..6f40d7be 100644
--- a/libdevcore/CommonData.h
+++ b/libdevcore/CommonData.h
@@ -145,6 +145,13 @@ inline std::string toHex(u256 val, HexPrefix prefix = HexPrefix::DontAdd)
return (prefix == HexPrefix::Add) ? "0x" + str : str;
}
+inline std::string toCompactHexWithPrefix(u256 val)
+{
+ std::ostringstream ret;
+ ret << std::hex << val;
+ return "0x" + ret.str();
+}
+
// Algorithms for string and string-like collections.
/// Escapes a string into the C-string representation.
diff --git a/libdevcore/IndentedWriter.cpp b/libdevcore/IndentedWriter.cpp
new file mode 100644
index 00000000..96aaf0fa
--- /dev/null
+++ b/libdevcore/IndentedWriter.cpp
@@ -0,0 +1,65 @@
+/*
+ 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/>.
+*/
+/**
+ * @date 2017
+ * Indented text writer.
+ */
+
+#include <libdevcore/IndentedWriter.h>
+#include <libdevcore/Assertions.h>
+
+using namespace std;
+using namespace dev;
+
+string IndentedWriter::format() const
+{
+ string result;
+ for (auto const& line: m_lines)
+ result += string(line.indentation * 4, ' ') + line.contents + "\n";
+ return result;
+}
+
+void IndentedWriter::newLine()
+{
+ if (!m_lines.back().contents.empty())
+ m_lines.push_back({ string(), m_lines.back().indentation });
+}
+
+void IndentedWriter::indent()
+{
+ newLine();
+ m_lines.back().indentation++;
+}
+
+void IndentedWriter::unindent()
+{
+ newLine();
+ assertThrow(m_lines.back().indentation > 0, IndentedWriterError, "Negative indentation.");
+ m_lines.back().indentation--;
+}
+
+void IndentedWriter::add(string const& _str)
+{
+ m_lines.back().contents += _str;
+}
+
+void IndentedWriter::addLine(string const& _line)
+{
+ newLine();
+ add(_line);
+ newLine();
+}
diff --git a/libdevcore/IndentedWriter.h b/libdevcore/IndentedWriter.h
new file mode 100644
index 00000000..4ddd87ed
--- /dev/null
+++ b/libdevcore/IndentedWriter.h
@@ -0,0 +1,67 @@
+/*
+ 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/>.
+*/
+/**
+ * @date 2017
+ * Indented text writer.
+ */
+
+#pragma once
+
+#include <vector>
+#include <string>
+
+#include <libdevcore/Exceptions.h>
+
+namespace dev
+{
+
+DEV_SIMPLE_EXCEPTION(IndentedWriterError);
+
+class IndentedWriter
+{
+public:
+ explicit IndentedWriter(): m_lines(std::vector<Line>{{std::string(), 0}}) {}
+
+ // Returns the formatted output.
+ std::string format() const;
+
+ // Go one indentation level in.
+ void indent();
+
+ // Go one indentation level out.
+ void unindent();
+
+ // Add text.
+ void add(std::string const& _str);
+
+ // Add text with new line.
+ void addLine(std::string const& _line);
+
+ // Add new line.
+ void newLine();
+
+private:
+ struct Line
+ {
+ std::string contents;
+ unsigned indentation;
+ };
+
+ std::vector<Line> m_lines;
+};
+
+}
diff --git a/libdevcore/Whiskers.cpp b/libdevcore/Whiskers.cpp
index 4bad8476..b0a4c755 100644
--- a/libdevcore/Whiskers.cpp
+++ b/libdevcore/Whiskers.cpp
@@ -90,7 +90,7 @@ string Whiskers::replace(
string tagName(_match[1]);
if (!tagName.empty())
{
- assertThrow(_parameters.count(tagName), WhiskersError, "Tag " + tagName + " not found.");
+ assertThrow(_parameters.count(tagName), WhiskersError, "Value for tag " + tagName + " not provided.");
return _parameters.at(tagName);
}
else
diff --git a/libsolidity/analysis/DeclarationContainer.h b/libsolidity/analysis/DeclarationContainer.h
index 9c7c89e7..301998b7 100644
--- a/libsolidity/analysis/DeclarationContainer.h
+++ b/libsolidity/analysis/DeclarationContainer.h
@@ -53,6 +53,7 @@ public:
bool registerDeclaration(Declaration const& _declaration, ASTString const* _name = nullptr, bool _invisible = false, bool _update = false);
std::vector<Declaration const*> resolveName(ASTString const& _name, bool _recursive = false) const;
ASTNode const* enclosingNode() const { return m_enclosingNode; }
+ DeclarationContainer const* enclosingContainer() const { return m_enclosingContainer; }
std::map<ASTString, std::vector<Declaration const*>> const& declarations() const { return m_declarations; }
/// @returns whether declaration is valid, and if not also returns previous declaration.
Declaration const* conflictingDeclaration(Declaration const& _declaration, ASTString const* _name = nullptr) const;
diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp
index df83f382..523e7176 100644
--- a/libsolidity/analysis/NameAndTypeResolver.cpp
+++ b/libsolidity/analysis/NameAndTypeResolver.cpp
@@ -452,13 +452,9 @@ bool DeclarationRegistrationHelper::registerDeclaration(
_errorLocation = &_declaration.location();
Declaration const* shadowedDeclaration = nullptr;
- if (_warnOnShadow && !_declaration.name().empty())
- for (auto const* decl: _container.resolveName(_declaration.name(), true))
- if (decl != &_declaration)
- {
- shadowedDeclaration = decl;
- break;
- }
+ if (_warnOnShadow && !_declaration.name().empty() && _container.enclosingContainer())
+ for (auto const* decl: _container.enclosingContainer()->resolveName(_declaration.name(), true))
+ shadowedDeclaration = decl;
if (!_container.registerDeclaration(_declaration, _name, !_declaration.isVisibleInContract()))
{
diff --git a/libsolidity/analysis/NameAndTypeResolver.h b/libsolidity/analysis/NameAndTypeResolver.h
index a498c7ba..59bd3b1f 100644
--- a/libsolidity/analysis/NameAndTypeResolver.h
+++ b/libsolidity/analysis/NameAndTypeResolver.h
@@ -169,6 +169,8 @@ private:
void closeCurrentScope();
void registerDeclaration(Declaration& _declaration, bool _opensScope);
+ static bool isOverloadedFunction(Declaration const& _declaration1, Declaration const& _declaration2);
+
/// @returns the canonical name of the current scope.
std::string currentCanonicalName() const;
diff --git a/libsolidity/analysis/StaticAnalyzer.cpp b/libsolidity/analysis/StaticAnalyzer.cpp
index 46477e1e..ab1cbb52 100644
--- a/libsolidity/analysis/StaticAnalyzer.cpp
+++ b/libsolidity/analysis/StaticAnalyzer.cpp
@@ -92,6 +92,17 @@ bool StaticAnalyzer::visit(VariableDeclaration const& _variable)
// This is not a no-op, the entry might pre-exist.
m_localVarUseCount[&_variable] += 0;
}
+ else if (_variable.isStateVariable())
+ {
+ set<StructDefinition const*> structsSeen;
+ if (structureSizeEstimate(*_variable.type(), structsSeen) >= bigint(1) << 64)
+ m_errorReporter.warning(
+ _variable.location(),
+ "Variable covers a large part of storage and thus makes collisions likely. "
+ "Either use mappings or dynamic arrays and allow their size to be increased only "
+ "in small quantities per transaction."
+ );
+ }
return true;
}
@@ -160,3 +171,34 @@ bool StaticAnalyzer::visit(InlineAssembly const& _inlineAssembly)
return true;
}
+
+bigint StaticAnalyzer::structureSizeEstimate(Type const& _type, set<StructDefinition const*>& _structsSeen)
+{
+ switch (_type.category())
+ {
+ case Type::Category::Array:
+ {
+ auto const& t = dynamic_cast<ArrayType const&>(_type);
+ return structureSizeEstimate(*t.baseType(), _structsSeen) * (t.isDynamicallySized() ? 1 : t.length());
+ }
+ case Type::Category::Struct:
+ {
+ auto const& t = dynamic_cast<StructType const&>(_type);
+ bigint size = 1;
+ if (!_structsSeen.count(&t.structDefinition()))
+ {
+ _structsSeen.insert(&t.structDefinition());
+ for (auto const& m: t.members(nullptr))
+ size += structureSizeEstimate(*m.type, _structsSeen);
+ }
+ return size;
+ }
+ case Type::Category::Mapping:
+ {
+ return structureSizeEstimate(*dynamic_cast<MappingType const&>(_type).valueType(), _structsSeen);
+ }
+ default:
+ break;
+ }
+ return bigint(1);
+}
diff --git a/libsolidity/analysis/StaticAnalyzer.h b/libsolidity/analysis/StaticAnalyzer.h
index 21a487df..a3080b42 100644
--- a/libsolidity/analysis/StaticAnalyzer.h
+++ b/libsolidity/analysis/StaticAnalyzer.h
@@ -65,6 +65,9 @@ private:
virtual bool visit(MemberAccess const& _memberAccess) override;
virtual bool visit(InlineAssembly const& _inlineAssembly) override;
+ /// @returns the size of this type in storage, including all sub-types.
+ static bigint structureSizeEstimate(Type const& _type, std::set<StructDefinition const*>& _structsSeen);
+
ErrorReporter& m_errorReporter;
/// Flag that indicates whether the current contract definition is a library.
diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp
index bde0e616..d2571cd3 100644
--- a/libsolidity/analysis/SyntaxChecker.cpp
+++ b/libsolidity/analysis/SyntaxChecker.cpp
@@ -18,6 +18,7 @@
#include <libsolidity/analysis/SyntaxChecker.h>
#include <memory>
#include <libsolidity/ast/AST.h>
+#include <libsolidity/ast/ExperimentalFeatures.h>
#include <libsolidity/analysis/SemVerHandler.h>
#include <libsolidity/interface/ErrorReporter.h>
#include <libsolidity/interface/Version.h>
@@ -33,9 +34,10 @@ bool SyntaxChecker::checkSyntax(ASTNode const& _astRoot)
return Error::containsOnlyWarnings(m_errorReporter.errors());
}
-bool SyntaxChecker::visit(SourceUnit const&)
+bool SyntaxChecker::visit(SourceUnit const& _sourceUnit)
{
m_versionPragmaFound = false;
+ m_sourceUnit = &_sourceUnit;
return true;
}
@@ -57,15 +59,46 @@ void SyntaxChecker::endVisit(SourceUnit const& _sourceUnit)
m_errorReporter.warning(_sourceUnit.location(), errorString);
}
+ m_sourceUnit = nullptr;
}
bool SyntaxChecker::visit(PragmaDirective const& _pragma)
{
solAssert(!_pragma.tokens().empty(), "");
solAssert(_pragma.tokens().size() == _pragma.literals().size(), "");
- if (_pragma.tokens()[0] != Token::Identifier || _pragma.literals()[0] != "solidity")
- m_errorReporter.syntaxError(_pragma.location(), "Unknown pragma \"" + _pragma.literals()[0] + "\"");
- else
+ if (_pragma.tokens()[0] != Token::Identifier)
+ m_errorReporter.syntaxError(_pragma.location(), "Invalid pragma \"" + _pragma.literals()[0] + "\"");
+ else if (_pragma.literals()[0] == "experimental")
+ {
+ solAssert(m_sourceUnit, "");
+ vector<string> literals(_pragma.literals().begin() + 1, _pragma.literals().end());
+ if (literals.size() == 0)
+ m_errorReporter.syntaxError(
+ _pragma.location(),
+ "Experimental feature name is missing."
+ );
+ else if (literals.size() > 1)
+ m_errorReporter.syntaxError(
+ _pragma.location(),
+ "Stray arguments."
+ );
+ else
+ {
+ string const literal = literals[0];
+ if (literal.empty())
+ m_errorReporter.syntaxError(_pragma.location(), "Empty experimental feature name is invalid.");
+ else if (!ExperimentalFeatureNames.count(literal))
+ m_errorReporter.syntaxError(_pragma.location(), "Unsupported experimental feature name.");
+ else if (m_sourceUnit->annotation().experimentalFeatures.count(ExperimentalFeatureNames.at(literal)))
+ m_errorReporter.syntaxError(_pragma.location(), "Duplicate experimental feature name.");
+ else
+ {
+ m_sourceUnit->annotation().experimentalFeatures.insert(ExperimentalFeatureNames.at(literal));
+ m_errorReporter.warning(_pragma.location(), "Experimental features are turned on. Do not use experimental features on live deployments.");
+ }
+ }
+ }
+ else if (_pragma.literals()[0] == "solidity")
{
vector<Token::Value> tokens(_pragma.tokens().begin() + 1, _pragma.tokens().end());
vector<string> literals(_pragma.literals().begin() + 1, _pragma.literals().end());
@@ -81,6 +114,8 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma)
);
m_versionPragmaFound = true;
}
+ else
+ m_errorReporter.syntaxError(_pragma.location(), "Unknown pragma \"" + _pragma.literals()[0] + "\"");
return true;
}
diff --git a/libsolidity/analysis/SyntaxChecker.h b/libsolidity/analysis/SyntaxChecker.h
index fb5cc6d7..fa34bab3 100644
--- a/libsolidity/analysis/SyntaxChecker.h
+++ b/libsolidity/analysis/SyntaxChecker.h
@@ -77,6 +77,8 @@ private:
bool m_versionPragmaFound = false;
int m_inLoopDepth = 0;
+
+ SourceUnit const* m_sourceUnit = nullptr;
};
}
diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp
index 1ee827d4..dbc95c4f 100644
--- a/libsolidity/analysis/TypeChecker.cpp
+++ b/libsolidity/analysis/TypeChecker.cpp
@@ -93,7 +93,7 @@ bool TypeChecker::visit(ContractDefinition const& _contract)
FunctionDefinition const* fallbackFunction = nullptr;
for (FunctionDefinition const* function: _contract.definedFunctions())
{
- if (function->name().empty())
+ if (function->isFallback())
{
if (fallbackFunction)
{
@@ -112,8 +112,6 @@ bool TypeChecker::visit(ContractDefinition const& _contract)
m_errorReporter.typeError(fallbackFunction->returnParameterList()->location(), "Fallback function cannot return values.");
}
}
- if (!function->isImplemented())
- _contract.annotation().isFullyImplemented = false;
}
for (auto const& n: _contract.subNodes())
@@ -188,20 +186,13 @@ void TypeChecker::checkContractAbstractFunctions(ContractDefinition const& _cont
using FunTypeAndFlag = std::pair<FunctionTypePointer, bool>;
map<string, vector<FunTypeAndFlag>> functions;
- bool allBaseConstructorsImplemented = true;
// Search from base to derived
for (ContractDefinition const* contract: boost::adaptors::reverse(_contract.annotation().linearizedBaseContracts))
for (FunctionDefinition const* function: contract->definedFunctions())
{
// Take constructors out of overload hierarchy
if (function->isConstructor())
- {
- if (!function->isImplemented())
- // Base contract's constructor is not fully implemented, no way to get
- // out of this.
- allBaseConstructorsImplemented = false;
continue;
- }
auto& overloads = functions[function->name()];
FunctionTypePointer funType = make_shared<FunctionType>(*function);
auto it = find_if(overloads.begin(), overloads.end(), [&](FunTypeAndFlag const& _funAndFlag)
@@ -219,16 +210,15 @@ void TypeChecker::checkContractAbstractFunctions(ContractDefinition const& _cont
it->second = true;
}
- if (!allBaseConstructorsImplemented)
- _contract.annotation().isFullyImplemented = false;
-
// Set to not fully implemented if at least one flag is false.
for (auto const& it: functions)
for (auto const& funAndFlag: it.second)
if (!funAndFlag.second)
{
- _contract.annotation().isFullyImplemented = false;
- return;
+ FunctionDefinition const* function = dynamic_cast<FunctionDefinition const*>(&funAndFlag.first->declaration());
+ solAssert(function, "");
+ _contract.annotation().unimplementedFunctions.push_back(function);
+ break;
}
}
@@ -266,7 +256,8 @@ void TypeChecker::checkContractAbstractConstructors(ContractDefinition const& _c
}
}
if (!argumentsNeeded.empty())
- _contract.annotation().isFullyImplemented = false;
+ for (ContractDefinition const* contract: argumentsNeeded)
+ _contract.annotation().unimplementedFunctions.push_back(contract->constructor());
}
void TypeChecker::checkContractIllegalOverrides(ContractDefinition const& _contract)
@@ -286,21 +277,10 @@ void TypeChecker::checkContractIllegalOverrides(ContractDefinition const& _contr
string const& name = function->name();
if (modifiers.count(name))
m_errorReporter.typeError(modifiers[name]->location(), "Override changes function to modifier.");
- FunctionType functionType(*function);
- // function should not change the return type
+
for (FunctionDefinition const* overriding: functions[name])
- {
- FunctionType overridingType(*overriding);
- if (!overridingType.hasEqualArgumentTypes(functionType))
- continue;
- if (
- overriding->visibility() != function->visibility() ||
- overriding->isDeclaredConst() != function->isDeclaredConst() ||
- overriding->isPayable() != function->isPayable() ||
- overridingType != functionType
- )
- m_errorReporter.typeError(overriding->location(), "Override changes extended function signature.");
- }
+ checkFunctionOverride(*overriding, *function);
+
functions[name].push_back(function);
}
for (ModifierDefinition const* modifier: contract->functionModifiers())
@@ -317,6 +297,42 @@ void TypeChecker::checkContractIllegalOverrides(ContractDefinition const& _contr
}
}
+void TypeChecker::checkFunctionOverride(FunctionDefinition const& function, FunctionDefinition const& super)
+{
+ FunctionType functionType(function);
+ FunctionType superType(super);
+
+ if (!functionType.hasEqualArgumentTypes(superType))
+ return;
+
+ if (function.visibility() != super.visibility())
+ overrideError(function, super, "Overriding function visibility differs.");
+
+ else if (function.isDeclaredConst() && !super.isDeclaredConst())
+ overrideError(function, super, "Overriding function should not be declared constant.");
+
+ else if (!function.isDeclaredConst() && super.isDeclaredConst())
+ overrideError(function, super, "Overriding function should be declared constant.");
+
+ else if (function.isPayable() && !super.isPayable())
+ overrideError(function, super, "Overriding function should not be declared payable.");
+
+ else if (!function.isPayable() && super.isPayable())
+ overrideError(function, super, "Overriding function should be declared payable.");
+
+ else if (functionType != superType)
+ overrideError(function, super, "Overriding function return types differ.");
+}
+
+void TypeChecker::overrideError(FunctionDefinition const& function, FunctionDefinition const& super, string message)
+{
+ m_errorReporter.typeError(
+ function.location(),
+ SecondarySourceLocation().append("Overriden function is here:", super.location()),
+ message
+ );
+}
+
void TypeChecker::checkContractExternalTypeClashes(ContractDefinition const& _contract)
{
map<string, vector<pair<Declaration const*, FunctionTypePointer>>> externalDeclarations;
@@ -482,10 +498,8 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
{
if (isLibraryFunction)
m_errorReporter.typeError(_function.location(), "Library functions cannot be payable.");
- if (!_function.isConstructor() && !_function.name().empty() && !_function.isPartOfExternalInterface())
+ if (!_function.isConstructor() && !_function.isFallback() && !_function.isPartOfExternalInterface())
m_errorReporter.typeError(_function.location(), "Internal functions cannot be payable.");
- if (_function.isDeclaredConst())
- m_errorReporter.typeError(_function.location(), "Functions cannot be constant and payable at the same time.");
}
for (ASTPointer<VariableDeclaration> const& var: _function.parameters() + _function.returnParameters())
{
@@ -525,6 +539,10 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
}
if (_function.isImplemented())
_function.body().accept(*this);
+ else if (_function.isConstructor())
+ m_errorReporter.typeError(_function.location(), "Constructor must be implemented if declared.");
+ else if (isLibraryFunction && _function.visibility() <= FunctionDefinition::Visibility::Internal)
+ m_errorReporter.typeError(_function.location(), "Internal library function must be implemented if declared.");
return false;
}
@@ -1050,7 +1068,7 @@ void TypeChecker::endVisit(ExpressionStatement const& _statement)
{
auto kind = callType->kind();
if (
- kind == FunctionType::Kind::Bare ||
+ kind == FunctionType::Kind::BareCall ||
kind == FunctionType::Kind::BareCallCode ||
kind == FunctionType::Kind::BareDelegateCall
)
@@ -1523,8 +1541,15 @@ void TypeChecker::endVisit(NewExpression const& _newExpression)
if (!contract)
m_errorReporter.fatalTypeError(_newExpression.location(), "Identifier is not a contract.");
- if (!contract->annotation().isFullyImplemented)
- m_errorReporter.typeError(_newExpression.location(), "Trying to create an instance of an abstract contract.");
+ if (!contract->annotation().unimplementedFunctions.empty())
+ m_errorReporter.typeError(
+ _newExpression.location(),
+ SecondarySourceLocation().append(
+ "Missing implementation:",
+ contract->annotation().unimplementedFunctions.front()->location()
+ ),
+ "Trying to create an instance of an abstract contract."
+ );
if (!contract->constructorIsPublic())
m_errorReporter.typeError(_newExpression.location(), "Contract with internal constructor cannot be created directly.");
@@ -1947,4 +1972,3 @@ void TypeChecker::requireLValue(Expression const& _expression)
else if (!_expression.annotation().isLValue)
m_errorReporter.typeError(_expression.location(), "Expression has to be an lvalue.");
}
-
diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h
index ee43d13a..f2e13765 100644
--- a/libsolidity/analysis/TypeChecker.h
+++ b/libsolidity/analysis/TypeChecker.h
@@ -62,6 +62,9 @@ private:
/// arguments and that there is at most one constructor.
void checkContractDuplicateFunctions(ContractDefinition const& _contract);
void checkContractIllegalOverrides(ContractDefinition const& _contract);
+ /// Reports a type error with an appropiate message if overriden function signature differs.
+ void checkFunctionOverride(FunctionDefinition const& function, FunctionDefinition const& super);
+ void overrideError(FunctionDefinition const& function, FunctionDefinition const& super, std::string message);
void checkContractAbstractFunctions(ContractDefinition const& _contract);
void checkContractAbstractConstructors(ContractDefinition const& _contract);
/// Checks that different functions with external visibility end up having different
diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp
index ebc8bd48..e173237e 100644
--- a/libsolidity/ast/AST.cpp
+++ b/libsolidity/ast/AST.cpp
@@ -162,7 +162,7 @@ FunctionDefinition const* ContractDefinition::fallbackFunction() const
{
for (ContractDefinition const* contract: annotation().linearizedBaseContracts)
for (FunctionDefinition const* f: contract->definedFunctions())
- if (f->name().empty())
+ if (f->isFallback())
return f;
return nullptr;
}
@@ -371,6 +371,15 @@ string FunctionDefinition::externalSignature() const
return FunctionType(*this).externalSignature();
}
+string FunctionDefinition::fullyQualifiedName() const
+{
+ auto const* contract = dynamic_cast<ContractDefinition const*>(scope());
+ solAssert(contract, "Enclosing scope of function definition was not set.");
+
+ auto fname = name().empty() ? "<fallback>" : name();
+ return sourceUnitName() + ":" + contract->name() + "." + fname;
+}
+
FunctionDefinitionAnnotation& FunctionDefinition::annotation() const
{
if (!m_annotation)
diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h
index e4656f72..8a577c0c 100644
--- a/libsolidity/ast/AST.h
+++ b/libsolidity/ast/AST.h
@@ -28,6 +28,7 @@
#include <libsolidity/ast/Types.h>
#include <libsolidity/interface/Exceptions.h>
#include <libsolidity/ast/ASTAnnotations.h>
+#include <libsolidity/ast/ASTEnums.h>
#include <libevmasm/SourceLocation.h>
#include <libevmasm/Instruction.h>
@@ -152,6 +153,24 @@ public:
/// Visibility ordered from restricted to unrestricted.
enum class Visibility { Default, Private, Internal, Public, External };
+ static std::string visibilityToString(Declaration::Visibility _visibility)
+ {
+ switch(_visibility)
+ {
+ case Declaration::Visibility::Public:
+ return "public";
+ case Declaration::Visibility::Internal:
+ return "internal";
+ case Declaration::Visibility::Private:
+ return "private";
+ case Declaration::Visibility::External:
+ return "external";
+ default:
+ solAssert(false, "Invalid visibility specifier.");
+ }
+ return std::string();
+ }
+
Declaration(
SourceLocation const& _location,
ASTPointer<ASTString> const& _name,
@@ -566,21 +585,19 @@ public:
SourceLocation const& _location,
ASTPointer<ASTString> const& _name,
Declaration::Visibility _visibility,
+ StateMutability _stateMutability,
bool _isConstructor,
ASTPointer<ASTString> const& _documentation,
ASTPointer<ParameterList> const& _parameters,
- bool _isDeclaredConst,
std::vector<ASTPointer<ModifierInvocation>> const& _modifiers,
ASTPointer<ParameterList> const& _returnParameters,
- bool _isPayable,
ASTPointer<Block> const& _body
):
CallableDeclaration(_location, _name, _visibility, _parameters, _returnParameters),
Documented(_documentation),
ImplementationOptional(_body != nullptr),
+ m_stateMutability(_stateMutability),
m_isConstructor(_isConstructor),
- m_isDeclaredConst(_isDeclaredConst),
- m_isPayable(_isPayable),
m_functionModifiers(_modifiers),
m_body(_body)
{}
@@ -588,17 +605,20 @@ public:
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
+ StateMutability stateMutability() const { return m_stateMutability; }
bool isConstructor() const { return m_isConstructor; }
- bool isDeclaredConst() const { return m_isDeclaredConst; }
- bool isPayable() const { return m_isPayable; }
+ bool isFallback() const { return name().empty(); }
+ bool isDeclaredConst() const { return m_stateMutability == StateMutability::View; }
+ bool isPayable() const { return m_stateMutability == StateMutability::Payable; }
std::vector<ASTPointer<ModifierInvocation>> const& modifiers() const { return m_functionModifiers; }
std::vector<ASTPointer<VariableDeclaration>> const& returnParameters() const { return m_returnParameters->parameters(); }
Block const& body() const { solAssert(m_body, ""); return *m_body; }
+ std::string fullyQualifiedName() const;
virtual bool isVisibleInContract() const override
{
- return Declaration::isVisibleInContract() && !isConstructor() && !name().empty();
+ return Declaration::isVisibleInContract() && !isConstructor() && !isFallback();
}
- virtual bool isPartOfExternalInterface() const override { return isPublic() && !m_isConstructor && !name().empty(); }
+ virtual bool isPartOfExternalInterface() const override { return isPublic() && !isConstructor() && !isFallback(); }
/// @returns the external signature of the function
/// That consists of the name of the function followed by the types of the
@@ -614,9 +634,8 @@ public:
virtual FunctionDefinitionAnnotation& annotation() const override;
private:
+ StateMutability m_stateMutability;
bool m_isConstructor;
- bool m_isDeclaredConst;
- bool m_isPayable;
std::vector<ASTPointer<ModifierInvocation>> m_functionModifiers;
ASTPointer<Block> m_body;
};
@@ -876,11 +895,10 @@ public:
ASTPointer<ParameterList> const& _parameterTypes,
ASTPointer<ParameterList> const& _returnTypes,
Declaration::Visibility _visibility,
- bool _isDeclaredConst,
- bool _isPayable
+ StateMutability _stateMutability
):
TypeName(_location), m_parameterTypes(_parameterTypes), m_returnTypes(_returnTypes),
- m_visibility(_visibility), m_isDeclaredConst(_isDeclaredConst), m_isPayable(_isPayable)
+ m_visibility(_visibility), m_stateMutability(_stateMutability)
{}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
@@ -894,15 +912,15 @@ public:
{
return m_visibility == Declaration::Visibility::Default ? Declaration::Visibility::Internal : m_visibility;
}
- bool isDeclaredConst() const { return m_isDeclaredConst; }
- bool isPayable() const { return m_isPayable; }
+ StateMutability stateMutability() const { return m_stateMutability; }
+ bool isDeclaredConst() const { return m_stateMutability == StateMutability::View; }
+ bool isPayable() const { return m_stateMutability == StateMutability::Payable; }
private:
ASTPointer<ParameterList> m_parameterTypes;
ASTPointer<ParameterList> m_returnTypes;
Declaration::Visibility m_visibility;
- bool m_isDeclaredConst;
- bool m_isPayable;
+ StateMutability m_stateMutability;
};
/**
diff --git a/libsolidity/ast/ASTAnnotations.h b/libsolidity/ast/ASTAnnotations.h
index 45a6dd1a..fd9efb4d 100644
--- a/libsolidity/ast/ASTAnnotations.h
+++ b/libsolidity/ast/ASTAnnotations.h
@@ -23,6 +23,7 @@
#pragma once
#include <libsolidity/ast/ASTForward.h>
+#include <libsolidity/ast/ExperimentalFeatures.h>
#include <map>
#include <memory>
@@ -61,6 +62,8 @@ struct SourceUnitAnnotation: ASTAnnotation
std::string path;
/// The exported symbols (all global symbols).
std::map<ASTString, std::vector<Declaration const*>> exportedSymbols;
+ /// Experimental features.
+ std::set<ExperimentalFeature> experimentalFeatures;
};
struct ImportAnnotation: ASTAnnotation
@@ -79,8 +82,8 @@ struct TypeDeclarationAnnotation: ASTAnnotation
struct ContractDefinitionAnnotation: TypeDeclarationAnnotation, DocumentedAnnotation
{
- /// Whether all functions are implemented.
- bool isFullyImplemented = true;
+ /// List of functions without a body. Can also contain functions from base classes.
+ std::vector<FunctionDefinition const*> unimplementedFunctions;
/// List of all (direct and indirect) base contracts in order from derived to
/// base, including the contract itself.
std::vector<ContractDefinition const*> linearizedBaseContracts;
diff --git a/libsolidity/ast/ASTEnums.h b/libsolidity/ast/ASTEnums.h
new file mode 100644
index 00000000..f7c75878
--- /dev/null
+++ b/libsolidity/ast/ASTEnums.h
@@ -0,0 +1,52 @@
+/*
+ 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/>.
+*/
+/**
+ * @date 2017
+ * Enums for AST classes.
+ */
+
+#pragma once
+
+#include <libsolidity/interface/Exceptions.h>
+
+#include <string>
+
+namespace dev
+{
+namespace solidity
+{
+
+// How a function can mutate the EVM state.
+enum class StateMutability { View, NonPayable, Payable };
+
+inline std::string stateMutabilityToString(StateMutability const& _stateMutability)
+{
+ switch(_stateMutability)
+ {
+ case StateMutability::View:
+ return "view";
+ case StateMutability::NonPayable:
+ return "nonpayable";
+ case StateMutability::Payable:
+ return "payable";
+ default:
+ solAssert(false, "Unknown state mutability.");
+ }
+}
+
+}
+}
diff --git a/libsolidity/ast/ASTForward.h b/libsolidity/ast/ASTForward.h
index cfeeaa58..15735368 100644
--- a/libsolidity/ast/ASTForward.h
+++ b/libsolidity/ast/ASTForward.h
@@ -95,6 +95,5 @@ using ASTPointer = std::shared_ptr<T>;
using ASTString = std::string;
-
}
}
diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp
index eda70b63..abee55ee 100644
--- a/libsolidity/ast/ASTJsonConverter.cpp
+++ b/libsolidity/ast/ASTJsonConverter.cpp
@@ -253,7 +253,7 @@ bool ASTJsonConverter::visit(ContractDefinition const& _node)
make_pair("name", _node.name()),
make_pair("documentation", _node.documentation() ? Json::Value(*_node.documentation()) : Json::nullValue),
make_pair("contractKind", contractKind(_node.contractKind())),
- make_pair("fullyImplemented", _node.annotation().isFullyImplemented),
+ make_pair("fullyImplemented", _node.annotation().unimplementedFunctions.empty()),
make_pair("linearizedBaseContracts", getContainerIds(_node.annotation().linearizedBaseContracts)),
make_pair("baseContracts", toJson(_node.baseContracts())),
make_pair("contractDependencies", getContainerIds(_node.annotation().contractDependencies)),
@@ -285,7 +285,7 @@ bool ASTJsonConverter::visit(StructDefinition const& _node)
{
setJsonNode(_node, "StructDefinition", {
make_pair("name", _node.name()),
- make_pair("visibility", visibility(_node.visibility())),
+ make_pair("visibility", Declaration::visibilityToString(_node.visibility())),
make_pair("canonicalName", _node.annotation().canonicalName),
make_pair("members", toJson(_node.members())),
make_pair("scope", idOrNull(_node.scope()))
@@ -325,7 +325,7 @@ bool ASTJsonConverter::visit(FunctionDefinition const& _node)
make_pair("name", _node.name()),
make_pair(m_legacy ? "constant" : "isDeclaredConst", _node.isDeclaredConst()),
make_pair("payable", _node.isPayable()),
- make_pair("visibility", visibility(_node.visibility())),
+ make_pair("visibility", Declaration::visibilityToString(_node.visibility())),
make_pair("parameters", toJson(_node.parameterList())),
make_pair("isConstructor", _node.isConstructor()),
make_pair("returnParameters", toJson(*_node.returnParameterList())),
@@ -346,7 +346,7 @@ bool ASTJsonConverter::visit(VariableDeclaration const& _node)
make_pair("constant", _node.isConstant()),
make_pair("stateVariable", _node.isStateVariable()),
make_pair("storageLocation", location(_node.referenceLocation())),
- make_pair("visibility", visibility(_node.visibility())),
+ make_pair("visibility", Declaration::visibilityToString(_node.visibility())),
make_pair("value", _node.value() ? toJson(*_node.value()) : Json::nullValue),
make_pair("scope", idOrNull(_node.scope())),
make_pair("typeDescriptions", typePointerToJson(_node.annotation().type))
@@ -361,7 +361,7 @@ bool ASTJsonConverter::visit(ModifierDefinition const& _node)
{
setJsonNode(_node, "ModifierDefinition", {
make_pair("name", _node.name()),
- make_pair("visibility", visibility(_node.visibility())),
+ make_pair("visibility", Declaration::visibilityToString(_node.visibility())),
make_pair("parameters", toJson(_node.parameterList())),
make_pair("body", toJson(_node.body()))
});
@@ -418,7 +418,7 @@ bool ASTJsonConverter::visit(FunctionTypeName const& _node)
{
setJsonNode(_node, "FunctionTypeName", {
make_pair("payable", _node.isPayable()),
- make_pair("visibility", visibility(_node.visibility())),
+ make_pair("visibility", Declaration::visibilityToString(_node.visibility())),
make_pair(m_legacy ? "constant" : "isDeclaredConst", _node.isDeclaredConst()),
make_pair("parameterTypes", toJson(*_node.parameterTypeList())),
make_pair("returnParameterTypes", toJson(*_node.returnParameterTypeList())),
@@ -730,23 +730,6 @@ void ASTJsonConverter::endVisit(EventDefinition const&)
m_inEvent = false;
}
-string ASTJsonConverter::visibility(Declaration::Visibility const& _visibility)
-{
- switch (_visibility)
- {
- case Declaration::Visibility::Private:
- return "private";
- case Declaration::Visibility::Internal:
- return "internal";
- case Declaration::Visibility::Public:
- return "public";
- case Declaration::Visibility::External:
- return "external";
- default:
- solAssert(false, "Unknown declaration visibility.");
- }
-}
-
string ASTJsonConverter::location(VariableDeclaration::Location _location)
{
switch (_location)
diff --git a/libsolidity/ast/ASTJsonConverter.h b/libsolidity/ast/ASTJsonConverter.h
index 27114c2a..70e260db 100644
--- a/libsolidity/ast/ASTJsonConverter.h
+++ b/libsolidity/ast/ASTJsonConverter.h
@@ -128,7 +128,6 @@ private:
return _node ? toJson(*_node) : Json::nullValue;
}
Json::Value inlineAssemblyIdentifierToJson(std::pair<assembly::Identifier const* , InlineAssemblyAnnotation::ExternalIdentifierInfo> _info);
- std::string visibility(Declaration::Visibility const& _visibility);
std::string location(VariableDeclaration::Location _location);
std::string contractKind(ContractDefinition::ContractKind _kind);
std::string functionCallKind(FunctionCallKind _kind);
diff --git a/libsolidity/ast/ExperimentalFeatures.h b/libsolidity/ast/ExperimentalFeatures.h
new file mode 100644
index 00000000..0c03ea4a
--- /dev/null
+++ b/libsolidity/ast/ExperimentalFeatures.h
@@ -0,0 +1,50 @@
+/*
+ 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/>.
+*/
+/**
+ * List of experimental features.
+ */
+
+#pragma once
+
+#include <map>
+
+namespace dev
+{
+namespace solidity
+{
+
+enum class ExperimentalFeature
+{
+ ABIEncoderV2, // new ABI encoder that makes use of JULIA
+ Test,
+ TestOnlyAnalysis
+};
+
+static const std::map<ExperimentalFeature, bool> ExperimentalFeatureOnlyAnalysis =
+{
+ { ExperimentalFeature::TestOnlyAnalysis, true },
+};
+
+static const std::map<std::string, ExperimentalFeature> ExperimentalFeatureNames =
+{
+ { "ABIEncoderV2", ExperimentalFeature::ABIEncoderV2 },
+ { "__test", ExperimentalFeature::Test },
+ { "__testOnlyAnalysis", ExperimentalFeature::TestOnlyAnalysis },
+};
+
+}
+}
diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp
index 84e4a077..302f1022 100644
--- a/libsolidity/ast/Types.cpp
+++ b/libsolidity/ast/Types.cpp
@@ -477,8 +477,8 @@ MemberList::MemberMap IntegerType::nativeMembers(ContractDefinition const*) cons
if (isAddress())
return {
{"balance", make_shared<IntegerType >(256)},
- {"call", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Kind::Bare, true, false, true)},
- {"callcode", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Kind::BareCallCode, true, false, true)},
+ {"call", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Kind::BareCall, true, StateMutability::Payable)},
+ {"callcode", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Kind::BareCallCode, true, StateMutability::Payable)},
{"delegatecall", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Kind::BareDelegateCall, true)},
{"send", make_shared<FunctionType>(strings{"uint"}, strings{"bool"}, FunctionType::Kind::Send)},
{"transfer", make_shared<FunctionType>(strings{"uint"}, strings(), FunctionType::Kind::Transfer)}
@@ -525,19 +525,20 @@ bool FixedPointType::isExplicitlyConvertibleTo(Type const& _convertTo) const
TypePointer FixedPointType::unaryOperatorResult(Token::Value _operator) const
{
- // "delete" is ok for all fixed types
- if (_operator == Token::Delete)
+ switch(_operator)
+ {
+ case Token::Delete:
+ // "delete" is ok for all fixed types
return make_shared<TupleType>();
- // for fixed, we allow +, -, ++ and --
- else if (
- _operator == Token::Add ||
- _operator == Token::Sub ||
- _operator == Token::Inc ||
- _operator == Token::Dec
- )
+ case Token::Add:
+ case Token::Sub:
+ case Token::Inc:
+ case Token::Dec:
+ // for fixed, we allow +, -, ++ and --
return shared_from_this();
- else
+ default:
return TypePointer();
+ }
}
bool FixedPointType::operator==(Type const& _other) const
@@ -1408,6 +1409,11 @@ unsigned ArrayType::calldataEncodedSize(bool _padded) const
return unsigned(size);
}
+bool ArrayType::isDynamicallyEncoded() const
+{
+ return isDynamicallySized() || baseType()->isDynamicallyEncoded();
+}
+
u256 ArrayType::storageSize() const
{
if (isDynamicallySized())
@@ -1523,8 +1529,6 @@ TypePointer ArrayType::interfaceType(bool _inLibrary) const
TypePointer baseExt = m_baseType->interfaceType(_inLibrary);
if (!baseExt)
return TypePointer();
- if (m_baseType->category() == Category::Array && m_baseType->isDynamicallySized())
- return TypePointer();
if (isDynamicallySized())
return make_shared<ArrayType>(DataLocation::Memory, baseExt);
@@ -1710,6 +1714,11 @@ unsigned StructType::calldataEncodedSize(bool _padded) const
return size;
}
+bool StructType::isDynamicallyEncoded() const
+{
+ solAssert(false, "Structs are not yet supported in the ABI.");
+}
+
u256 StructType::memorySize() const
{
u256 size;
@@ -1989,8 +1998,7 @@ TypePointer TupleType::closestTemporaryType(TypePointer const& _targetType) cons
FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal):
m_kind(_isInternal ? Kind::Internal : Kind::External),
- m_isConstant(_function.isDeclaredConst()),
- m_isPayable(_isInternal ? false : _function.isPayable()),
+ m_stateMutability(_function.stateMutability()),
m_declaration(&_function)
{
TypePointers params;
@@ -1998,6 +2006,9 @@ FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal
TypePointers retParams;
vector<string> retParamNames;
+ if (_isInternal && m_stateMutability == StateMutability::Payable)
+ m_stateMutability = StateMutability::NonPayable;
+
params.reserve(_function.parameters().size());
paramNames.reserve(_function.parameters().size());
for (ASTPointer<VariableDeclaration> const& var: _function.parameters())
@@ -2019,7 +2030,7 @@ FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal
}
FunctionType::FunctionType(VariableDeclaration const& _varDecl):
- m_kind(Kind::External), m_isConstant(true), m_declaration(&_varDecl)
+ m_kind(Kind::External), m_stateMutability(StateMutability::View), m_declaration(&_varDecl)
{
TypePointers paramTypes;
vector<string> paramNames;
@@ -2079,7 +2090,7 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl):
}
FunctionType::FunctionType(EventDefinition const& _event):
- m_kind(Kind::Event), m_isConstant(true), m_declaration(&_event)
+ m_kind(Kind::Event), m_stateMutability(StateMutability::View), m_declaration(&_event)
{
TypePointers params;
vector<string> paramNames;
@@ -2096,14 +2107,10 @@ FunctionType::FunctionType(EventDefinition const& _event):
FunctionType::FunctionType(FunctionTypeName const& _typeName):
m_kind(_typeName.visibility() == VariableDeclaration::Visibility::External ? Kind::External : Kind::Internal),
- m_isConstant(_typeName.isDeclaredConst()),
- m_isPayable(_typeName.isPayable())
+ m_stateMutability(_typeName.stateMutability())
{
if (_typeName.isPayable())
- {
solAssert(m_kind == Kind::External, "Internal payable function type used.");
- solAssert(!m_isConstant, "Payable constant function");
- }
for (auto const& t: _typeName.parameterTypes())
{
solAssert(t->annotation().type, "Type not set for parameter.");
@@ -2131,7 +2138,7 @@ FunctionTypePointer FunctionType::newExpressionType(ContractDefinition const& _c
FunctionDefinition const* constructor = _contract.constructor();
TypePointers parameters;
strings parameterNames;
- bool payable = false;
+ StateMutability stateMutability = StateMutability::NonPayable;
if (constructor)
{
@@ -2140,7 +2147,8 @@ FunctionTypePointer FunctionType::newExpressionType(ContractDefinition const& _c
parameterNames.push_back(var->name());
parameters.push_back(var->annotation().type);
}
- payable = constructor->isPayable();
+ if (constructor->isPayable())
+ stateMutability = StateMutability::Payable;
}
return make_shared<FunctionType>(
parameters,
@@ -2150,8 +2158,7 @@ FunctionTypePointer FunctionType::newExpressionType(ContractDefinition const& _c
Kind::Creation,
false,
nullptr,
- false,
- payable
+ stateMutability
);
}
@@ -2178,7 +2185,7 @@ string FunctionType::identifier() const
case Kind::External: id += "external"; break;
case Kind::CallCode: id += "callcode"; break;
case Kind::DelegateCall: id += "delegatecall"; break;
- case Kind::Bare: id += "bare"; break;
+ case Kind::BareCall: id += "barecall"; break;
case Kind::BareCallCode: id += "barecallcode"; break;
case Kind::BareDelegateCall: id += "baredelegatecall"; break;
case Kind::Creation: id += "creation"; break;
@@ -2210,6 +2217,8 @@ string FunctionType::identifier() const
}
if (isConstant())
id += "_constant";
+ if (isPayable())
+ id += "_payable";
id += identifierList(m_parameterTypes) + "returns" + identifierList(m_returnParameterTypes);
if (m_gasSet)
id += "gas";
@@ -2224,23 +2233,22 @@ bool FunctionType::operator==(Type const& _other) const
{
if (_other.category() != category())
return false;
- FunctionType const& other = dynamic_cast<FunctionType const&>(_other);
- if (m_kind != other.m_kind)
- return false;
- if (m_isConstant != other.isConstant())
+ FunctionType const& other = dynamic_cast<FunctionType const&>(_other);
+ if (
+ m_kind != other.m_kind ||
+ isConstant() != other.isConstant() ||
+ isPayable() != other.isPayable() ||
+ m_parameterTypes.size() != other.m_parameterTypes.size() ||
+ m_returnParameterTypes.size() != other.m_returnParameterTypes.size()
+ )
return false;
- if (m_parameterTypes.size() != other.m_parameterTypes.size() ||
- m_returnParameterTypes.size() != other.m_returnParameterTypes.size())
- return false;
auto typeCompare = [](TypePointer const& _a, TypePointer const& _b) -> bool { return *_a == *_b; };
-
- if (!equal(m_parameterTypes.cbegin(), m_parameterTypes.cend(),
- other.m_parameterTypes.cbegin(), typeCompare))
- return false;
- if (!equal(m_returnParameterTypes.cbegin(), m_returnParameterTypes.cend(),
- other.m_returnParameterTypes.cbegin(), typeCompare))
+ if (
+ !equal(m_parameterTypes.cbegin(), m_parameterTypes.cend(), other.m_parameterTypes.cbegin(), typeCompare) ||
+ !equal(m_returnParameterTypes.cbegin(), m_returnParameterTypes.cend(), other.m_returnParameterTypes.cbegin(), typeCompare)
+ )
return false;
//@todo this is ugly, but cannot be prevented right now
if (m_gasSet != other.m_gasSet || m_valueSet != other.m_valueSet)
@@ -2292,9 +2300,9 @@ string FunctionType::toString(bool _short) const
for (auto it = m_parameterTypes.begin(); it != m_parameterTypes.end(); ++it)
name += (*it)->toString(_short) + (it + 1 == m_parameterTypes.end() ? "" : ",");
name += ")";
- if (m_isConstant)
+ if (isConstant())
name += " constant";
- if (m_isPayable)
+ if (isPayable())
name += " payable";
if (m_kind == Kind::External)
name += " external";
@@ -2344,14 +2352,26 @@ unsigned FunctionType::sizeOnStack() const
}
unsigned size = 0;
- if (kind == Kind::External || kind == Kind::CallCode || kind == Kind::DelegateCall)
+
+ switch(kind)
+ {
+ case Kind::External:
+ case Kind::CallCode:
+ case Kind::DelegateCall:
size = 2;
- else if (kind == Kind::Bare || kind == Kind::BareCallCode || kind == Kind::BareDelegateCall)
- size = 1;
- else if (kind == Kind::Internal)
- size = 1;
- else if (kind == Kind::ArrayPush || kind == Kind::ByteArrayPush)
+ break;
+ case Kind::BareCall:
+ case Kind::BareCallCode:
+ case Kind::BareDelegateCall:
+ case Kind::Internal:
+ case Kind::ArrayPush:
+ case Kind::ByteArrayPush:
size = 1;
+ break;
+ default:
+ break;
+ }
+
if (m_gasSet)
size++;
if (m_valueSet)
@@ -2389,10 +2409,14 @@ FunctionTypePointer FunctionType::interfaceFunctionType() const
return FunctionTypePointer();
return make_shared<FunctionType>(
- paramTypes, retParamTypes,
- m_parameterNames, m_returnParameterNames,
- m_kind, m_arbitraryParameters,
- m_declaration, m_isConstant, m_isPayable
+ paramTypes,
+ retParamTypes,
+ m_parameterNames,
+ m_returnParameterNames,
+ m_kind,
+ m_arbitraryParameters,
+ m_declaration,
+ m_stateMutability
);
}
@@ -2402,17 +2426,14 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con
{
case Kind::External:
case Kind::Creation:
- case Kind::ECRecover:
- case Kind::SHA256:
- case Kind::RIPEMD160:
- case Kind::Bare:
+ case Kind::BareCall:
case Kind::BareCallCode:
case Kind::BareDelegateCall:
{
MemberList::MemberMap members;
if (m_kind != Kind::BareDelegateCall && m_kind != Kind::DelegateCall)
{
- if (m_isPayable)
+ if (isPayable())
members.push_back(MemberList::Member(
"value",
make_shared<FunctionType>(
@@ -2423,8 +2444,7 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con
Kind::SetValue,
false,
nullptr,
- false,
- false,
+ StateMutability::NonPayable,
m_gasSet,
m_valueSet
)
@@ -2441,8 +2461,7 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con
Kind::SetGas,
false,
nullptr,
- false,
- false,
+ StateMutability::NonPayable,
m_gasSet,
m_valueSet
)
@@ -2509,7 +2528,7 @@ bool FunctionType::isBareCall() const
{
switch (m_kind)
{
- case Kind::Bare:
+ case Kind::BareCall:
case Kind::BareCallCode:
case Kind::BareDelegateCall:
case Kind::ECRecover:
@@ -2524,6 +2543,7 @@ bool FunctionType::isBareCall() const
string FunctionType::externalSignature() const
{
solAssert(m_declaration != nullptr, "External signature of function needs declaration");
+ solAssert(!m_declaration->name().empty(), "Fallback function has no signature.");
bool _inLibrary = dynamic_cast<ContractDefinition const&>(*m_declaration->scope()).isLibrary();
@@ -2577,8 +2597,7 @@ TypePointer FunctionType::copyAndSetGasOrValue(bool _setGas, bool _setValue) con
m_kind,
m_arbitraryParameters,
m_declaration,
- m_isConstant,
- m_isPayable,
+ m_stateMutability,
m_gasSet || _setGas,
m_valueSet || _setValue,
m_bound
@@ -2627,8 +2646,7 @@ FunctionTypePointer FunctionType::asMemberFunction(bool _inLibrary, bool _bound)
kind,
m_arbitraryParameters,
m_declaration,
- m_isConstant,
- m_isPayable,
+ m_stateMutability,
m_gasSet,
m_valueSet,
_bound
diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h
index 1db46355..56546a82 100644
--- a/libsolidity/ast/Types.h
+++ b/libsolidity/ast/Types.h
@@ -24,6 +24,7 @@
#include <libsolidity/interface/Exceptions.h>
#include <libsolidity/ast/ASTForward.h>
+#include <libsolidity/ast/ASTEnums.h>
#include <libsolidity/parsing/Token.h>
#include <libdevcore/Common.h>
@@ -187,6 +188,7 @@ public:
/// @returns number of bytes used by this type when encoded for CALL. If it is a dynamic type,
/// returns the size of the pointer (usually 32). Returns 0 if the type cannot be encoded
/// in calldata.
+ /// @note: This should actually not be called on types, where isDynamicallyEncoded returns true.
/// If @a _padded then it is assumed that each element is padded to a multiple of 32 bytes.
virtual unsigned calldataEncodedSize(bool _padded) const { (void)_padded; return 0; }
/// @returns the size of this data type in bytes when stored in memory. For memory-reference
@@ -194,8 +196,10 @@ public:
virtual unsigned memoryHeadSize() const { return calldataEncodedSize(); }
/// Convenience version of @see calldataEncodedSize(bool)
unsigned calldataEncodedSize() const { return calldataEncodedSize(true); }
- /// @returns true if the type is dynamically encoded in calldata
+ /// @returns true if the type is a dynamic array
virtual bool isDynamicallySized() const { return false; }
+ /// @returns true if the type is dynamically encoded in the ABI
+ virtual bool isDynamicallyEncoded() const { return false; }
/// @returns the number of storage slots required to hold this value in storage.
/// For dynamically "allocated" types, it returns the size of the statically allocated head,
virtual u256 storageSize() const { return 1; }
@@ -609,6 +613,7 @@ public:
virtual bool operator==(const Type& _other) const override;
virtual unsigned calldataEncodedSize(bool _padded) const override;
virtual bool isDynamicallySized() const override { return m_hasDynamicLength; }
+ virtual bool isDynamicallyEncoded() const override;
virtual u256 storageSize() const override;
virtual bool canLiveOutsideStorage() const override { return m_baseType->canLiveOutsideStorage(); }
virtual unsigned sizeOnStack() const override;
@@ -723,6 +728,7 @@ public:
virtual std::string identifier() const override;
virtual bool operator==(Type const& _other) const override;
virtual unsigned calldataEncodedSize(bool _padded) const override;
+ virtual bool isDynamicallyEncoded() const override;
u256 memorySize() const;
virtual u256 storageSize() const override;
virtual bool canLiveOutsideStorage() const override { return true; }
@@ -838,7 +844,7 @@ public:
External, ///< external call using CALL
CallCode, ///< external call using CALLCODE, i.e. not exchanging the storage
DelegateCall, ///< external call using DELEGATECALL, i.e. not exchanging the storage
- Bare, ///< CALL without function hash
+ BareCall, ///< CALL without function hash
BareCallCode, ///< CALLCODE without function hash
BareDelegateCall, ///< DELEGATECALL without function hash
Creation, ///< external call using CREATE
@@ -884,8 +890,7 @@ public:
strings const& _returnParameterTypes,
Kind _kind = Kind::Internal,
bool _arbitraryParameters = false,
- bool _constant = false,
- bool _payable = false
+ StateMutability _stateMutability = StateMutability::NonPayable
): FunctionType(
parseElementaryTypeVector(_parameterTypes),
parseElementaryTypeVector(_returnParameterTypes),
@@ -894,8 +899,7 @@ public:
_kind,
_arbitraryParameters,
nullptr,
- _constant,
- _payable
+ _stateMutability
)
{
}
@@ -912,8 +916,7 @@ public:
Kind _kind = Kind::Internal,
bool _arbitraryParameters = false,
Declaration const* _declaration = nullptr,
- bool _isConstant = false,
- bool _isPayable = false,
+ StateMutability _stateMutability = StateMutability::NonPayable,
bool _gasSet = false,
bool _valueSet = false,
bool _bound = false
@@ -923,12 +926,11 @@ public:
m_parameterNames(_parameterNames),
m_returnParameterNames(_returnParameterNames),
m_kind(_kind),
+ m_stateMutability(_stateMutability),
m_arbitraryParameters(_arbitraryParameters),
m_gasSet(_gasSet),
m_valueSet(_valueSet),
m_bound(_bound),
- m_isConstant(_isConstant),
- m_isPayable(_isPayable),
m_declaration(_declaration)
{
solAssert(
@@ -980,6 +982,7 @@ public:
/// @returns true if the ABI is used for this call (only meaningful for external calls)
bool isBareCall() const;
Kind const& kind() const { return m_kind; }
+ StateMutability stateMutability() const { return m_stateMutability; }
/// @returns the external signature of this function type given the function name
std::string externalSignature() const;
/// @returns the external identifier of this function (the hash of the signature).
@@ -990,12 +993,12 @@ public:
return *m_declaration;
}
bool hasDeclaration() const { return !!m_declaration; }
- bool isConstant() const { return m_isConstant; }
+ bool isConstant() const { return m_stateMutability == StateMutability::View; }
/// @returns true if the the result of this function only depends on its arguments
/// and it does not modify the state.
/// Currently, this will only return true for internal functions like keccak and ecrecover.
bool isPure() const;
- bool isPayable() const { return m_isPayable; }
+ bool isPayable() const { return m_stateMutability == StateMutability::Payable; }
/// @return A shared pointer of an ASTString.
/// Can contain a nullptr in which case indicates absence of documentation
ASTPointer<ASTString> documentation() const;
@@ -1028,13 +1031,12 @@ private:
std::vector<std::string> m_parameterNames;
std::vector<std::string> m_returnParameterNames;
Kind const m_kind;
+ StateMutability m_stateMutability = StateMutability::NonPayable;
/// true if the function takes an arbitrary number of arguments of arbitrary types
bool const m_arbitraryParameters = false;
bool const m_gasSet = false; ///< true iff the gas value to be used is on the stack
bool const m_valueSet = false; ///< true iff the value to be sent is on the stack
bool const m_bound = false; ///< true iff the function is called as arg1.fun(arg2, ..., argn)
- bool m_isConstant = false;
- bool m_isPayable = false;
Declaration const* m_declaration = nullptr;
};
diff --git a/libsolidity/codegen/ABIFunctions.cpp b/libsolidity/codegen/ABIFunctions.cpp
new file mode 100644
index 00000000..a2938ed7
--- /dev/null
+++ b/libsolidity/codegen/ABIFunctions.cpp
@@ -0,0 +1,1074 @@
+/*
+ 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/>.
+*/
+/**
+ * @author Christian <chris@ethereum.org>
+ * @date 2017
+ * Routines that generate JULIA code related to ABI encoding, decoding and type conversions.
+ */
+
+#include <libsolidity/codegen/ABIFunctions.h>
+
+#include <libdevcore/Whiskers.h>
+
+#include <libsolidity/ast/AST.h>
+
+using namespace std;
+using namespace dev;
+using namespace dev::solidity;
+
+ABIFunctions::~ABIFunctions()
+{
+ // This throws an exception and thus might cause immediate termination, but hey,
+ // it's a failed assertion anyway :-)
+ solAssert(m_requestedFunctions.empty(), "Forgot to call ``requestedFunctions()``.");
+}
+
+string ABIFunctions::tupleEncoder(
+ TypePointers const& _givenTypes,
+ TypePointers const& _targetTypes,
+ bool _encodeAsLibraryTypes
+)
+{
+ // stack: <$value0> <$value1> ... <$value(n-1)> <$headStart>
+
+ solAssert(!_givenTypes.empty(), "");
+ size_t const headSize_ = headSize(_targetTypes);
+
+ Whiskers encoder(R"(
+ {
+ let tail := add($headStart, <headSize>)
+ <encodeElements>
+ <deepestStackElement> := tail
+ }
+ )");
+ encoder("headSize", to_string(headSize_));
+ string encodeElements;
+ size_t headPos = 0;
+ size_t stackPos = 0;
+ for (size_t i = 0; i < _givenTypes.size(); ++i)
+ {
+ solAssert(_givenTypes[i], "");
+ solAssert(_targetTypes[i], "");
+ size_t sizeOnStack = _givenTypes[i]->sizeOnStack();
+ string valueNames = "";
+ for (size_t j = 0; j < sizeOnStack; j++)
+ valueNames += "$value" + to_string(stackPos++) + ", ";
+ bool dynamic = _targetTypes[i]->isDynamicallyEncoded();
+ Whiskers elementTempl(
+ dynamic ?
+ string(R"(
+ mstore(add($headStart, <pos>), sub(tail, $headStart))
+ tail := <abiEncode>(<values> tail)
+ )") :
+ string(R"(
+ <abiEncode>(<values> add($headStart, <pos>))
+ )")
+ );
+ elementTempl("values", valueNames);
+ elementTempl("pos", to_string(headPos));
+ elementTempl("abiEncode", abiEncodingFunction(*_givenTypes[i], *_targetTypes[i], _encodeAsLibraryTypes, false));
+ encodeElements += elementTempl.render();
+ headPos += dynamic ? 0x20 : _targetTypes[i]->calldataEncodedSize();
+ }
+ solAssert(headPos == headSize_, "");
+ encoder("encodeElements", encodeElements);
+ encoder("deepestStackElement", stackPos > 0 ? "$value0" : "$headStart");
+
+ return encoder.render();
+}
+
+string ABIFunctions::requestedFunctions()
+{
+ string result;
+ for (auto const& f: m_requestedFunctions)
+ result += f.second;
+ m_requestedFunctions.clear();
+ return result;
+}
+
+string ABIFunctions::cleanupFunction(Type const& _type, bool _revertOnFailure)
+{
+ string functionName = string("cleanup_") + (_revertOnFailure ? "revert_" : "assert_") + _type.identifier();
+ return createFunction(functionName, [&]() {
+ Whiskers templ(R"(
+ function <functionName>(value) -> cleaned {
+ <body>
+ }
+ )");
+ templ("functionName", functionName);
+ switch (_type.category())
+ {
+ case Type::Category::Integer:
+ {
+ IntegerType const& type = dynamic_cast<IntegerType const&>(_type);
+ if (type.numBits() == 256)
+ templ("body", "cleaned := value");
+ else if (type.isSigned())
+ templ("body", "cleaned := signextend(" + to_string(type.numBits() / 8 - 1) + ", value)");
+ else
+ templ("body", "cleaned := and(value, " + toCompactHexWithPrefix((u256(1) << type.numBits()) - 1) + ")");
+ break;
+ }
+ case Type::Category::RationalNumber:
+ templ("body", "cleaned := value");
+ break;
+ case Type::Category::Bool:
+ templ("body", "cleaned := iszero(iszero(value))");
+ break;
+ case Type::Category::FixedPoint:
+ solUnimplemented("Fixed point types not implemented.");
+ break;
+ case Type::Category::Array:
+ solAssert(false, "Array cleanup requested.");
+ break;
+ case Type::Category::Struct:
+ solAssert(false, "Struct cleanup requested.");
+ break;
+ case Type::Category::FixedBytes:
+ {
+ FixedBytesType const& type = dynamic_cast<FixedBytesType const&>(_type);
+ if (type.numBytes() == 32)
+ templ("body", "cleaned := value");
+ else if (type.numBytes() == 0)
+ templ("body", "cleaned := 0");
+ else
+ {
+ size_t numBits = type.numBytes() * 8;
+ u256 mask = ((u256(1) << numBits) - 1) << (256 - numBits);
+ templ("body", "cleaned := and(value, " + toCompactHexWithPrefix(mask) + ")");
+ }
+ break;
+ }
+ case Type::Category::Contract:
+ templ("body", "cleaned := " + cleanupFunction(IntegerType(0, IntegerType::Modifier::Address)) + "(value)");
+ break;
+ case Type::Category::Enum:
+ {
+ size_t members = dynamic_cast<EnumType const&>(_type).numberOfMembers();
+ solAssert(members > 0, "empty enum should have caused a parser error.");
+ Whiskers w("switch lt(value, <members>) case 0 { <failure> } cleaned := value");
+ w("members", to_string(members));
+ if (_revertOnFailure)
+ w("failure", "revert(0, 0)");
+ else
+ w("failure", "invalid()");
+ templ("body", w.render());
+ break;
+ }
+ default:
+ solAssert(false, "Cleanup of type " + _type.identifier() + " requested.");
+ }
+
+ return templ.render();
+ });
+}
+
+string ABIFunctions::conversionFunction(Type const& _from, Type const& _to)
+{
+ string functionName =
+ "convert_" +
+ _from.identifier() +
+ "_to_" +
+ _to.identifier();
+ return createFunction(functionName, [&]() {
+ Whiskers templ(R"(
+ function <functionName>(value) -> converted {
+ <body>
+ }
+ )");
+ templ("functionName", functionName);
+ string body;
+ auto toCategory = _to.category();
+ auto fromCategory = _from.category();
+ switch (fromCategory)
+ {
+ case Type::Category::Integer:
+ case Type::Category::RationalNumber:
+ case Type::Category::Contract:
+ {
+ if (RationalNumberType const* rational = dynamic_cast<RationalNumberType const*>(&_from))
+ solUnimplementedAssert(!rational->isFractional(), "Not yet implemented - FixedPointType.");
+ if (toCategory == Type::Category::FixedBytes)
+ {
+ solAssert(
+ fromCategory == Type::Category::Integer || fromCategory == Type::Category::RationalNumber,
+ "Invalid conversion to FixedBytesType requested."
+ );
+ FixedBytesType const& toBytesType = dynamic_cast<FixedBytesType const&>(_to);
+ body =
+ Whiskers("converted := <shiftLeft>(<clean>(value))")
+ ("shiftLeft", shiftLeftFunction(256 - toBytesType.numBytes() * 8))
+ ("clean", cleanupFunction(_from))
+ .render();
+ }
+ else if (toCategory == Type::Category::Enum)
+ {
+ solAssert(_from.mobileType(), "");
+ body =
+ Whiskers("converted := <cleanEnum>(<cleanInt>(value))")
+ ("cleanEnum", cleanupFunction(_to, false))
+ // "mobileType()" returns integer type for rational
+ ("cleanInt", cleanupFunction(*_from.mobileType()))
+ .render();
+ }
+ else if (toCategory == Type::Category::FixedPoint)
+ {
+ solUnimplemented("Not yet implemented - FixedPointType.");
+ }
+ else
+ {
+ solAssert(
+ toCategory == Type::Category::Integer ||
+ toCategory == Type::Category::Contract,
+ "");
+ IntegerType const addressType(0, IntegerType::Modifier::Address);
+ IntegerType const& to =
+ toCategory == Type::Category::Integer ?
+ dynamic_cast<IntegerType const&>(_to) :
+ addressType;
+
+ // Clean according to the "to" type, except if this is
+ // a widening conversion.
+ IntegerType const* cleanupType = &to;
+ if (fromCategory != Type::Category::RationalNumber)
+ {
+ IntegerType const& from =
+ fromCategory == Type::Category::Integer ?
+ dynamic_cast<IntegerType const&>(_from) :
+ addressType;
+ if (to.numBits() > from.numBits())
+ cleanupType = &from;
+ }
+ body =
+ Whiskers("converted := <cleanInt>(value)")
+ ("cleanInt", cleanupFunction(*cleanupType))
+ .render();
+ }
+ break;
+ }
+ case Type::Category::Bool:
+ {
+ solAssert(_from == _to, "Invalid conversion for bool.");
+ body =
+ Whiskers("converted := <clean>(value)")
+ ("clean", cleanupFunction(_from))
+ .render();
+ break;
+ }
+ case Type::Category::FixedPoint:
+ solUnimplemented("Fixed point types not implemented.");
+ break;
+ case Type::Category::Array:
+ solUnimplementedAssert(false, "Array conversion not implemented.");
+ break;
+ case Type::Category::Struct:
+ solUnimplementedAssert(false, "Struct conversion not implemented.");
+ break;
+ case Type::Category::FixedBytes:
+ {
+ FixedBytesType const& from = dynamic_cast<FixedBytesType const&>(_from);
+ if (toCategory == Type::Category::Integer)
+ body =
+ Whiskers("converted := <convert>(<shift>(value))")
+ ("shift", shiftRightFunction(256 - from.numBytes() * 8, false))
+ ("convert", conversionFunction(IntegerType(from.numBytes() * 8), _to))
+ .render();
+ else
+ {
+ // clear for conversion to longer bytes
+ solAssert(toCategory == Type::Category::FixedBytes, "Invalid type conversion requested.");
+ body =
+ Whiskers("converted := <clean>(value)")
+ ("clean", cleanupFunction(from))
+ .render();
+ }
+ break;
+ }
+ case Type::Category::Function:
+ {
+ solAssert(false, "Conversion should not be called for function types.");
+ break;
+ }
+ case Type::Category::Enum:
+ {
+ solAssert(toCategory == Type::Category::Integer || _from == _to, "");
+ EnumType const& enumType = dynamic_cast<decltype(enumType)>(_from);
+ body =
+ Whiskers("converted := <clean>(value)")
+ ("clean", cleanupFunction(enumType))
+ .render();
+ break;
+ }
+ case Type::Category::Tuple:
+ {
+ solUnimplementedAssert(false, "Tuple conversion not implemented.");
+ break;
+ }
+ default:
+ solAssert(false, "");
+ }
+
+ solAssert(!body.empty(), "");
+ templ("body", body);
+ return templ.render();
+ });
+}
+
+string ABIFunctions::cleanupCombinedExternalFunctionIdFunction()
+{
+ string functionName = "cleanup_combined_external_function_id";
+ return createFunction(functionName, [&]() {
+ return Whiskers(R"(
+ function <functionName>(addr_and_selector) -> cleaned {
+ cleaned := <clean>(addr_and_selector)
+ }
+ )")
+ ("functionName", functionName)
+ ("clean", cleanupFunction(FixedBytesType(24)))
+ .render();
+ });
+}
+
+string ABIFunctions::combineExternalFunctionIdFunction()
+{
+ string functionName = "combine_external_function_id";
+ return createFunction(functionName, [&]() {
+ return Whiskers(R"(
+ function <functionName>(addr, selector) -> combined {
+ combined := <shl64>(or(<shl32>(addr), and(selector, 0xffffffff)))
+ }
+ )")
+ ("functionName", functionName)
+ ("shl32", shiftLeftFunction(32))
+ ("shl64", shiftLeftFunction(64))
+ .render();
+ });
+}
+
+string ABIFunctions::abiEncodingFunction(
+ Type const& _from,
+ Type const& _to,
+ bool _encodeAsLibraryTypes,
+ bool _compacted
+)
+{
+ solUnimplementedAssert(
+ _to.mobileType() &&
+ _to.mobileType()->interfaceType(_encodeAsLibraryTypes) &&
+ _to.mobileType()->interfaceType(_encodeAsLibraryTypes)->encodingType(),
+ "Encoding type \"" + _to.toString() + "\" not yet implemented."
+ );
+ TypePointer toInterface = _to.mobileType()->interfaceType(_encodeAsLibraryTypes)->encodingType();
+ Type const& to = *toInterface;
+
+ if (_from.category() == Type::Category::StringLiteral)
+ return abiEncodingFunctionStringLiteral(_from, to, _encodeAsLibraryTypes);
+ else if (auto toArray = dynamic_cast<ArrayType const*>(&to))
+ {
+ solAssert(_from.category() == Type::Category::Array, "");
+ solAssert(to.dataStoredIn(DataLocation::Memory), "");
+ ArrayType const& fromArray = dynamic_cast<ArrayType const&>(_from);
+ if (fromArray.location() == DataLocation::CallData)
+ return abiEncodingFunctionCalldataArray(fromArray, *toArray, _encodeAsLibraryTypes);
+ else if (!fromArray.isByteArray() && (
+ fromArray.location() == DataLocation::Memory ||
+ fromArray.baseType()->storageBytes() > 16
+ ))
+ return abiEncodingFunctionSimpleArray(fromArray, *toArray, _encodeAsLibraryTypes);
+ else if (fromArray.location() == DataLocation::Memory)
+ return abiEncodingFunctionMemoryByteArray(fromArray, *toArray, _encodeAsLibraryTypes);
+ else if (fromArray.location() == DataLocation::Storage)
+ return abiEncodingFunctionCompactStorageArray(fromArray, *toArray, _encodeAsLibraryTypes);
+ else
+ solAssert(false, "");
+ }
+ else if (dynamic_cast<StructType const*>(&to))
+ {
+ solUnimplementedAssert(false, "Structs not yet implemented.");
+ }
+ else if (_from.category() == Type::Category::Function)
+ return abiEncodingFunctionFunctionType(
+ dynamic_cast<FunctionType const&>(_from),
+ to,
+ _encodeAsLibraryTypes,
+ _compacted
+ );
+
+ solAssert(_from.sizeOnStack() == 1, "");
+ solAssert(to.isValueType(), "");
+ solAssert(to.calldataEncodedSize() == 32, "");
+ string functionName =
+ "abi_encode_" +
+ _from.identifier() +
+ "_to_" +
+ to.identifier() +
+ (_encodeAsLibraryTypes ? "_library" : "");
+ return createFunction(functionName, [&]() {
+ solAssert(!to.isDynamicallyEncoded(), "");
+
+ Whiskers templ(R"(
+ function <functionName>(value, pos) {
+ mstore(pos, <cleanupConvert>)
+ }
+ )");
+ templ("functionName", functionName);
+
+ if (_from.dataStoredIn(DataLocation::Storage) && to.isValueType())
+ {
+ // special case: convert storage reference type to value type - this is only
+ // possible for library calls where we just forward the storage reference
+ solAssert(_encodeAsLibraryTypes, "");
+ solAssert(to == IntegerType(256), "");
+ templ("cleanupConvert", "value");
+ }
+ else
+ {
+ if (_from == to)
+ templ("cleanupConvert", cleanupFunction(_from) + "(value)");
+ else
+ templ("cleanupConvert", conversionFunction(_from, to) + "(value)");
+ }
+ return templ.render();
+ });
+}
+
+string ABIFunctions::abiEncodingFunctionCalldataArray(
+ Type const& _from,
+ Type const& _to,
+ bool _encodeAsLibraryTypes
+)
+{
+ solAssert(_to.isDynamicallySized(), "");
+ solAssert(_from.category() == Type::Category::Array, "Unknown dynamic type.");
+ solAssert(_to.category() == Type::Category::Array, "Unknown dynamic type.");
+ auto const& fromArrayType = dynamic_cast<ArrayType const&>(_from);
+ auto const& toArrayType = dynamic_cast<ArrayType const&>(_to);
+
+ solAssert(fromArrayType.location() == DataLocation::CallData, "");
+
+ solAssert(
+ *fromArrayType.copyForLocation(DataLocation::Memory, true) ==
+ *toArrayType.copyForLocation(DataLocation::Memory, true),
+ ""
+ );
+
+ string functionName =
+ "abi_encode_" +
+ _from.identifier() +
+ "_to_" +
+ _to.identifier() +
+ (_encodeAsLibraryTypes ? "_library" : "");
+ return createFunction(functionName, [&]() {
+ solUnimplementedAssert(fromArrayType.isByteArray(), "");
+ // TODO if this is not a byte array, we might just copy byte-by-byte anyway,
+ // because the encoding is position-independent, but we have to check that.
+ Whiskers templ(R"(
+ function <functionName>(start, length, pos) -> end {
+ <storeLength> // might update pos
+ <copyFun>(start, pos, length)
+ end := add(pos, <roundUpFun>(length))
+ }
+ )");
+ templ("storeLength", _to.isDynamicallySized() ? "mstore(pos, length) pos := add(pos, 0x20)" : "");
+ templ("functionName", functionName);
+ templ("copyFun", copyToMemoryFunction(true));
+ templ("roundUpFun", roundUpFunction());
+ return templ.render();
+ });
+}
+
+string ABIFunctions::abiEncodingFunctionSimpleArray(
+ ArrayType const& _from,
+ ArrayType const& _to,
+ bool _encodeAsLibraryTypes
+)
+{
+ string functionName =
+ "abi_encode_" +
+ _from.identifier() +
+ "_to_" +
+ _to.identifier() +
+ (_encodeAsLibraryTypes ? "_library" : "");
+
+ solAssert(_from.isDynamicallySized() == _to.isDynamicallySized(), "");
+ solAssert(_from.length() == _to.length(), "");
+ solAssert(_from.dataStoredIn(DataLocation::Memory) || _from.dataStoredIn(DataLocation::Storage), "");
+ solAssert(!_from.isByteArray(), "");
+ solAssert(_from.dataStoredIn(DataLocation::Memory) || _from.baseType()->storageBytes() > 16, "");
+
+ return createFunction(functionName, [&]() {
+ bool dynamic = _to.isDynamicallyEncoded();
+ bool dynamicBase = _to.baseType()->isDynamicallyEncoded();
+ bool inMemory = _from.dataStoredIn(DataLocation::Memory);
+ Whiskers templ(
+ dynamicBase ?
+ R"(
+ function <functionName>(value, pos) <return> {
+ let length := <lengthFun>(value)
+ <storeLength> // might update pos
+ let headStart := pos
+ let tail := add(pos, mul(length, 0x20))
+ let srcPtr := <dataAreaFun>(value)
+ for { let i := 0 } lt(i, length) { i := add(i, 1) }
+ {
+ mstore(pos, sub(tail, headStart))
+ tail := <encodeToMemoryFun>(<arrayElementAccess>(srcPtr), tail)
+ srcPtr := <nextArrayElement>(srcPtr)
+ pos := add(pos, <elementEncodedSize>)
+ }
+ pos := tail
+ <assignEnd>
+ }
+ )" :
+ R"(
+ function <functionName>(value, pos) <return> {
+ let length := <lengthFun>(value)
+ <storeLength> // might update pos
+ let srcPtr := <dataAreaFun>(value)
+ for { let i := 0 } lt(i, length) { i := add(i, 1) }
+ {
+ <encodeToMemoryFun>(<arrayElementAccess>(srcPtr), pos)
+ srcPtr := <nextArrayElement>(srcPtr)
+ pos := add(pos, <elementEncodedSize>)
+ }
+ <assignEnd>
+ }
+ )"
+ );
+ templ("functionName", functionName);
+ templ("return", dynamic ? " -> end " : "");
+ templ("assignEnd", dynamic ? "end := pos" : "");
+ templ("lengthFun", arrayLengthFunction(_from));
+ if (_to.isDynamicallySized())
+ templ("storeLength", "mstore(pos, length) pos := add(pos, 0x20)");
+ else
+ templ("storeLength", "");
+ templ("dataAreaFun", arrayDataAreaFunction(_from));
+ templ("elementEncodedSize", toCompactHexWithPrefix(_to.baseType()->calldataEncodedSize()));
+ templ("encodeToMemoryFun", abiEncodingFunction(
+ *_from.baseType(),
+ *_to.baseType(),
+ _encodeAsLibraryTypes,
+ true
+ ));
+ templ("arrayElementAccess", inMemory ? "mload" : "sload");
+ templ("nextArrayElement", nextArrayElementFunction(_from));
+ return templ.render();
+ });
+}
+
+string ABIFunctions::abiEncodingFunctionMemoryByteArray(
+ ArrayType const& _from,
+ ArrayType const& _to,
+ bool _encodeAsLibraryTypes
+)
+{
+ string functionName =
+ "abi_encode_" +
+ _from.identifier() +
+ "_to_" +
+ _to.identifier() +
+ (_encodeAsLibraryTypes ? "_library" : "");
+
+ solAssert(_from.isDynamicallySized() == _to.isDynamicallySized(), "");
+ solAssert(_from.length() == _to.length(), "");
+ solAssert(_from.dataStoredIn(DataLocation::Memory), "");
+ solAssert(_from.isByteArray(), "");
+
+ return createFunction(functionName, [&]() {
+ solAssert(_to.isByteArray(), "");
+ Whiskers templ(R"(
+ function <functionName>(value, pos) -> end {
+ let length := <lengthFun>(value)
+ mstore(pos, length)
+ <copyFun>(add(value, 0x20), add(pos, 0x20), length)
+ end := add(add(pos, 0x20), <roundUpFun>(length))
+ }
+ )");
+ templ("functionName", functionName);
+ templ("lengthFun", arrayLengthFunction(_from));
+ templ("copyFun", copyToMemoryFunction(false));
+ templ("roundUpFun", roundUpFunction());
+ return templ.render();
+ });
+}
+
+string ABIFunctions::abiEncodingFunctionCompactStorageArray(
+ ArrayType const& _from,
+ ArrayType const& _to,
+ bool _encodeAsLibraryTypes
+)
+{
+ string functionName =
+ "abi_encode_" +
+ _from.identifier() +
+ "_to_" +
+ _to.identifier() +
+ (_encodeAsLibraryTypes ? "_library" : "");
+
+ solAssert(_from.isDynamicallySized() == _to.isDynamicallySized(), "");
+ solAssert(_from.length() == _to.length(), "");
+ solAssert(_from.dataStoredIn(DataLocation::Storage), "");
+
+ return createFunction(functionName, [&]() {
+ if (_from.isByteArray())
+ {
+ solAssert(_to.isByteArray(), "");
+ Whiskers templ(R"(
+ function <functionName>(value, pos) -> ret {
+ let slotValue := sload(value)
+ switch and(slotValue, 1)
+ case 0 {
+ // short byte array
+ let length := and(div(slotValue, 2), 0x7f)
+ mstore(pos, length)
+ mstore(add(pos, 0x20), and(slotValue, not(0xff)))
+ ret := add(pos, 0x40)
+ }
+ case 1 {
+ // long byte array
+ let length := div(slotValue, 2)
+ mstore(pos, length)
+ pos := add(pos, 0x20)
+ let dataPos := <arrayDataSlot>(value)
+ let i := 0
+ for { } lt(i, length) { i := add(i, 0x20) } {
+ mstore(add(pos, i), sload(dataPos))
+ dataPos := add(dataPos, 1)
+ }
+ ret := add(pos, i)
+ }
+ }
+ )");
+ templ("functionName", functionName);
+ templ("arrayDataSlot", arrayDataAreaFunction(_from));
+ return templ.render();
+ }
+ else
+ {
+ // Multiple items per slot
+ solAssert(_from.baseType()->storageBytes() <= 16, "");
+ solAssert(!_from.baseType()->isDynamicallyEncoded(), "");
+ solAssert(_from.baseType()->isValueType(), "");
+ bool dynamic = _to.isDynamicallyEncoded();
+ size_t storageBytes = _from.baseType()->storageBytes();
+ size_t itemsPerSlot = 32 / storageBytes;
+ // This always writes full slot contents to memory, which might be
+ // more than desired, i.e. it writes beyond the end of memory.
+ Whiskers templ(
+ R"(
+ function <functionName>(value, pos) <return> {
+ let length := <lengthFun>(value)
+ <storeLength> // might update pos
+ let originalPos := pos
+ let srcPtr := <dataArea>(value)
+ for { let i := 0 } lt(i, length) { i := add(i, <itemsPerSlot>) }
+ {
+ let data := sload(srcPtr)
+ <#items>
+ <encodeToMemoryFun>(<shiftRightFun>(data), pos)
+ pos := add(pos, <elementEncodedSize>)
+ </items>
+ srcPtr := add(srcPtr, 1)
+ }
+ pos := add(originalPos, mul(length, <elementEncodedSize>))
+ <assignEnd>
+ }
+ )"
+ );
+ templ("functionName", functionName);
+ templ("return", dynamic ? " -> end " : "");
+ templ("assignEnd", dynamic ? "end := pos" : "");
+ templ("lengthFun", arrayLengthFunction(_from));
+ if (_to.isDynamicallySized())
+ templ("storeLength", "mstore(pos, length) pos := add(pos, 0x20)");
+ else
+ templ("storeLength", "");
+ templ("dataArea", arrayDataAreaFunction(_from));
+ templ("itemsPerSlot", to_string(itemsPerSlot));
+ string elementEncodedSize = toCompactHexWithPrefix(_to.baseType()->calldataEncodedSize());
+ templ("elementEncodedSize", elementEncodedSize);
+ string encodeToMemoryFun = abiEncodingFunction(
+ *_from.baseType(),
+ *_to.baseType(),
+ _encodeAsLibraryTypes,
+ true
+ );
+ templ("encodeToMemoryFun", encodeToMemoryFun);
+ std::vector<std::map<std::string, std::string>> items(itemsPerSlot);
+ for (size_t i = 0; i < itemsPerSlot; ++i)
+ items[i]["shiftRightFun"] = shiftRightFunction(i * storageBytes * 8, false);
+ templ("items", items);
+ return templ.render();
+ }
+ });
+}
+
+string ABIFunctions::abiEncodingFunctionStringLiteral(
+ Type const& _from,
+ Type const& _to,
+ bool _encodeAsLibraryTypes
+)
+{
+ solAssert(_from.category() == Type::Category::StringLiteral, "");
+
+ string functionName =
+ "abi_encode_" +
+ _from.identifier() +
+ "_to_" +
+ _to.identifier() +
+ (_encodeAsLibraryTypes ? "_library" : "");
+ return createFunction(functionName, [&]() {
+ auto const& strType = dynamic_cast<StringLiteralType const&>(_from);
+ string const& value = strType.value();
+ solAssert(_from.sizeOnStack() == 0, "");
+
+ if (_to.isDynamicallySized())
+ {
+ Whiskers templ(R"(
+ function <functionName>(pos) -> end {
+ mstore(pos, <length>)
+ <#word>
+ mstore(add(pos, <offset>), <wordValue>)
+ </word>
+ end := add(pos, <overallSize>)
+ }
+ )");
+ templ("functionName", functionName);
+
+ // TODO this can make use of CODECOPY for large strings once we have that in JULIA
+ size_t words = (value.size() + 31) / 32;
+ templ("overallSize", to_string(32 + words * 32));
+ templ("length", to_string(value.size()));
+ vector<map<string, string>> wordParams(words);
+ for (size_t i = 0; i < words; ++i)
+ {
+ wordParams[i]["offset"] = to_string(32 + i * 32);
+ wordParams[i]["wordValue"] = "0x" + h256(value.substr(32 * i, 32), h256::AlignLeft).hex();
+ }
+ templ("word", wordParams);
+ return templ.render();
+ }
+ else
+ {
+ solAssert(_to.category() == Type::Category::FixedBytes, "");
+ solAssert(value.size() <= 32, "");
+ Whiskers templ(R"(
+ function <functionName>(pos) {
+ mstore(pos, <wordValue>)
+ }
+ )");
+ templ("functionName", functionName);
+ templ("wordValue", "0x" + h256(value, h256::AlignLeft).hex());
+ return templ.render();
+ }
+ });
+}
+
+string ABIFunctions::abiEncodingFunctionFunctionType(
+ FunctionType const& _from,
+ Type const& _to,
+ bool _encodeAsLibraryTypes,
+ bool _compacted
+)
+{
+ solAssert(_from.kind() == FunctionType::Kind::External, "");
+ solAssert(_from == _to, "");
+
+ string functionName =
+ "abi_encode_" +
+ _from.identifier() +
+ "_to_" +
+ _to.identifier() +
+ (_compacted ? "_compacted" : "") +
+ (_encodeAsLibraryTypes ? "_library" : "");
+
+ if (_compacted)
+ {
+ return createFunction(functionName, [&]() {
+ return Whiskers(R"(
+ function <functionName>(addr_and_function_id, pos) {
+ mstore(pos, <cleanExtFun>(addr_and_function_id))
+ }
+ )")
+ ("functionName", functionName)
+ ("cleanExtFun", cleanupCombinedExternalFunctionIdFunction())
+ .render();
+ });
+ }
+ else
+ {
+ return createFunction(functionName, [&]() {
+ return Whiskers(R"(
+ function <functionName>(addr, function_id, pos) {
+ mstore(pos, <combineExtFun>(addr, function_id))
+ }
+ )")
+ ("functionName", functionName)
+ ("combineExtFun", combineExternalFunctionIdFunction())
+ .render();
+ });
+ }
+}
+
+string ABIFunctions::copyToMemoryFunction(bool _fromCalldata)
+{
+ string functionName = "copy_" + string(_fromCalldata ? "calldata" : "memory") + "_to_memory";
+ return createFunction(functionName, [&]() {
+ if (_fromCalldata)
+ {
+ return Whiskers(R"(
+ function <functionName>(src, dst, length) {
+ calldatacopy(dst, src, length)
+ // clear end
+ mstore(add(dst, length), 0)
+ }
+ )")
+ ("functionName", functionName)
+ .render();
+ }
+ else
+ {
+ return Whiskers(R"(
+ function <functionName>(src, dst, length) {
+ let i := 0
+ for { } lt(i, length) { i := add(i, 32) }
+ {
+ mstore(add(dst, i), mload(add(src, i)))
+ }
+ switch eq(i, length)
+ case 0 {
+ // clear end
+ mstore(add(dst, length), 0)
+ }
+ }
+ )")
+ ("functionName", functionName)
+ .render();
+ }
+ });
+}
+
+string ABIFunctions::shiftLeftFunction(size_t _numBits)
+{
+ string functionName = "shift_left_" + to_string(_numBits);
+ return createFunction(functionName, [&]() {
+ solAssert(_numBits < 256, "");
+ return
+ Whiskers(R"(function <functionName>(value) -> newValue {
+ newValue := mul(value, <multiplier>)
+ })")
+ ("functionName", functionName)
+ ("multiplier", toCompactHexWithPrefix(u256(1) << _numBits))
+ .render();
+ });
+}
+
+string ABIFunctions::shiftRightFunction(size_t _numBits, bool _signed)
+{
+ string functionName = "shift_right_" + to_string(_numBits) + (_signed ? "_signed" : "_unsigned");
+ return createFunction(functionName, [&]() {
+ solAssert(_numBits < 256, "");
+ return
+ Whiskers(R"(function <functionName>(value) -> newValue {
+ newValue := <div>(value, <multiplier>)
+ })")
+ ("functionName", functionName)
+ ("div", _signed ? "sdiv" : "div")
+ ("multiplier", toCompactHexWithPrefix(u256(1) << _numBits))
+ .render();
+ });
+}
+
+string ABIFunctions::roundUpFunction()
+{
+ string functionName = "round_up_to_mul_of_32";
+ return createFunction(functionName, [&]() {
+ return
+ Whiskers(R"(function <functionName>(value) -> result {
+ result := and(add(value, 31), not(31))
+ })")
+ ("functionName", functionName)
+ .render();
+ });
+}
+
+string ABIFunctions::arrayLengthFunction(ArrayType const& _type)
+{
+ string functionName = "array_length_" + _type.identifier();
+ return createFunction(functionName, [&]() {
+ Whiskers w(R"(
+ function <functionName>(value) -> length {
+ <body>
+ }
+ )");
+ w("functionName", functionName);
+ string body;
+ if (!_type.isDynamicallySized())
+ body = "length := " + toCompactHexWithPrefix(_type.length());
+ else
+ {
+ switch (_type.location())
+ {
+ case DataLocation::CallData:
+ solAssert(false, "called regular array length function on calldata array");
+ break;
+ case DataLocation::Memory:
+ body = "length := mload(value)";
+ break;
+ case DataLocation::Storage:
+ if (_type.isByteArray())
+ {
+ // Retrieve length both for in-place strings and off-place strings:
+ // Computes (x & (0x100 * (ISZERO (x & 1)) - 1)) / 2
+ // i.e. for short strings (x & 1 == 0) it does (x & 0xff) / 2 and for long strings it
+ // computes (x & (-1)) / 2, which is equivalent to just x / 2.
+ body = R"(
+ length := sload(value)
+ let mask := sub(mul(0x100, iszero(and(length, 1))), 1)
+ length := div(and(length, mask), 2)
+ )";
+ }
+ else
+ body = "length := sload(value)";
+ break;
+ }
+ }
+ solAssert(!body.empty(), "");
+ w("body", body);
+ return w.render();
+ });
+}
+
+string ABIFunctions::arrayDataAreaFunction(ArrayType const& _type)
+{
+ string functionName = "array_dataslot_" + _type.identifier();
+ return createFunction(functionName, [&]() {
+ if (_type.dataStoredIn(DataLocation::Memory))
+ {
+ if (_type.isDynamicallySized())
+ return Whiskers(R"(
+ function <functionName>(memPtr) -> dataPtr {
+ dataPtr := add(memPtr, 0x20)
+ }
+ )")
+ ("functionName", functionName)
+ .render();
+ else
+ return Whiskers(R"(
+ function <functionName>(memPtr) -> dataPtr {
+ dataPtr := memPtr
+ }
+ )")
+ ("functionName", functionName)
+ .render();
+ }
+ else if (_type.dataStoredIn(DataLocation::Storage))
+ {
+ if (_type.isDynamicallySized())
+ {
+ Whiskers w(R"(
+ function <functionName>(slot) -> dataSlot {
+ mstore(0, slot)
+ dataSlot := keccak256(0, 0x20)
+ }
+ )");
+ w("functionName", functionName);
+ return w.render();
+ }
+ else
+ {
+ Whiskers w(R"(
+ function <functionName>(slot) -> dataSlot {
+ dataSlot := slot
+ }
+ )");
+ w("functionName", functionName);
+ return w.render();
+ }
+ }
+ else
+ {
+ // Not used for calldata
+ solAssert(false, "");
+ }
+ });
+}
+
+string ABIFunctions::nextArrayElementFunction(ArrayType const& _type)
+{
+ solAssert(!_type.isByteArray(), "");
+ solAssert(
+ _type.location() == DataLocation::Memory ||
+ _type.location() == DataLocation::Storage,
+ ""
+ );
+ solAssert(
+ _type.location() == DataLocation::Memory ||
+ _type.baseType()->storageBytes() > 16,
+ ""
+ );
+ string functionName = "array_nextElement_" + _type.identifier();
+ return createFunction(functionName, [&]() {
+ if (_type.location() == DataLocation::Memory)
+ return Whiskers(R"(
+ function <functionName>(memPtr) -> nextPtr {
+ nextPtr := add(memPtr, 0x20)
+ }
+ )")
+ ("functionName", functionName)
+ .render();
+ else if (_type.location() == DataLocation::Storage)
+ return Whiskers(R"(
+ function <functionName>(slot) -> nextSlot {
+ nextSlot := add(slot, 1)
+ }
+ )")
+ ("functionName", functionName)
+ .render();
+ else
+ solAssert(false, "");
+ });
+}
+
+string ABIFunctions::createFunction(string const& _name, function<string ()> const& _creator)
+{
+ if (!m_requestedFunctions.count(_name))
+ {
+ auto fun = _creator();
+ solAssert(!fun.empty(), "");
+ m_requestedFunctions[_name] = fun;
+ }
+ return _name;
+}
+
+size_t ABIFunctions::headSize(TypePointers const& _targetTypes)
+{
+ size_t headSize = 0;
+ for (auto const& t: _targetTypes)
+ {
+ if (t->isDynamicallyEncoded())
+ headSize += 0x20;
+ else
+ {
+ solAssert(t->calldataEncodedSize() > 0, "");
+ headSize += t->calldataEncodedSize();
+ }
+ }
+
+ return headSize;
+}
diff --git a/libsolidity/codegen/ABIFunctions.h b/libsolidity/codegen/ABIFunctions.h
new file mode 100644
index 00000000..76f4b467
--- /dev/null
+++ b/libsolidity/codegen/ABIFunctions.h
@@ -0,0 +1,172 @@
+/*
+ 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/>.
+*/
+/**
+ * @author Christian <chris@ethereum.org>
+ * @date 2017
+ * Routines that generate JULIA code related to ABI encoding, decoding and type conversions.
+ */
+
+#pragma once
+
+#include <libsolidity/ast/ASTForward.h>
+
+#include <vector>
+#include <functional>
+#include <map>
+
+namespace dev {
+namespace solidity {
+
+class Type;
+class ArrayType;
+class StructType;
+class FunctionType;
+using TypePointer = std::shared_ptr<Type const>;
+using TypePointers = std::vector<TypePointer>;
+
+///
+/// Class to generate encoding and decoding functions. Also maintains a collection
+/// of "functions to be generated" in order to avoid generating the same function
+/// multiple times.
+///
+/// Make sure to include the result of ``requestedFunctions()`` to a block that
+/// is visible from the code that was generated here.
+class ABIFunctions
+{
+public:
+ ~ABIFunctions();
+
+ /// @returns assembly code block to ABI-encode values of @a _givenTypes residing on the stack
+ /// into memory, converting the types to @a _targetTypes on the fly.
+ /// Assumed variables to be present: <$value0> <$value1> ... <$value(n-1)> <$headStart>
+ /// Does not allocate memory (does not change the memory head pointer), but writes
+ /// to memory starting at $headStart and an unrestricted amount after that.
+ /// Assigns the end of encoded memory either to $value0 or (if that is not present)
+ /// to $headStart.
+ std::string tupleEncoder(
+ TypePointers const& _givenTypes,
+ TypePointers const& _targetTypes,
+ bool _encodeAsLibraryTypes = false
+ );
+
+ /// @returns auxiliary functions referenced from the block generated in @a tupleEncoder
+ std::string requestedFunctions();
+
+private:
+ /// @returns the name of the cleanup function for the given type and
+ /// adds its implementation to the requested functions.
+ /// @param _revertOnFailure if true, causes revert on invalid data,
+ /// otherwise an assertion failure.
+ std::string cleanupFunction(Type const& _type, bool _revertOnFailure = false);
+
+ /// @returns the name of the function that converts a value of type @a _from
+ /// to a value of type @a _to. The resulting vale is guaranteed to be in range
+ /// (i.e. "clean"). Asserts on failure.
+ std::string conversionFunction(Type const& _from, Type const& _to);
+
+ std::string cleanupCombinedExternalFunctionIdFunction();
+
+ /// @returns a function that combines the address and selector to a single value
+ /// for use in the ABI.
+ std::string combineExternalFunctionIdFunction();
+
+ /// @returns the name of the ABI encoding function with the given type
+ /// and queues the generation of the function to the requested functions.
+ /// @param _compacted if true, the input value was just loaded from storage
+ /// or memory and thus might be compacted into a single slot (depending on the type).
+ std::string abiEncodingFunction(
+ Type const& _givenType,
+ Type const& _targetType,
+ bool _encodeAsLibraryTypes,
+ bool _compacted
+ );
+ /// Part of @a abiEncodingFunction for array target type and given calldata array.
+ std::string abiEncodingFunctionCalldataArray(
+ Type const& _givenType,
+ Type const& _targetType,
+ bool _encodeAsLibraryTypes
+ );
+ /// Part of @a abiEncodingFunction for array target type and given memory array or
+ /// a given storage array with one item per slot.
+ std::string abiEncodingFunctionSimpleArray(
+ ArrayType const& _givenType,
+ ArrayType const& _targetType,
+ bool _encodeAsLibraryTypes
+ );
+ std::string abiEncodingFunctionMemoryByteArray(
+ ArrayType const& _givenType,
+ ArrayType const& _targetType,
+ bool _encodeAsLibraryTypes
+ );
+ /// Part of @a abiEncodingFunction for array target type and given storage array
+ /// where multiple items are packed into the same storage slot.
+ std::string abiEncodingFunctionCompactStorageArray(
+ ArrayType const& _givenType,
+ ArrayType const& _targetType,
+ bool _encodeAsLibraryTypes
+ );
+
+ // @returns the name of the ABI encoding function with the given type
+ // and queues the generation of the function to the requested functions.
+ // Case for _givenType being a string literal
+ std::string abiEncodingFunctionStringLiteral(
+ Type const& _givenType,
+ Type const& _targetType,
+ bool _encodeAsLibraryTypes
+ );
+
+ std::string abiEncodingFunctionFunctionType(
+ FunctionType const& _from,
+ Type const& _to,
+ bool _encodeAsLibraryTypes,
+ bool _compacted
+ );
+
+ /// @returns a function that copies raw bytes of dynamic length from calldata
+ /// or memory to memory.
+ /// Pads with zeros and might write more than exactly length.
+ std::string copyToMemoryFunction(bool _fromCalldata);
+
+ std::string shiftLeftFunction(size_t _numBits);
+ std::string shiftRightFunction(size_t _numBits, bool _signed);
+ /// @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.
+ std::string roundUpFunction();
+
+ std::string arrayLengthFunction(ArrayType const& _type);
+ /// @returns the name of a function that converts a storage slot number
+ /// or a memory pointer to the slot number / memory pointer for the data position of an array
+ /// which is stored in that slot / memory area.
+ std::string arrayDataAreaFunction(ArrayType const& _type);
+ /// @returns the name of a function that advances an array data pointer to the next element.
+ /// Only works for memory arrays and storage arrays that store one item per slot.
+ std::string nextArrayElementFunction(ArrayType const& _type);
+
+ /// Helper function that uses @a _creator to create a function and add it to
+ /// @a m_requestedFunctions if it has not been created yet and returns @a _name in both
+ /// cases.
+ std::string createFunction(std::string const& _name, std::function<std::string()> const& _creator);
+
+ /// @returns the size of the static part of the encoding of the given types.
+ size_t headSize(TypePointers const& _targetTypes);
+
+ /// Map from function name to code for a multi-use function.
+ std::map<std::string, std::string> m_requestedFunctions;
+};
+
+}
+}
diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp
index bc4de3ee..ed780d0b 100644
--- a/libsolidity/codegen/CompilerContext.cpp
+++ b/libsolidity/codegen/CompilerContext.cpp
@@ -44,11 +44,6 @@ namespace dev
namespace solidity
{
-void CompilerContext::addMagicGlobal(MagicVariableDeclaration const& _declaration)
-{
- m_magicGlobals.insert(&_declaration);
-}
-
void CompilerContext::addStateVariable(
VariableDeclaration const& _declaration,
u256 const& _storageOffset,
diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h
index 13821f67..583360ea 100644
--- a/libsolidity/codegen/CompilerContext.h
+++ b/libsolidity/codegen/CompilerContext.h
@@ -48,7 +48,7 @@ namespace solidity {
class CompilerContext
{
public:
- CompilerContext(CompilerContext* _runtimeContext = nullptr):
+ explicit CompilerContext(CompilerContext* _runtimeContext = nullptr):
m_asm(std::make_shared<eth::Assembly>()),
m_runtimeContext(_runtimeContext)
{
@@ -56,7 +56,9 @@ public:
m_runtimeSub = size_t(m_asm->newSub(m_runtimeContext->m_asm).data());
}
- void addMagicGlobal(MagicVariableDeclaration const& _declaration);
+ void setExperimentalFeatures(std::set<ExperimentalFeature> const& _features) { m_experimentalFeatures = _features; }
+ bool experimentalFeatureActive(ExperimentalFeature _feature) const { return m_experimentalFeatures.count(_feature); }
+
void addStateVariable(VariableDeclaration const& _declaration, u256 const& _storageOffset, unsigned _byteOffset);
void addVariable(VariableDeclaration const& _declaration, unsigned _offsetToCurrent = 0);
void removeVariable(VariableDeclaration const& _declaration);
@@ -68,7 +70,6 @@ public:
void adjustStackOffset(int _adjustment) { m_asm->adjustDeposit(_adjustment); }
unsigned stackHeight() const { solAssert(m_asm->deposit() >= 0, ""); return unsigned(m_asm->deposit()); }
- bool isMagicGlobal(Declaration const* _declaration) const { return m_magicGlobals.count(_declaration) != 0; }
bool isLocalVariable(Declaration const* _declaration) const;
bool isStateVariable(Declaration const* _declaration) const { return m_stateVariables.count(_declaration) != 0; }
@@ -265,8 +266,8 @@ private:
} m_functionCompilationQueue;
eth::AssemblyPointer m_asm;
- /// Magic global variables like msg, tx or this, distinguished by type.
- std::set<Declaration const*> m_magicGlobals;
+ /// Activated experimental features.
+ std::set<ExperimentalFeature> m_experimentalFeatures;
/// Other already compiled contracts to be used in contract creation calls.
std::map<ContractDefinition const*, eth::Assembly const*> m_compiledContracts;
/// Storage offsets of state variables
diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp
index 782aad9d..a0fc5d55 100644
--- a/libsolidity/codegen/CompilerUtils.cpp
+++ b/libsolidity/codegen/CompilerUtils.cpp
@@ -25,6 +25,7 @@
#include <libevmasm/Instruction.h>
#include <libsolidity/codegen/ArrayUtils.h>
#include <libsolidity/codegen/LValue.h>
+#include <libsolidity/codegen/ABIFunctions.h>
using namespace std;
@@ -182,6 +183,18 @@ void CompilerUtils::encodeToMemory(
if (_givenTypes.empty())
return;
+ else if (
+ _padToWordBoundaries &&
+ !_copyDynamicDataInPlace &&
+ m_context.experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2)
+ )
+ {
+ // Use the new JULIA-based encoding function
+ auto stackHeightBefore = m_context.stackHeight();
+ abiEncode(_givenTypes, targetTypes, _encodeAsLibraryTypes);
+ solAssert(stackHeightBefore - m_context.stackHeight() == sizeOnStack(_givenTypes), "");
+ return;
+ }
// Stack during operation:
// <v1> <v2> ... <vn> <mem_start> <dyn_head_1> ... <dyn_head_r> <end_of_mem>
@@ -289,6 +302,28 @@ void CompilerUtils::encodeToMemory(
popStackSlots(argSize + dynPointers + 1);
}
+void CompilerUtils::abiEncode(
+ TypePointers const& _givenTypes,
+ TypePointers const& _targetTypes,
+ bool _encodeAsLibraryTypes
+)
+{
+ // stack: <$value0> <$value1> ... <$value(n-1)> <$headStart>
+
+ vector<string> variables;
+ size_t numValues = sizeOnStack(_givenTypes);
+ for (size_t i = 0; i < numValues; ++i)
+ variables.push_back("$value" + to_string(i));
+ variables.push_back("$headStart");
+
+ ABIFunctions funs;
+ string routine = funs.tupleEncoder(_givenTypes, _targetTypes, _encodeAsLibraryTypes);
+ routine += funs.requestedFunctions();
+ m_context.appendInlineAssembly("{" + routine + "}", variables);
+ // Remove everyhing except for "value0" / the final memory pointer.
+ popStackSlots(numValues);
+}
+
void CompilerUtils::zeroInitialiseMemoryArray(ArrayType const& _type)
{
auto repeat = m_context.newTag();
diff --git a/libsolidity/codegen/CompilerUtils.h b/libsolidity/codegen/CompilerUtils.h
index fb169463..09427788 100644
--- a/libsolidity/codegen/CompilerUtils.h
+++ b/libsolidity/codegen/CompilerUtils.h
@@ -103,6 +103,14 @@ public:
bool _encodeAsLibraryTypes = false
);
+ /// Special case of @a encodeToMemory which assumes that everything is padded to words
+ /// and dynamic data is not copied in place (i.e. a proper ABI encoding).
+ void abiEncode(
+ TypePointers const& _givenTypes,
+ TypePointers const& _targetTypes,
+ bool _encodeAsLibraryTypes = false
+ );
+
/// Zero-initialises (the data part of) an already allocated memory array.
/// Length has to be nonzero!
/// Stack pre: <length> <memptr>
diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp
index cad388df..29a22fae 100644
--- a/libsolidity/codegen/ContractCompiler.cpp
+++ b/libsolidity/codegen/ContractCompiler.cpp
@@ -100,6 +100,7 @@ void ContractCompiler::initializeContext(
map<ContractDefinition const*, eth::Assembly const*> const& _compiledContracts
)
{
+ m_context.setExperimentalFeatures(_contract.sourceUnit().annotation().experimentalFeatures);
m_context.setCompiledContracts(_compiledContracts);
m_context.setInheritanceHierarchy(_contract.annotation().linearizedBaseContracts);
CompilerUtils(m_context).initialiseFreeMemoryPointer();
@@ -267,18 +268,13 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac
m_context << notFound;
if (fallback)
{
- m_context.setStackOffset(0);
if (!fallback->isPayable())
appendCallValueCheck();
- // Return tag is used to jump out of the function.
- eth::AssemblyItem returnTag = m_context.pushNewTag();
- fallback->accept(*this);
- m_context << returnTag;
+ solAssert(fallback->isFallback(), "");
solAssert(FunctionType(*fallback).parameterTypes().empty(), "");
solAssert(FunctionType(*fallback).returnParameterTypes().empty(), "");
- // Return tag gets consumed.
- m_context.adjustStackOffset(-1);
+ fallback->accept(*this);
m_context << Instruction::STOP;
}
else
@@ -536,7 +532,8 @@ bool ContractCompiler::visit(FunctionDefinition const& _function)
m_context.adjustStackOffset(-(int)c_returnValuesSize);
- if (!_function.isConstructor())
+ /// The constructor and the fallback function doesn't to jump out.
+ if (!_function.isConstructor() && !_function.isFallback())
m_context.appendJump(eth::AssemblyItem::JumpType::OutOfFunction);
return false;
}
diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp
index a35008bf..55d35c44 100644
--- a/libsolidity/codegen/ExpressionCompiler.cpp
+++ b/libsolidity/codegen/ExpressionCompiler.cpp
@@ -546,7 +546,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
case FunctionType::Kind::External:
case FunctionType::Kind::CallCode:
case FunctionType::Kind::DelegateCall:
- case FunctionType::Kind::Bare:
+ case FunctionType::Kind::BareCall:
case FunctionType::Kind::BareCallCode:
case FunctionType::Kind::BareDelegateCall:
_functionCall.expression().accept(*this);
@@ -642,11 +642,10 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
TypePointers{},
strings(),
strings(),
- FunctionType::Kind::Bare,
+ FunctionType::Kind::BareCall,
false,
nullptr,
- false,
- false,
+ StateMutability::NonPayable,
true,
true
),
@@ -973,7 +972,7 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
case FunctionType::Kind::DelegateCall:
case FunctionType::Kind::CallCode:
case FunctionType::Kind::Send:
- case FunctionType::Kind::Bare:
+ case FunctionType::Kind::BareCall:
case FunctionType::Kind::BareCallCode:
case FunctionType::Kind::BareDelegateCall:
case FunctionType::Kind::Transfer:
@@ -1560,7 +1559,7 @@ void ExpressionCompiler::appendExternalFunctionCall(
utils().moveToStackTop(gasValueSize, _functionType.selfType()->sizeOnStack());
auto funKind = _functionType.kind();
- bool returnSuccessCondition = funKind == FunctionType::Kind::Bare || funKind == FunctionType::Kind::BareCallCode;
+ bool returnSuccessCondition = funKind == FunctionType::Kind::BareCall || funKind == FunctionType::Kind::BareCallCode || funKind == FunctionType::Kind::BareDelegateCall;
bool isCallCode = funKind == FunctionType::Kind::BareCallCode || funKind == FunctionType::Kind::CallCode;
bool isDelegateCall = funKind == FunctionType::Kind::BareDelegateCall || funKind == FunctionType::Kind::DelegateCall;
@@ -1579,7 +1578,7 @@ void ExpressionCompiler::appendExternalFunctionCall(
TypePointers parameterTypes = _functionType.parameterTypes();
bool manualFunctionId = false;
if (
- (funKind == FunctionType::Kind::Bare || funKind == FunctionType::Kind::BareCallCode || funKind == FunctionType::Kind::BareDelegateCall) &&
+ (funKind == FunctionType::Kind::BareCall || funKind == FunctionType::Kind::BareCallCode || funKind == FunctionType::Kind::BareDelegateCall) &&
!_arguments.empty()
)
{
@@ -1622,7 +1621,7 @@ void ExpressionCompiler::appendExternalFunctionCall(
// zero bytes (which we cannot detect).
solAssert(0 < retSize && retSize <= 32, "");
utils().fetchFreeMemoryPointer();
- m_context << Instruction::DUP1 << u256(0) << Instruction::MSTORE;
+ m_context << u256(0) << Instruction::DUP2 << Instruction::MSTORE;
m_context << u256(32) << Instruction::ADD;
utils().storeFreeMemoryPointer();
}
@@ -1712,7 +1711,7 @@ void ExpressionCompiler::appendExternalFunctionCall(
u256 gasNeededByCaller = eth::GasCosts::callGas + 10;
if (_functionType.valueSet())
gasNeededByCaller += eth::GasCosts::callValueTransferGas;
- if (!isCallCode && !isDelegateCall && !existenceChecked)
+ if (!existenceChecked)
gasNeededByCaller += eth::GasCosts::callNewAccountGas; // we never know
m_context << gasNeededByCaller << Instruction::GAS << Instruction::SUB;
}
diff --git a/libsolidity/interface/ABI.cpp b/libsolidity/interface/ABI.cpp
index 12f958fc..9fc2f4e8 100644
--- a/libsolidity/interface/ABI.cpp
+++ b/libsolidity/interface/ABI.cpp
@@ -38,6 +38,7 @@ Json::Value ABI::generate(ContractDefinition const& _contractDef)
method["name"] = it.second->declaration().name();
method["constant"] = it.second->isConstant();
method["payable"] = it.second->isPayable();
+ method["statemutability"] = stateMutabilityToString(it.second->stateMutability());
method["inputs"] = formatTypeList(
externalFunctionType->parameterNames(),
externalFunctionType->parameterTypes(),
@@ -57,6 +58,7 @@ Json::Value ABI::generate(ContractDefinition const& _contractDef)
auto externalFunction = FunctionType(*_contractDef.constructor(), false).interfaceFunctionType();
solAssert(!!externalFunction, "");
method["payable"] = externalFunction->isPayable();
+ method["statemutability"] = stateMutabilityToString(externalFunction->stateMutability());
method["inputs"] = formatTypeList(
externalFunction->parameterNames(),
externalFunction->parameterTypes(),
@@ -71,6 +73,7 @@ Json::Value ABI::generate(ContractDefinition const& _contractDef)
Json::Value method;
method["type"] = "fallback";
method["payable"] = externalFunctionType->isPayable();
+ method["statemutability"] = stateMutabilityToString(externalFunctionType->stateMutability());
abi.append(method);
}
for (auto const& it: _contractDef.interfaceEvents())
diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp
index 02983a82..70bebfa5 100644
--- a/libsolidity/interface/CompilerStack.cpp
+++ b/libsolidity/interface/CompilerStack.cpp
@@ -632,6 +632,17 @@ string CompilerStack::absolutePath(string const& _path, string const& _reference
return result.generic_string();
}
+namespace
+{
+bool onlySafeExperimentalFeaturesActivated(set<ExperimentalFeature> const& features)
+{
+ for (auto const feature: features)
+ if (!ExperimentalFeatureOnlyAnalysis.count(feature))
+ return false;
+ return true;
+}
+}
+
void CompilerStack::compileContract(
ContractDefinition const& _contract,
map<ContractDefinition const*, eth::Assembly const*>& _compiledContracts
@@ -639,7 +650,7 @@ void CompilerStack::compileContract(
{
if (
_compiledContracts.count(&_contract) ||
- !_contract.annotation().isFullyImplemented ||
+ !_contract.annotation().unimplementedFunctions.empty() ||
!_contract.constructorIsPublic()
)
return;
@@ -649,10 +660,23 @@ void CompilerStack::compileContract(
shared_ptr<Compiler> compiler = make_shared<Compiler>(m_optimize, m_optimizeRuns);
Contract& compiledContract = m_contracts.at(_contract.fullyQualifiedName());
string metadata = createMetadata(compiledContract);
- bytes cborEncodedMetadata =
- // CBOR-encoding of {"bzzr0": dev::swarmHash(metadata)}
- bytes{0xa1, 0x65, 'b', 'z', 'z', 'r', '0', 0x58, 0x20} +
- dev::swarmHash(metadata).asBytes();
+ bytes cborEncodedHash =
+ // CBOR-encoding of the key "bzzr0"
+ bytes{0x65, 'b', 'z', 'z', 'r', '0'}+
+ // CBOR-encoding of the hash
+ bytes{0x58, 0x20} + dev::swarmHash(metadata).asBytes();
+ bytes cborEncodedMetadata;
+ if (onlySafeExperimentalFeaturesActivated(_contract.sourceUnit().annotation().experimentalFeatures))
+ cborEncodedMetadata =
+ // CBOR-encoding of {"bzzr0": dev::swarmHash(metadata)}
+ bytes{0xa1} +
+ cborEncodedHash;
+ else
+ cborEncodedMetadata =
+ // CBOR-encoding of {"bzzr0": dev::swarmHash(metadata), "experimental": true}
+ bytes{0xa2} +
+ cborEncodedHash +
+ bytes{0x6c, 'e', 'x', 'p', 'e', 'r', 'i', 'm', 'e', 'n', 't', 'a', 'l', 0xf5};
solAssert(cborEncodedMetadata.size() <= 0xffff, "Metadata too large");
// 16-bit big endian length
cborEncodedMetadata += toCompactBigEndian(cborEncodedMetadata.size(), 2);
@@ -935,7 +959,7 @@ Json::Value CompilerStack::gasEstimates(string const& _contractName) const
for (auto const& it: contract.definedFunctions())
{
/// Exclude externally visible functions, constructor and the fallback function
- if (it->isPartOfExternalInterface() || it->isConstructor() || it->name().empty())
+ if (it->isPartOfExternalInterface() || it->isConstructor() || it->isFallback())
continue;
size_t entry = functionEntryPoint(_contractName, *it);
@@ -943,12 +967,14 @@ Json::Value CompilerStack::gasEstimates(string const& _contractName) const
if (entry > 0)
gas = GasEstimator::functionalEstimation(*items, entry, *it);
+ /// TODO: This could move into a method shared with externalSignature()
FunctionType type(*it);
string sig = it->name() + "(";
auto paramTypes = type.parameterTypes();
for (auto it = paramTypes.begin(); it != paramTypes.end(); ++it)
sig += (*it)->toString() + (it + 1 == paramTypes.end() ? "" : ",");
sig += ")";
+
internalFunctions[sig] = gasToJson(gas);
}
diff --git a/libsolidity/interface/ErrorReporter.cpp b/libsolidity/interface/ErrorReporter.cpp
index f9ef4ceb..e6171756 100644
--- a/libsolidity/interface/ErrorReporter.cpp
+++ b/libsolidity/interface/ErrorReporter.cpp
@@ -151,6 +151,16 @@ void ErrorReporter::syntaxError(SourceLocation const& _location, string const& _
);
}
+void ErrorReporter::typeError(SourceLocation const& _location, SecondarySourceLocation const& _secondaryLocation, string const& _description)
+{
+ error(
+ Error::Type::TypeError,
+ _location,
+ _secondaryLocation,
+ _description
+ );
+}
+
void ErrorReporter::typeError(SourceLocation const& _location, string const& _description)
{
error(
diff --git a/libsolidity/interface/ErrorReporter.h b/libsolidity/interface/ErrorReporter.h
index 8b066a3e..12f4e8df 100644
--- a/libsolidity/interface/ErrorReporter.h
+++ b/libsolidity/interface/ErrorReporter.h
@@ -73,6 +73,12 @@ public:
void syntaxError(SourceLocation const& _location, std::string const& _description);
+ void typeError(
+ SourceLocation const& _location,
+ SecondarySourceLocation const& _secondaryLocation = SecondarySourceLocation(),
+ std::string const& _description = std::string()
+ );
+
void typeError(SourceLocation const& _location, std::string const& _description);
void fatalTypeError(SourceLocation const& _location, std::string const& _description);
diff --git a/libsolidity/interface/SourceReferenceFormatter.cpp b/libsolidity/interface/SourceReferenceFormatter.cpp
index 7730a99a..62d22999 100644
--- a/libsolidity/interface/SourceReferenceFormatter.cpp
+++ b/libsolidity/interface/SourceReferenceFormatter.cpp
@@ -101,6 +101,8 @@ void SourceReferenceFormatter::printExceptionInformation(
_stream << _name;
if (string const* description = boost::get_error_info<errinfo_comment>(_exception))
_stream << ": " << *description << endl;
+ else
+ _stream << endl;
printSourceLocation(_stream, location, _scannerFromSourceName);
@@ -108,9 +110,8 @@ void SourceReferenceFormatter::printExceptionInformation(
{
for (auto info: secondarylocation->infos)
{
- _stream << info.first << " ";
printSourceName(_stream, &info.second, _scannerFromSourceName);
- _stream << endl;
+ _stream << info.first << endl;
printSourceLocation(_stream, &info.second, _scannerFromSourceName);
}
_stream << endl;
diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp
index b98991f3..d3a6aa45 100644
--- a/libsolidity/parsing/Parser.cpp
+++ b/libsolidity/parsing/Parser.cpp
@@ -64,10 +64,30 @@ private:
SourceLocation m_location;
};
+/// Utility class that creates an error and throws an exception if the
+/// recursion depth is too deep.
+class Parser::RecursionGuard
+{
+public:
+ RecursionGuard(Parser& _parser):
+ m_parser(_parser)
+ {
+ m_parser.increaseRecursionDepth();
+ }
+ ~RecursionGuard()
+ {
+ m_parser.decreaseRecursionDepth();
+ }
+
+private:
+ Parser& m_parser;
+};
+
ASTPointer<SourceUnit> Parser::parse(shared_ptr<Scanner> const& _scanner)
{
try
{
+ m_recursionDepth = 0;
m_scanner = _scanner;
ASTNodeFactory nodeFactory(*this);
vector<ASTPointer<ASTNode>> nodes;
@@ -90,6 +110,7 @@ ASTPointer<SourceUnit> Parser::parse(shared_ptr<Scanner> const& _scanner)
fatalParserError(string("Expected pragma, import directive or contract/interface/library definition."));
}
}
+ solAssert(m_recursionDepth == 0, "");
return nodeFactory.createNode<SourceUnit>(nodes);
}
catch (FatalError const&)
@@ -102,6 +123,7 @@ ASTPointer<SourceUnit> Parser::parse(shared_ptr<Scanner> const& _scanner)
ASTPointer<PragmaDirective> Parser::parsePragmaDirective()
{
+ RecursionGuard recursionGuard(*this);
// pragma anything* ;
// Currently supported:
// pragma solidity ^0.4.0 || ^0.3.0;
@@ -132,6 +154,7 @@ ASTPointer<PragmaDirective> Parser::parsePragmaDirective()
ASTPointer<ImportDirective> Parser::parseImportDirective()
{
+ RecursionGuard recursionGuard(*this);
// import "abc" [as x];
// import * as x from "abc";
// import {a as b, c} from "abc";
@@ -212,6 +235,7 @@ ContractDefinition::ContractKind Parser::tokenToContractKind(Token::Value _token
ASTPointer<ContractDefinition> Parser::parseContractDefinition(Token::Value _expectedKind)
{
+ RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this);
ASTPointer<ASTString> docString;
if (m_scanner->currentCommentLiteral() != "")
@@ -275,6 +299,7 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition(Token::Value _exp
ASTPointer<InheritanceSpecifier> Parser::parseInheritanceSpecifier()
{
+ RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this);
ASTPointer<UserDefinedTypeName> name(parseUserDefinedTypeName());
vector<ASTPointer<Expression>> arguments;
@@ -307,8 +332,22 @@ Declaration::Visibility Parser::parseVisibilitySpecifier(Token::Value _token)
return visibility;
}
+StateMutability Parser::parseStateMutability(Token::Value _token)
+{
+ StateMutability stateMutability(StateMutability::NonPayable);
+ if (_token == Token::Payable)
+ stateMutability = StateMutability::Payable;
+ else if (_token == Token::Constant)
+ stateMutability = StateMutability::View;
+ else
+ solAssert(false, "Invalid state mutability specifier.");
+ m_scanner->next();
+ return stateMutability;
+}
+
Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _forceEmptyName, bool _allowModifiers)
{
+ RecursionGuard recursionGuard(*this);
FunctionHeaderParserResult result;
expectToken(Token::Function);
if (_forceEmptyName || m_scanner->currentToken() == Token::LParen)
@@ -321,23 +360,7 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _forceEmptyN
while (true)
{
Token::Value token = m_scanner->currentToken();
- if (token == Token::Const)
- {
- if (result.isDeclaredConst)
- parserError(string("Multiple \"constant\" specifiers."));
-
- result.isDeclaredConst = true;
- m_scanner->next();
- }
- else if (m_scanner->currentToken() == Token::Payable)
- {
- if (result.isPayable)
- parserError(string("Multiple \"payable\" specifiers."));
-
- result.isPayable = true;
- m_scanner->next();
- }
- else if (_allowModifiers && token == Token::Identifier)
+ if (_allowModifiers && token == Token::Identifier)
{
// This can either be a modifier (function declaration) or the name of the
// variable (function type name plus variable).
@@ -354,12 +377,30 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _forceEmptyN
{
if (result.visibility != Declaration::Visibility::Default)
{
- parserError(string("Multiple visibility specifiers."));
+ parserError(string(
+ "Visibility already specified as \"" +
+ Declaration::visibilityToString(result.visibility) +
+ "\"."
+ ));
m_scanner->next();
}
else
result.visibility = parseVisibilitySpecifier(token);
}
+ else if (Token::isStateMutabilitySpecifier(token))
+ {
+ if (result.stateMutability != StateMutability::NonPayable)
+ {
+ parserError(string(
+ "State mutability already specified as \"" +
+ stateMutabilityToString(result.stateMutability) +
+ "\"."
+ ));
+ m_scanner->next();
+ }
+ else
+ result.stateMutability = parseStateMutability(token);
+ }
else
break;
}
@@ -376,6 +417,7 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _forceEmptyN
ASTPointer<ASTNode> Parser::parseFunctionDefinitionOrFunctionTypeStateVariable(ASTString const* _contractName)
{
+ RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this);
ASTPointer<ASTString> docstring;
if (m_scanner->currentCommentLiteral() != "")
@@ -404,13 +446,12 @@ ASTPointer<ASTNode> Parser::parseFunctionDefinitionOrFunctionTypeStateVariable(A
return nodeFactory.createNode<FunctionDefinition>(
header.name,
header.visibility,
+ header.stateMutability,
c_isConstructor,
docstring,
header.parameters,
- header.isDeclaredConst,
header.modifiers,
header.returnParameters,
- header.isPayable,
block
);
}
@@ -421,8 +462,7 @@ ASTPointer<ASTNode> Parser::parseFunctionDefinitionOrFunctionTypeStateVariable(A
header.parameters,
header.returnParameters,
header.visibility,
- header.isDeclaredConst,
- header.isPayable
+ header.stateMutability
);
type = parseTypeNameSuffix(type, nodeFactory);
VarDeclParserOptions options;
@@ -436,6 +476,7 @@ ASTPointer<ASTNode> Parser::parseFunctionDefinitionOrFunctionTypeStateVariable(A
ASTPointer<StructDefinition> Parser::parseStructDefinition()
{
+ RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this);
expectToken(Token::Struct);
ASTPointer<ASTString> name = expectIdentifierToken();
@@ -453,6 +494,7 @@ ASTPointer<StructDefinition> Parser::parseStructDefinition()
ASTPointer<EnumValue> Parser::parseEnumValue()
{
+ RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this);
nodeFactory.markEndPosition();
return nodeFactory.createNode<EnumValue>(expectIdentifierToken());
@@ -460,6 +502,7 @@ ASTPointer<EnumValue> Parser::parseEnumValue()
ASTPointer<EnumDefinition> Parser::parseEnumDefinition()
{
+ RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this);
expectToken(Token::Enum);
ASTPointer<ASTString> name = expectIdentifierToken();
@@ -488,6 +531,7 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
ASTPointer<TypeName> const& _lookAheadArrayType
)
{
+ RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory = _lookAheadArrayType ?
ASTNodeFactory(*this, _lookAheadArrayType) : ASTNodeFactory(*this);
ASTPointer<TypeName> type;
@@ -512,7 +556,11 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
{
if (visibility != Declaration::Visibility::Default)
{
- parserError(string("Visibility already specified."));
+ parserError(string(
+ "Visibility already specified as \"" +
+ Declaration::visibilityToString(visibility) +
+ "\"."
+ ));
m_scanner->next();
}
else
@@ -522,7 +570,7 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
{
if (_options.allowIndexed && token == Token::Indexed)
isIndexed = true;
- else if (token == Token::Const)
+ else if (token == Token::Constant)
isDeclaredConst = true;
else if (_options.allowLocationSpecifier && Token::isLocationSpecifier(token))
{
@@ -576,6 +624,7 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
ASTPointer<ModifierDefinition> Parser::parseModifierDefinition()
{
+ RecursionGuard recursionGuard(*this);
ScopeGuard resetModifierFlag([this]() { m_insideModifier = false; });
m_insideModifier = true;
@@ -603,6 +652,7 @@ ASTPointer<ModifierDefinition> Parser::parseModifierDefinition()
ASTPointer<EventDefinition> Parser::parseEventDefinition()
{
+ RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this);
ASTPointer<ASTString> docstring;
if (m_scanner->currentCommentLiteral() != "")
@@ -632,6 +682,7 @@ ASTPointer<EventDefinition> Parser::parseEventDefinition()
ASTPointer<UsingForDirective> Parser::parseUsingDirective()
{
+ RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this);
expectToken(Token::Using);
@@ -649,6 +700,7 @@ ASTPointer<UsingForDirective> Parser::parseUsingDirective()
ASTPointer<ModifierInvocation> Parser::parseModifierInvocation()
{
+ RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this);
ASTPointer<Identifier> name(parseIdentifier());
vector<ASTPointer<Expression>> arguments;
@@ -666,6 +718,7 @@ ASTPointer<ModifierInvocation> Parser::parseModifierInvocation()
ASTPointer<Identifier> Parser::parseIdentifier()
{
+ RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this);
nodeFactory.markEndPosition();
return nodeFactory.createNode<Identifier>(expectIdentifierToken());
@@ -673,6 +726,7 @@ ASTPointer<Identifier> Parser::parseIdentifier()
ASTPointer<UserDefinedTypeName> Parser::parseUserDefinedTypeName()
{
+ RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this);
nodeFactory.markEndPosition();
vector<ASTString> identifierPath{*expectIdentifierToken()};
@@ -687,6 +741,7 @@ ASTPointer<UserDefinedTypeName> Parser::parseUserDefinedTypeName()
ASTPointer<TypeName> Parser::parseTypeNameSuffix(ASTPointer<TypeName> type, ASTNodeFactory& nodeFactory)
{
+ RecursionGuard recursionGuard(*this);
while (m_scanner->currentToken() == Token::LBrack)
{
m_scanner->next();
@@ -702,6 +757,7 @@ ASTPointer<TypeName> Parser::parseTypeNameSuffix(ASTPointer<TypeName> type, ASTN
ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar)
{
+ RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this);
ASTPointer<TypeName> type;
Token::Value token = m_scanner->currentToken();
@@ -737,19 +793,20 @@ ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar)
ASTPointer<FunctionTypeName> Parser::parseFunctionType()
{
+ RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this);
FunctionHeaderParserResult header = parseFunctionHeader(true, false);
return nodeFactory.createNode<FunctionTypeName>(
header.parameters,
header.returnParameters,
header.visibility,
- header.isDeclaredConst,
- header.isPayable
+ header.stateMutability
);
}
ASTPointer<Mapping> Parser::parseMapping()
{
+ RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this);
expectToken(Token::Mapping);
expectToken(Token::LParen);
@@ -776,6 +833,7 @@ ASTPointer<ParameterList> Parser::parseParameterList(
bool _allowEmpty
)
{
+ RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this);
vector<ASTPointer<VariableDeclaration>> parameters;
VarDeclParserOptions options(_options);
@@ -797,6 +855,7 @@ ASTPointer<ParameterList> Parser::parseParameterList(
ASTPointer<Block> Parser::parseBlock(ASTPointer<ASTString> const& _docString)
{
+ RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this);
expectToken(Token::LBrace);
vector<ASTPointer<Statement>> statements;
@@ -809,6 +868,7 @@ ASTPointer<Block> Parser::parseBlock(ASTPointer<ASTString> const& _docString)
ASTPointer<Statement> Parser::parseStatement()
{
+ RecursionGuard recursionGuard(*this);
ASTPointer<ASTString> docString;
if (m_scanner->currentCommentLiteral() != "")
docString = make_shared<ASTString>(m_scanner->currentCommentLiteral());
@@ -871,6 +931,7 @@ ASTPointer<Statement> Parser::parseStatement()
ASTPointer<InlineAssembly> Parser::parseInlineAssembly(ASTPointer<ASTString> const& _docString)
{
+ RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this);
expectToken(Token::Assembly);
if (m_scanner->currentToken() == Token::StringLiteral)
@@ -888,6 +949,7 @@ ASTPointer<InlineAssembly> Parser::parseInlineAssembly(ASTPointer<ASTString> con
ASTPointer<IfStatement> Parser::parseIfStatement(ASTPointer<ASTString> const& _docString)
{
+ RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this);
expectToken(Token::If);
expectToken(Token::LParen);
@@ -908,6 +970,7 @@ ASTPointer<IfStatement> Parser::parseIfStatement(ASTPointer<ASTString> const& _d
ASTPointer<WhileStatement> Parser::parseWhileStatement(ASTPointer<ASTString> const& _docString)
{
+ RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this);
expectToken(Token::While);
expectToken(Token::LParen);
@@ -920,6 +983,7 @@ ASTPointer<WhileStatement> Parser::parseWhileStatement(ASTPointer<ASTString> con
ASTPointer<WhileStatement> Parser::parseDoWhileStatement(ASTPointer<ASTString> const& _docString)
{
+ RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this);
expectToken(Token::Do);
ASTPointer<Statement> body = parseStatement();
@@ -935,6 +999,7 @@ ASTPointer<WhileStatement> Parser::parseDoWhileStatement(ASTPointer<ASTString> c
ASTPointer<ForStatement> Parser::parseForStatement(ASTPointer<ASTString> const& _docString)
{
+ RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this);
ASTPointer<Statement> initExpression;
ASTPointer<Expression> conditionExpression;
@@ -968,6 +1033,7 @@ ASTPointer<ForStatement> Parser::parseForStatement(ASTPointer<ASTString> const&
ASTPointer<Statement> Parser::parseSimpleStatement(ASTPointer<ASTString> const& _docString)
{
+ RecursionGuard recursionGuard(*this);
// These two cases are very hard to distinguish:
// x[7 * 20 + 3] a; - x[7 * 20 + 3] = 9;
// In the first case, x is a type name, in the second it is the name of a variable.
@@ -1030,6 +1096,7 @@ ASTPointer<VariableDeclarationStatement> Parser::parseVariableDeclarationStateme
ASTPointer<TypeName> const& _lookAheadArrayType
)
{
+ RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this);
if (_lookAheadArrayType)
nodeFactory.setLocation(_lookAheadArrayType->location());
@@ -1093,6 +1160,7 @@ ASTPointer<ExpressionStatement> Parser::parseExpressionStatement(
ASTPointer<Expression> const& _lookAheadIndexAccessStructure
)
{
+ RecursionGuard recursionGuard(*this);
ASTPointer<Expression> expression = parseExpression(_lookAheadIndexAccessStructure);
return ASTNodeFactory(*this, expression).createNode<ExpressionStatement>(_docString, expression);
}
@@ -1101,6 +1169,7 @@ ASTPointer<Expression> Parser::parseExpression(
ASTPointer<Expression> const& _lookAheadIndexAccessStructure
)
{
+ RecursionGuard recursionGuard(*this);
ASTPointer<Expression> expression = parseBinaryExpression(4, _lookAheadIndexAccessStructure);
if (Token::isAssignmentOp(m_scanner->currentToken()))
{
@@ -1129,6 +1198,7 @@ ASTPointer<Expression> Parser::parseBinaryExpression(
ASTPointer<Expression> const& _lookAheadIndexAccessStructure
)
{
+ RecursionGuard recursionGuard(*this);
ASTPointer<Expression> expression = parseUnaryExpression(_lookAheadIndexAccessStructure);
ASTNodeFactory nodeFactory(*this, expression);
int precedence = Token::precedence(m_scanner->currentToken());
@@ -1148,6 +1218,7 @@ ASTPointer<Expression> Parser::parseUnaryExpression(
ASTPointer<Expression> const& _lookAheadIndexAccessStructure
)
{
+ RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory = _lookAheadIndexAccessStructure ?
ASTNodeFactory(*this, _lookAheadIndexAccessStructure) : ASTNodeFactory(*this);
Token::Value token = m_scanner->currentToken();
@@ -1176,6 +1247,7 @@ ASTPointer<Expression> Parser::parseLeftHandSideExpression(
ASTPointer<Expression> const& _lookAheadIndexAccessStructure
)
{
+ RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory = _lookAheadIndexAccessStructure ?
ASTNodeFactory(*this, _lookAheadIndexAccessStructure) : ASTNodeFactory(*this);
@@ -1233,6 +1305,7 @@ ASTPointer<Expression> Parser::parseLeftHandSideExpression(
ASTPointer<Expression> Parser::parsePrimaryExpression()
{
+ RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this);
Token::Value token = m_scanner->currentToken();
ASTPointer<Expression> expression;
@@ -1292,10 +1365,11 @@ ASTPointer<Expression> Parser::parsePrimaryExpression()
parserError("Expected expression (inline array elements cannot be omitted).");
else
components.push_back(ASTPointer<Expression>());
+
if (m_scanner->currentToken() == oppositeToken)
break;
- else if (m_scanner->currentToken() == Token::Comma)
- m_scanner->next();
+
+ expectToken(Token::Comma);
}
nodeFactory.markEndPosition();
expectToken(oppositeToken);
@@ -1322,6 +1396,7 @@ ASTPointer<Expression> Parser::parsePrimaryExpression()
vector<ASTPointer<Expression>> Parser::parseFunctionCallListArguments()
{
+ RecursionGuard recursionGuard(*this);
vector<ASTPointer<Expression>> arguments;
if (m_scanner->currentToken() != Token::RParen)
{
@@ -1337,6 +1412,7 @@ vector<ASTPointer<Expression>> Parser::parseFunctionCallListArguments()
pair<vector<ASTPointer<Expression>>, vector<ASTPointer<ASTString>>> Parser::parseFunctionCallArguments()
{
+ RecursionGuard recursionGuard(*this);
pair<vector<ASTPointer<Expression>>, vector<ASTPointer<ASTString>>> ret;
Token::Value token = m_scanner->currentToken();
if (token == Token::LBrace)
@@ -1403,6 +1479,7 @@ ASTPointer<TypeName> Parser::typeNameIndexAccessStructure(
)
{
solAssert(!_path.empty(), "");
+ RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this);
SourceLocation location = _path.front()->location();
location.end = _path.back()->location().end;
@@ -1435,6 +1512,7 @@ ASTPointer<Expression> Parser::expressionFromIndexAccessStructure(
)
{
solAssert(!_path.empty(), "");
+ RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this, _path.front());
ASTPointer<Expression> expression(_path.front());
for (size_t i = 1; i < _path.size(); ++i)
@@ -1458,11 +1536,25 @@ ASTPointer<Expression> Parser::expressionFromIndexAccessStructure(
ASTPointer<ParameterList> Parser::createEmptyParameterList()
{
+ RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this);
nodeFactory.setLocationEmpty();
return nodeFactory.createNode<ParameterList>(vector<ASTPointer<VariableDeclaration>>());
}
+void Parser::increaseRecursionDepth()
+{
+ m_recursionDepth++;
+ if (m_recursionDepth >= 4096)
+ fatalParserError("Maximum recursion depth reached during parsing.");
+}
+
+void Parser::decreaseRecursionDepth()
+{
+ solAssert(m_recursionDepth > 0, "");
+ m_recursionDepth--;
+}
+
string Parser::currentTokenName()
{
Token::Value token = m_scanner->currentToken();
diff --git a/libsolidity/parsing/Parser.h b/libsolidity/parsing/Parser.h
index 19631c58..5e6f3ef6 100644
--- a/libsolidity/parsing/Parser.h
+++ b/libsolidity/parsing/Parser.h
@@ -41,6 +41,7 @@ public:
private:
class ASTNodeFactory;
+ class RecursionGuard;
struct VarDeclParserOptions
{
@@ -60,8 +61,7 @@ private:
ASTPointer<ParameterList> parameters;
ASTPointer<ParameterList> returnParameters;
Declaration::Visibility visibility = Declaration::Visibility::Default;
- bool isDeclaredConst = false;
- bool isPayable = false;
+ StateMutability stateMutability = StateMutability::NonPayable;
std::vector<ASTPointer<ModifierInvocation>> modifiers;
};
@@ -73,6 +73,7 @@ private:
ASTPointer<ContractDefinition> parseContractDefinition(Token::Value _expectedKind);
ASTPointer<InheritanceSpecifier> parseInheritanceSpecifier();
Declaration::Visibility parseVisibilitySpecifier(Token::Value _token);
+ StateMutability parseStateMutability(Token::Value _token);
FunctionHeaderParserResult parseFunctionHeader(bool _forceEmptyName, bool _allowModifiers);
ASTPointer<ASTNode> parseFunctionDefinitionOrFunctionTypeStateVariable(ASTString const* _contractName);
ASTPointer<FunctionDefinition> parseFunctionDefinition(ASTString const* _contractName);
@@ -164,8 +165,14 @@ private:
/// Creates an empty ParameterList at the current location (used if parameters can be omitted).
ASTPointer<ParameterList> createEmptyParameterList();
+ /// Increases the recursion depth and throws an exception if it is too deep.
+ void increaseRecursionDepth();
+ void decreaseRecursionDepth();
+
/// Flag that signifies whether '_' is parsed as a PlaceholderStatement or a regular identifier.
bool m_insideModifier = false;
+ /// Current recursion depth during parsing.
+ size_t m_recursionDepth = 0;
};
}
diff --git a/libsolidity/parsing/Token.h b/libsolidity/parsing/Token.h
index d412b3f0..efbe5e9e 100644
--- a/libsolidity/parsing/Token.h
+++ b/libsolidity/parsing/Token.h
@@ -143,7 +143,7 @@ namespace solidity
K(As, "as", 0) \
K(Assembly, "assembly", 0) \
K(Break, "break", 0) \
- K(Const, "constant", 0) \
+ K(Constant, "constant", 0) \
K(Continue, "continue", 0) \
K(Contract, "contract", 0) \
K(Do, "do", 0) \
@@ -290,6 +290,7 @@ public:
static bool isVisibilitySpecifier(Value op) { return isVariableVisibilitySpecifier(op) || op == External; }
static bool isVariableVisibilitySpecifier(Value op) { return op == Public || op == Private || op == Internal; }
static bool isLocationSpecifier(Value op) { return op == Memory || op == Storage; }
+ static bool isStateMutabilitySpecifier(Value op) { return op == Constant || op == Payable; }
static bool isEtherSubdenomination(Value op) { return op == SubWei || op == SubSzabo || op == SubFinney || op == SubEther; }
static bool isTimeSubdenomination(Value op) { return op == SubSecond || op == SubMinute || op == SubHour || op == SubDay || op == SubWeek || op == SubYear; }
static bool isReservedKeyword(Value op) { return (Abstract <= op && op <= TypeOf); }
diff --git a/solc/jsonCompiler.cpp b/solc/jsonCompiler.cpp
index de797b3c..ab928ac0 100644
--- a/solc/jsonCompiler.cpp
+++ b/solc/jsonCompiler.cpp
@@ -38,6 +38,9 @@ extern "C" {
typedef void (*CStyleReadFileCallback)(char const* _path, char** o_contents, char** o_error);
}
+namespace
+{
+
ReadFile::Callback wrapReadCallback(CStyleReadFileCallback _readCallback = nullptr)
{
ReadFile::Callback readCallback;
@@ -260,6 +263,8 @@ string compileStandardInternal(string const& _input, CStyleReadFileCallback _rea
return compiler.compile(_input);
}
+}
+
static string s_outputBuffer;
extern "C"
diff --git a/solc/main.cpp b/solc/main.cpp
index c61da6e9..6d559542 100644
--- a/solc/main.cpp
+++ b/solc/main.cpp
@@ -40,7 +40,7 @@ specified default locale if it is valid, and if not then it will modify the
environment the process is running in to use a sensible default. This also means
that users do not need to install language packs for their OS.
*/
-void setDefaultOrCLocale()
+static void setDefaultOrCLocale()
{
#if __unix__
if (!std::setlocale(LC_ALL, ""))
diff --git a/test/boostTest.cpp b/test/boostTest.cpp
index c2121940..d8c5b678 100644
--- a/test/boostTest.cpp
+++ b/test/boostTest.cpp
@@ -46,6 +46,7 @@ test_suite* init_unit_test_suite( int /*argc*/, char* /*argv*/[] )
if (dev::test::Options::get().disableIPC)
{
for (auto suite: {
+ "ABIEncoderTest",
"SolidityAuctionRegistrar",
"SolidityFixedFeeRegistrar",
"SolidityWallet",
diff --git a/test/libdevcore/IndentedWriter.cpp b/test/libdevcore/IndentedWriter.cpp
new file mode 100644
index 00000000..a694aa1b
--- /dev/null
+++ b/test/libdevcore/IndentedWriter.cpp
@@ -0,0 +1,75 @@
+/*
+ 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/>.
+*/
+/**
+ * Unit tests for IndentedWriter.
+ */
+
+#include <libdevcore/IndentedWriter.h>
+
+#include "../TestHelper.h"
+
+using namespace std;
+
+namespace dev
+{
+namespace test
+{
+
+BOOST_AUTO_TEST_SUITE(IndentedWriterTest)
+
+BOOST_AUTO_TEST_CASE(empty)
+{
+ IndentedWriter iw;
+ BOOST_CHECK_EQUAL(iw.format(), "\n");
+}
+
+BOOST_AUTO_TEST_CASE(new_lines)
+{
+ IndentedWriter iw;
+ iw.newLine();
+ BOOST_CHECK_EQUAL(iw.format(), "\n");
+}
+
+BOOST_AUTO_TEST_CASE(text_without_newline)
+{
+ IndentedWriter iw;
+ iw.add("Hello World");
+ BOOST_CHECK_EQUAL(iw.format(), "Hello World\n");
+}
+
+BOOST_AUTO_TEST_CASE(text_with_newline)
+{
+ IndentedWriter iw;
+ iw.addLine("Hello World");
+ BOOST_CHECK_EQUAL(iw.format(), "Hello World\n\n");
+}
+
+BOOST_AUTO_TEST_CASE(indent)
+{
+ IndentedWriter iw;
+ iw.addLine("Hello");
+ iw.indent();
+ iw.addLine("World");
+ iw.unindent();
+ iw.addLine("and everyone else");
+ BOOST_CHECK_EQUAL(iw.format(), "Hello\n World\nand everyone else\n\n");
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+}
+}
diff --git a/test/libsolidity/ABIEncoderTests.cpp b/test/libsolidity/ABIEncoderTests.cpp
new file mode 100644
index 00000000..297c4ef0
--- /dev/null
+++ b/test/libsolidity/ABIEncoderTests.cpp
@@ -0,0 +1,405 @@
+/*
+ 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/>.
+*/
+/**
+ * Unit tests for Solidity's ABI encoder.
+ */
+
+#include <functional>
+#include <string>
+#include <tuple>
+#include <boost/test/unit_test.hpp>
+#include <libsolidity/interface/Exceptions.h>
+#include <test/libsolidity/SolidityExecutionFramework.h>
+
+using namespace std;
+using namespace std::placeholders;
+using namespace dev::test;
+
+namespace dev
+{
+namespace solidity
+{
+namespace test
+{
+
+#define REQUIRE_LOG_DATA(DATA) do { \
+ BOOST_REQUIRE_EQUAL(m_logs.size(), 1); \
+ BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); \
+ BOOST_CHECK_EQUAL(toHex(m_logs[0].data), toHex(DATA)); \
+} while (false)
+
+static string const NewEncoderPragma = "pragma experimental ABIEncoderV2;\n";
+
+#define NEW_ENCODER(CODE) \
+{ \
+ sourceCode = NewEncoderPragma + sourceCode; \
+ { CODE } \
+}
+
+#define BOTH_ENCODERS(CODE) \
+{ \
+ { CODE } \
+ NEW_ENCODER(CODE) \
+}
+
+BOOST_FIXTURE_TEST_SUITE(ABIEncoderTest, SolidityExecutionFramework)
+
+BOOST_AUTO_TEST_CASE(both_encoders_macro)
+{
+ // This tests that the "both encoders macro" at least runs twice and
+ // modifies the source.
+ string sourceCode;
+ int runs = 0;
+ BOTH_ENCODERS(runs++;)
+ BOOST_CHECK(sourceCode == NewEncoderPragma);
+ BOOST_CHECK_EQUAL(runs, 2);
+}
+
+BOOST_AUTO_TEST_CASE(value_types)
+{
+ string sourceCode = R"(
+ contract C {
+ event E(uint a, uint16 b, uint24 c, int24 d, bytes3 x, bool, C);
+ function f() {
+ bytes6 x = hex"1bababababa2";
+ bool b;
+ assembly { b := 7 }
+ C c;
+ assembly { c := sub(0, 5) }
+ E(10, uint16(uint256(-2)), uint24(0x12121212), int24(int256(-1)), bytes3(x), b, c);
+ }
+ }
+ )";
+ BOTH_ENCODERS(
+ compileAndRun(sourceCode);
+ callContractFunction("f()");
+ REQUIRE_LOG_DATA(encodeArgs(
+ 10, u256(65534), u256(0x121212), u256(-1), string("\x1b\xab\xab"), true, u160(u256(-5))
+ ));
+ )
+}
+
+BOOST_AUTO_TEST_CASE(string_literal)
+{
+ string sourceCode = R"(
+ contract C {
+ event E(string, bytes20, string);
+ function f() {
+ E("abcdef", "abcde", "abcdefabcdefgehabcabcasdfjklabcdefabcedefghabcabcasdfjklabcdefabcdefghabcabcasdfjklabcdeefabcdefghabcabcasdefjklabcdefabcdefghabcabcasdfjkl");
+ }
+ }
+ )";
+ BOTH_ENCODERS(
+ compileAndRun(sourceCode);
+ callContractFunction("f()");
+ REQUIRE_LOG_DATA(encodeArgs(
+ 0x60, string("abcde"), 0xa0,
+ 6, string("abcdef"),
+ 0x8b, string("abcdefabcdefgehabcabcasdfjklabcdefabcedefghabcabcasdfjklabcdefabcdefghabcabcasdfjklabcdeefabcdefghabcabcasdefjklabcdefabcdefghabcabcasdfjkl")
+ ));
+ )
+}
+
+
+BOOST_AUTO_TEST_CASE(enum_type_cleanup)
+{
+ string sourceCode = R"(
+ contract C {
+ enum E { A, B }
+ function f(uint x) returns (E en) {
+ assembly { en := x }
+ }
+ }
+ )";
+ BOTH_ENCODERS(
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("f(uint256)", 0) == encodeArgs(0));
+ BOOST_CHECK(callContractFunction("f(uint256)", 1) == encodeArgs(1));
+ BOOST_CHECK(callContractFunction("f(uint256)", 2) == encodeArgs());
+ )
+}
+
+BOOST_AUTO_TEST_CASE(conversion)
+{
+ string sourceCode = R"(
+ contract C {
+ event E(bytes4, bytes4, uint16, uint8, int16, int8);
+ function f() {
+ bytes2 x; assembly { x := 0xf1f2f3f400000000000000000000000000000000000000000000000000000000 }
+ uint8 a;
+ uint16 b = 0x1ff;
+ int8 c;
+ int16 d;
+ assembly { a := sub(0, 1) c := 0x0101ff d := 0xff01 }
+ E(10, x, a, uint8(b), c, int8(d));
+ }
+ }
+ )";
+ BOTH_ENCODERS(
+ compileAndRun(sourceCode);
+ callContractFunction("f()");
+ REQUIRE_LOG_DATA(encodeArgs(
+ string(3, 0) + string("\x0a"), string("\xf1\xf2"),
+ 0xff, 0xff, u256(-1), u256(1)
+ ));
+ )
+}
+
+BOOST_AUTO_TEST_CASE(memory_array_one_dim)
+{
+ string sourceCode = R"(
+ contract C {
+ event E(uint a, int16[] b, uint c);
+ function f() {
+ int16[] memory x = new int16[](3);
+ assembly {
+ for { let i := 0 } lt(i, 3) { i := add(i, 1) } {
+ mstore(add(x, mul(add(i, 1), 0x20)), add(0xfffffffe, i))
+ }
+ }
+ E(10, x, 11);
+ }
+ }
+ )";
+
+ compileAndRun(sourceCode);
+ callContractFunction("f()");
+ // The old encoder does not clean array elements.
+ REQUIRE_LOG_DATA(encodeArgs(10, 0x60, 11, 3, u256("0xfffffffe"), u256("0xffffffff"), u256("0x100000000")));
+
+ compileAndRun(NewEncoderPragma + sourceCode);
+ callContractFunction("f()");
+ REQUIRE_LOG_DATA(encodeArgs(10, 0x60, 11, 3, u256(-2), u256(-1), u256(0)));
+}
+
+BOOST_AUTO_TEST_CASE(memory_array_two_dim)
+{
+ string sourceCode = R"(
+ contract C {
+ event E(uint a, int16[][2] b, uint c);
+ function f() {
+ int16[][2] memory x;
+ x[0] = new int16[](3);
+ x[1] = new int16[](2);
+ x[0][0] = 7;
+ x[0][1] = int16(0x010203040506);
+ x[0][2] = -1;
+ x[1][0] = 4;
+ x[1][1] = 5;
+ E(10, x, 11);
+ }
+ }
+ )";
+ NEW_ENCODER(
+ compileAndRun(sourceCode);
+ callContractFunction("f()");
+ REQUIRE_LOG_DATA(encodeArgs(10, 0x60, 11, 0x40, 0xc0, 3, 7, 0x0506, u256(-1), 2, 4, 5));
+ )
+}
+
+BOOST_AUTO_TEST_CASE(memory_byte_array)
+{
+ string sourceCode = R"(
+ contract C {
+ event E(uint a, bytes[] b, uint c);
+ function f() {
+ bytes[] memory x = new bytes[](2);
+ x[0] = "abcabcdefghjklmnopqrsuvwabcdefgijklmnopqrstuwabcdefgijklmnoprstuvw";
+ x[1] = "abcdefghijklmnopqrtuvwabcfghijklmnopqstuvwabcdeghijklmopqrstuvw";
+ E(10, x, 11);
+ }
+ }
+ )";
+ NEW_ENCODER(
+ compileAndRun(sourceCode);
+ callContractFunction("f()");
+ REQUIRE_LOG_DATA(encodeArgs(
+ 10, 0x60, 11,
+ 2, 0x40, 0xc0,
+ 66, string("abcabcdefghjklmnopqrsuvwabcdefgijklmnopqrstuwabcdefgijklmnoprstuvw"),
+ 63, string("abcdefghijklmnopqrtuvwabcfghijklmnopqstuvwabcdeghijklmopqrstuvw")
+ ));
+ )
+}
+
+BOOST_AUTO_TEST_CASE(storage_byte_array)
+{
+ string sourceCode = R"(
+ contract C {
+ bytes short;
+ bytes long;
+ event E(bytes s, bytes l);
+ function f() {
+ short = "123456789012345678901234567890a";
+ long = "ffff123456789012345678901234567890afffffffff123456789012345678901234567890a";
+ E(short, long);
+ }
+ }
+ )";
+ BOTH_ENCODERS(
+ compileAndRun(sourceCode);
+ callContractFunction("f()");
+ REQUIRE_LOG_DATA(encodeArgs(
+ 0x40, 0x80,
+ 31, string("123456789012345678901234567890a"),
+ 75, string("ffff123456789012345678901234567890afffffffff123456789012345678901234567890a")
+ ));
+ )
+}
+
+BOOST_AUTO_TEST_CASE(storage_array)
+{
+ string sourceCode = R"(
+ contract C {
+ address[3] addr;
+ event E(address[3] a);
+ function f() {
+ assembly {
+ sstore(0, sub(0, 1))
+ sstore(1, sub(0, 2))
+ sstore(2, sub(0, 3))
+ }
+ E(addr);
+ }
+ }
+ )";
+ BOTH_ENCODERS(
+ compileAndRun(sourceCode);
+ callContractFunction("f()");
+ REQUIRE_LOG_DATA(encodeArgs(u160(-1), u160(-2), u160(-3)));
+ )
+}
+
+BOOST_AUTO_TEST_CASE(storage_array_dyn)
+{
+ string sourceCode = R"(
+ contract C {
+ address[] addr;
+ event E(address[] a);
+ function f() {
+ addr.push(1);
+ addr.push(2);
+ addr.push(3);
+ E(addr);
+ }
+ }
+ )";
+ BOTH_ENCODERS(
+ compileAndRun(sourceCode);
+ callContractFunction("f()");
+ REQUIRE_LOG_DATA(encodeArgs(0x20, 3, u160(1), u160(2), u160(3)));
+ )
+}
+
+BOOST_AUTO_TEST_CASE(storage_array_compact)
+{
+ string sourceCode = R"(
+ contract C {
+ int72[] x;
+ event E(int72[]);
+ function f() {
+ x.push(-1);
+ x.push(2);
+ x.push(-3);
+ x.push(4);
+ x.push(-5);
+ x.push(6);
+ x.push(-7);
+ x.push(8);
+ E(x);
+ }
+ }
+ )";
+ BOTH_ENCODERS(
+ compileAndRun(sourceCode);
+ callContractFunction("f()");
+ REQUIRE_LOG_DATA(encodeArgs(
+ 0x20, 8, u256(-1), 2, u256(-3), 4, u256(-5), 6, u256(-7), 8
+ ));
+ )
+}
+
+BOOST_AUTO_TEST_CASE(external_function)
+{
+ string sourceCode = R"(
+ contract C {
+ event E(function(uint) external returns (uint), function(uint) external returns (uint));
+ function(uint) external returns (uint) g;
+ function f(uint) returns (uint) {
+ g = this.f;
+ E(this.f, g);
+ }
+ }
+ )";
+ BOTH_ENCODERS(
+ compileAndRun(sourceCode);
+ callContractFunction("f(uint256)");
+ string functionIdF = asString(m_contractAddress.ref()) + asString(FixedHash<4>(dev::keccak256("f(uint256)")).ref());
+ REQUIRE_LOG_DATA(encodeArgs(functionIdF, functionIdF));
+ )
+}
+
+BOOST_AUTO_TEST_CASE(external_function_cleanup)
+{
+ string sourceCode = R"(
+ contract C {
+ event E(function(uint) external returns (uint), function(uint) external returns (uint));
+ // This test relies on the fact that g is stored in slot zero.
+ function(uint) external returns (uint) g;
+ function f(uint) returns (uint) {
+ function(uint) external returns (uint)[1] memory h;
+ assembly { sstore(0, sub(0, 1)) mstore(h, sub(0, 1)) }
+ E(h[0], g);
+ }
+ }
+ )";
+ BOTH_ENCODERS(
+ compileAndRun(sourceCode);
+ callContractFunction("f(uint256)");
+ REQUIRE_LOG_DATA(encodeArgs(string(24, char(-1)), string(24, char(-1))));
+ )
+}
+
+BOOST_AUTO_TEST_CASE(calldata)
+{
+ string sourceCode = R"(
+ contract C {
+ event E(bytes);
+ function f(bytes a) external {
+ E(a);
+ }
+ }
+ )";
+ string s("abcdef");
+ string t("abcdefgggggggggggggggggggggggggggggggggggggggghhheeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeggg");
+ bool newEncoder = false;
+ BOTH_ENCODERS(
+ compileAndRun(sourceCode);
+ callContractFunction("f(bytes)", 0x20, s.size(), s);
+ // The old encoder did not pad to multiples of 32 bytes
+ REQUIRE_LOG_DATA(encodeArgs(0x20, s.size()) + (newEncoder ? encodeArgs(s) : asBytes(s)));
+ callContractFunction("f(bytes)", 0x20, t.size(), t);
+ REQUIRE_LOG_DATA(encodeArgs(0x20, t.size()) + (newEncoder ? encodeArgs(t) : asBytes(t)));
+ newEncoder = true;
+ )
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+}
+}
+} // end namespaces
diff --git a/test/libsolidity/Imports.cpp b/test/libsolidity/Imports.cpp
index 00f093b7..03287b28 100644
--- a/test/libsolidity/Imports.cpp
+++ b/test/libsolidity/Imports.cpp
@@ -226,18 +226,19 @@ BOOST_AUTO_TEST_CASE(shadowing_builtins_with_imports)
}
)");
BOOST_CHECK(c.compile());
- auto numErrors = c.errors().size();
- // Sometimes we get the prerelease warning, sometimes not.
- BOOST_CHECK(2 <= numErrors && numErrors <= 3);
+ size_t errorCount = 0;
for (auto const& e: c.errors())
{
string const* msg = e->comment();
BOOST_REQUIRE(msg);
+ if (msg->find("pre-release") != string::npos)
+ continue;
BOOST_CHECK(
- msg->find("pre-release") != string::npos ||
msg->find("shadows a builtin symbol") != string::npos
);
+ errorCount++;
}
+ BOOST_CHECK_EQUAL(errorCount, 1);
}
BOOST_AUTO_TEST_CASE(shadowing_builtins_with_multiple_imports)
diff --git a/test/libsolidity/Metadata.cpp b/test/libsolidity/Metadata.cpp
index 0d3caddd..c46e3160 100644
--- a/test/libsolidity/Metadata.cpp
+++ b/test/libsolidity/Metadata.cpp
@@ -38,6 +38,7 @@ BOOST_AUTO_TEST_CASE(metadata_stamp)
// Check that the metadata stamp is at the end of the runtime bytecode.
char const* sourceCode = R"(
pragma solidity >=0.0;
+ pragma experimental __testOnlyAnalysis;
contract test {
function g(function(uint) external returns (uint) x) {}
}
@@ -58,6 +59,35 @@ BOOST_AUTO_TEST_CASE(metadata_stamp)
BOOST_CHECK(std::equal(expectation.begin(), expectation.end(), bytecode.end() - metadataCBORSize - 2));
}
+BOOST_AUTO_TEST_CASE(metadata_stamp_experimental)
+{
+ // Check that the metadata stamp is at the end of the runtime bytecode.
+ char const* sourceCode = R"(
+ pragma solidity >=0.0;
+ pragma experimental __test;
+ contract test {
+ function g(function(uint) external returns (uint) x) {}
+ }
+ )";
+ CompilerStack compilerStack;
+ compilerStack.addSource("", std::string(sourceCode));
+ compilerStack.setOptimiserSettings(dev::test::Options::get().optimize);
+ ETH_TEST_REQUIRE_NO_THROW(compilerStack.compile(), "Compiling contract failed");
+ bytes const& bytecode = compilerStack.runtimeObject("test").bytecode;
+ std::string const& metadata = compilerStack.metadata("test");
+ BOOST_CHECK(dev::test::isValidMetadata(metadata));
+ bytes hash = dev::swarmHash(metadata).asBytes();
+ BOOST_REQUIRE(hash.size() == 32);
+ BOOST_REQUIRE(bytecode.size() >= 2);
+ size_t metadataCBORSize = (size_t(bytecode.end()[-2]) << 8) + size_t(bytecode.end()[-1]);
+ BOOST_REQUIRE(metadataCBORSize < bytecode.size() - 2);
+ bytes expectation =
+ bytes{0xa2, 0x65, 'b', 'z', 'z', 'r', '0', 0x58, 0x20} +
+ hash +
+ bytes{0x6c, 'e', 'x', 'p', 'e', 'r', 'i', 'm', 'e', 'n', 't', 'a', 'l', 0xf5};
+ BOOST_CHECK(std::equal(expectation.begin(), expectation.end(), bytecode.end() - metadataCBORSize - 2));
+}
+
BOOST_AUTO_TEST_CASE(metadata_relevant_sources)
{
CompilerStack compilerStack;
diff --git a/test/libsolidity/SolidityABIJSON.cpp b/test/libsolidity/SolidityABIJSON.cpp
index 452a2662..80b4b6ad 100644
--- a/test/libsolidity/SolidityABIJSON.cpp
+++ b/test/libsolidity/SolidityABIJSON.cpp
@@ -76,6 +76,7 @@ BOOST_AUTO_TEST_CASE(basic_test)
"name": "f",
"constant": false,
"payable" : false,
+ "statemutability": "nonpayable",
"type": "function",
"inputs": [
{
@@ -119,6 +120,7 @@ BOOST_AUTO_TEST_CASE(multiple_methods)
"name": "f",
"constant": false,
"payable" : false,
+ "statemutability": "nonpayable",
"type": "function",
"inputs": [
{
@@ -137,6 +139,7 @@ BOOST_AUTO_TEST_CASE(multiple_methods)
"name": "g",
"constant": false,
"payable" : false,
+ "statemutability": "nonpayable",
"type": "function",
"inputs": [
{
@@ -169,6 +172,7 @@ BOOST_AUTO_TEST_CASE(multiple_params)
"name": "f",
"constant": false,
"payable" : false,
+ "statemutability": "nonpayable",
"type": "function",
"inputs": [
{
@@ -207,6 +211,7 @@ BOOST_AUTO_TEST_CASE(multiple_methods_order)
"name": "c",
"constant": false,
"payable" : false,
+ "statemutability": "nonpayable",
"type": "function",
"inputs": [
{
@@ -225,6 +230,7 @@ BOOST_AUTO_TEST_CASE(multiple_methods_order)
"name": "f",
"constant": false,
"payable" : false,
+ "statemutability": "nonpayable",
"type": "function",
"inputs": [
{
@@ -258,6 +264,7 @@ BOOST_AUTO_TEST_CASE(const_function)
"name": "foo",
"constant": false,
"payable" : false,
+ "statemutability": "nonpayable",
"type": "function",
"inputs": [
{
@@ -280,6 +287,7 @@ BOOST_AUTO_TEST_CASE(const_function)
"name": "boo",
"constant": true,
"payable" : false,
+ "statemutability": "view",
"type": "function",
"inputs": [{
"name": "a",
@@ -311,6 +319,7 @@ BOOST_AUTO_TEST_CASE(events)
"name": "f",
"constant": false,
"payable" : false,
+ "statemutability": "nonpayable",
"type": "function",
"inputs": [
{
@@ -392,6 +401,7 @@ BOOST_AUTO_TEST_CASE(inherited)
"name": "baseFunction",
"constant": false,
"payable" : false,
+ "statemutability": "nonpayable",
"type": "function",
"inputs":
[{
@@ -408,6 +418,7 @@ BOOST_AUTO_TEST_CASE(inherited)
"name": "derivedFunction",
"constant": false,
"payable" : false,
+ "statemutability": "nonpayable",
"type": "function",
"inputs":
[{
@@ -463,6 +474,7 @@ BOOST_AUTO_TEST_CASE(empty_name_input_parameter_with_named_one)
"name": "f",
"constant": false,
"payable" : false,
+ "statemutability": "nonpayable",
"type": "function",
"inputs": [
{
@@ -505,6 +517,7 @@ BOOST_AUTO_TEST_CASE(empty_name_return_parameter)
"name": "f",
"constant": false,
"payable" : false,
+ "statemutability": "nonpayable",
"type": "function",
"inputs": [
{
@@ -548,12 +561,44 @@ BOOST_AUTO_TEST_CASE(constructor_abi)
}
],
"payable": false,
+ "statemutability": "nonpayable",
"type": "constructor"
}
])";
checkInterface(sourceCode, interface);
}
+BOOST_AUTO_TEST_CASE(payable_constructor_abi)
+{
+ char const* sourceCode = R"(
+ contract test {
+ function test(uint param1, test param2, bool param3) payable {}
+ }
+ )";
+
+ char const* interface = R"([
+ {
+ "inputs": [
+ {
+ "name": "param1",
+ "type": "uint256"
+ },
+ {
+ "name": "param2",
+ "type": "address"
+ },
+ {
+ "name": "param3",
+ "type": "bool"
+ }
+ ],
+ "payable": true,
+ "statemutability": "payable",
+ "type": "constructor"
+ }
+ ])";
+ checkInterface(sourceCode, interface);
+}
BOOST_AUTO_TEST_CASE(return_param_in_abi)
{
@@ -574,6 +619,7 @@ BOOST_AUTO_TEST_CASE(return_param_in_abi)
{
"constant" : false,
"payable" : false,
+ "statemutability": "nonpayable",
"inputs" : [],
"name" : "ret",
"outputs" : [
@@ -592,6 +638,7 @@ BOOST_AUTO_TEST_CASE(return_param_in_abi)
}
],
"payable": false,
+ "statemutability": "nonpayable",
"type": "constructor"
}
]
@@ -613,6 +660,7 @@ BOOST_AUTO_TEST_CASE(strings_and_arrays)
{
"constant" : false,
"payable" : false,
+ "statemutability": "nonpayable",
"name": "f",
"inputs": [
{ "name": "a", "type": "string" },
@@ -641,6 +689,7 @@ BOOST_AUTO_TEST_CASE(library_function)
{
"constant" : false,
"payable" : false,
+ "statemutability": "nonpayable",
"name": "f",
"inputs": [
{ "name": "b", "type": "test.StructType storage" },
@@ -670,6 +719,7 @@ BOOST_AUTO_TEST_CASE(include_fallback_function)
[
{
"payable": false,
+ "statemutability": "nonpayable",
"type" : "fallback"
}
]
@@ -691,6 +741,7 @@ BOOST_AUTO_TEST_CASE(payable_function)
{
"constant" : false,
"payable": false,
+ "statemutability": "nonpayable",
"inputs": [],
"name": "f",
"outputs": [],
@@ -699,6 +750,7 @@ BOOST_AUTO_TEST_CASE(payable_function)
{
"constant" : false,
"payable": true,
+ "statemutability": "payable",
"inputs": [],
"name": "g",
"outputs": [],
@@ -721,6 +773,7 @@ BOOST_AUTO_TEST_CASE(payable_fallback_function)
[
{
"payable": true,
+ "statemutability": "payable",
"type" : "fallback"
}
]
@@ -741,6 +794,7 @@ BOOST_AUTO_TEST_CASE(function_type)
{
"constant" : false,
"payable": false,
+ "statemutability": "nonpayable",
"inputs": [{
"name": "x",
"type": "function"
diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp
index 5bcde441..73dd7d22 100644
--- a/test/libsolidity/SolidityEndToEndTest.cpp
+++ b/test/libsolidity/SolidityEndToEndTest.cpp
@@ -1950,6 +1950,87 @@ BOOST_AUTO_TEST_CASE(ripemd)
testContractAgainstCpp("a(bytes32)", f, u256(-1));
}
+BOOST_AUTO_TEST_CASE(packed_keccak256)
+{
+ char const* sourceCode = R"(
+ contract test {
+ function a(bytes32 input) returns (bytes32 hash) {
+ var b = 65536;
+ uint c = 256;
+ return keccak256(8, input, b, input, c);
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ auto f = [&](u256 const& _x) -> u256
+ {
+ return dev::keccak256(
+ toCompactBigEndian(unsigned(8)) +
+ toBigEndian(_x) +
+ toCompactBigEndian(unsigned(65536)) +
+ toBigEndian(_x) +
+ toBigEndian(u256(256))
+ );
+ };
+ testContractAgainstCpp("a(bytes32)", f, u256(4));
+ testContractAgainstCpp("a(bytes32)", f, u256(5));
+ testContractAgainstCpp("a(bytes32)", f, u256(-1));
+}
+
+BOOST_AUTO_TEST_CASE(packed_sha256)
+{
+ char const* sourceCode = R"(
+ contract test {
+ function a(bytes32 input) returns (bytes32 hash) {
+ var b = 65536;
+ uint c = 256;
+ return sha256(8, input, b, input, c);
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ auto f = [&](u256 const& _x) -> bytes
+ {
+ if (_x == u256(4))
+ return fromHex("804e0d7003cfd70fc925dc103174d9f898ebb142ecc2a286da1abd22ac2ce3ac");
+ if (_x == u256(5))
+ return fromHex("e94921945f9068726c529a290a954f412bcac53184bb41224208a31edbf63cf0");
+ if (_x == u256(-1))
+ return fromHex("f14def4d07cd185ddd8b10a81b2238326196a38867e6e6adbcc956dc913488c7");
+ return fromHex("");
+ };
+ testContractAgainstCpp("a(bytes32)", f, u256(4));
+ testContractAgainstCpp("a(bytes32)", f, u256(5));
+ testContractAgainstCpp("a(bytes32)", f, u256(-1));
+}
+
+BOOST_AUTO_TEST_CASE(packed_ripemd160)
+{
+ char const* sourceCode = R"(
+ contract test {
+ function a(bytes32 input) returns (bytes32 hash) {
+ var b = 65536;
+ uint c = 256;
+ return ripemd160(8, input, b, input, c);
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ auto f = [&](u256 const& _x) -> bytes
+ {
+ if (_x == u256(4))
+ return fromHex("f93175303eba2a7b372174fc9330237f5ad202fc000000000000000000000000");
+ if (_x == u256(5))
+ return fromHex("04f4fc112e2bfbe0d38f896a46629e08e2fcfad5000000000000000000000000");
+ if (_x == u256(-1))
+ return fromHex("c0a2e4b1f3ff766a9a0089e7a410391730872495000000000000000000000000");
+ return fromHex("");
+ };
+ testContractAgainstCpp("a(bytes32)", f, u256(4));
+ testContractAgainstCpp("a(bytes32)", f, u256(5));
+ testContractAgainstCpp("a(bytes32)", f, u256(-1));
+}
+
BOOST_AUTO_TEST_CASE(ecrecover)
{
char const* sourceCode = R"(
@@ -2318,21 +2399,6 @@ BOOST_AUTO_TEST_CASE(gas_and_value_basic)
BOOST_REQUIRE(callContractFunction("checkState()") == encodeArgs(false, 20 - 5));
}
-BOOST_AUTO_TEST_CASE(gas_for_builtin)
-{
- char const* sourceCode = R"(
- contract Contract {
- function test(uint g) returns (bytes32 data, bool flag) {
- data = ripemd160.gas(g)("abc");
- flag = true;
- }
- }
- )";
- compileAndRun(sourceCode);
- BOOST_CHECK(callContractFunction("test(uint256)", 500) == bytes());
- BOOST_CHECK(callContractFunction("test(uint256)", 800) == encodeArgs(u256("0x8eb208f7e05d987a9b044a8e98c6b087f15a0bfc000000000000000000000000"), true));
-}
-
BOOST_AUTO_TEST_CASE(value_complex)
{
char const* sourceCode = R"(
@@ -3062,7 +3128,7 @@ BOOST_AUTO_TEST_CASE(event_really_lots_of_data)
callContractFunction("deposit()");
BOOST_REQUIRE_EQUAL(m_logs.size(), 1);
BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress);
- BOOST_CHECK(m_logs[0].data == encodeArgs(10, 0x60, 15, 4) + FixedHash<4>(dev::keccak256("deposit()")).asBytes());
+ BOOST_CHECK_EQUAL(toHex(m_logs[0].data), toHex(encodeArgs(10, 0x60, 15, 4) + FixedHash<4>(dev::keccak256("deposit()")).asBytes()));
BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1);
BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("Deposit(uint256,bytes,uint256)")));
}
@@ -3086,7 +3152,32 @@ BOOST_AUTO_TEST_CASE(event_really_lots_of_data_from_storage)
callContractFunction("deposit()");
BOOST_REQUIRE_EQUAL(m_logs.size(), 1);
BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress);
- BOOST_CHECK(m_logs[0].data == encodeArgs(10, 0x60, 15, 3, string("ABC")));
+ BOOST_CHECK_EQUAL(toHex(m_logs[0].data), toHex(encodeArgs(10, 0x60, 15, 3, string("ABC"))));
+ BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1);
+ BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("Deposit(uint256,bytes,uint256)")));
+}
+
+BOOST_AUTO_TEST_CASE(event_really_really_lots_of_data_from_storage)
+{
+ char const* sourceCode = R"(
+ contract ClientReceipt {
+ bytes x;
+ event Deposit(uint fixeda, bytes dynx, uint fixedb);
+ function deposit() {
+ x.length = 31;
+ x[0] = "A";
+ x[1] = "B";
+ x[2] = "C";
+ x[30] = "Z";
+ Deposit(10, x, 15);
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ callContractFunction("deposit()");
+ BOOST_REQUIRE_EQUAL(m_logs.size(), 1);
+ BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress);
+ BOOST_CHECK(m_logs[0].data == encodeArgs(10, 0x60, 15, 31, string("ABC") + string(27, 0) + "Z"));
BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1);
BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("Deposit(uint256,bytes,uint256)")));
}
@@ -4336,6 +4427,92 @@ BOOST_AUTO_TEST_CASE(array_copy_storage_storage_struct)
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,
+ // because it will always copy to memory first.
+ char const* sourceCode = R"(
+ pragma experimental ABIEncoderV2;
+ contract c {
+ uint8[] x;
+ uint16[] y;
+ uint24[] z;
+ uint24[][] w;
+ function test1() returns (uint8[]) {
+ for (uint i = 0; i < 101; ++i)
+ x.push(uint8(i));
+ return x;
+ }
+ function test2() returns (uint16[]) {
+ for (uint i = 0; i < 101; ++i)
+ y.push(uint16(i));
+ return y;
+ }
+ function test3() returns (uint24[]) {
+ for (uint i = 0; i < 101; ++i)
+ z.push(uint24(i));
+ return z;
+ }
+ function test4() returns (uint24[][]) {
+ w.length = 5;
+ for (uint i = 0; i < 5; ++i)
+ for (uint j = 0; j < 101; ++j)
+ w[i].push(uint24(j));
+ return w;
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ bytes valueSequence;
+ for (size_t i = 0; i < 101; ++i)
+ valueSequence += toBigEndian(u256(i));
+ BOOST_CHECK(callContractFunction("test1()") == encodeArgs(0x20, 101) + valueSequence);
+ BOOST_CHECK(callContractFunction("test2()") == encodeArgs(0x20, 101) + valueSequence);
+ BOOST_CHECK(callContractFunction("test3()") == encodeArgs(0x20, 101) + valueSequence);
+ BOOST_CHECK(callContractFunction("test4()") ==
+ encodeArgs(0x20, 5, 0xa0, 0xa0 + 102 * 32 * 1, 0xa0 + 102 * 32 * 2, 0xa0 + 102 * 32 * 3, 0xa0 + 102 * 32 * 4) +
+ encodeArgs(101) + valueSequence +
+ encodeArgs(101) + valueSequence +
+ encodeArgs(101) + valueSequence +
+ encodeArgs(101) + valueSequence +
+ encodeArgs(101) + valueSequence
+ );
+}
+
+BOOST_AUTO_TEST_CASE(array_copy_storage_abi_signed)
+{
+ // NOTE: This does not really test copying from storage to ABI directly,
+ // because it will always copy to memory first.
+ char const* sourceCode = R"(
+ contract c {
+ int16[] x;
+ function test() returns (int16[]) {
+ x.push(int16(-1));
+ x.push(int16(-1));
+ x.push(int16(8));
+ x.push(int16(-16));
+ x.push(int16(-2));
+ x.push(int16(6));
+ x.push(int16(8));
+ x.push(int16(-1));
+ return x;
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ bytes valueSequence;
+ BOOST_CHECK(callContractFunction("test()") == encodeArgs(0x20, 8,
+ u256(-1),
+ u256(-1),
+ u256(8),
+ u256(-16),
+ u256(-2),
+ u256(6),
+ u256(8),
+ u256(-1)
+ ));
+}
+
BOOST_AUTO_TEST_CASE(array_push)
{
char const* sourceCode = R"(
@@ -8251,6 +8428,53 @@ BOOST_AUTO_TEST_CASE(failing_ecrecover_invalid_input)
BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(0)));
}
+BOOST_AUTO_TEST_CASE(failing_ecrecover_invalid_input_proper)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function f() returns (address) {
+ return recover(
+ 0x77e5189111eb6557e8a637b27ef8fbb15bc61d61c2f00cc48878f3a296e5e0ca,
+ 0, // invalid v value
+ 0x6944c77849b18048f6abe0db8084b0d0d0689cdddb53d2671c36967b58691ad4,
+ 0xef4f06ba4f78319baafd0424365777241af4dfd3da840471b4b4b087b7750d0d,
+ 0xca35b7d915458ef540ade6068dfe2f44e8fa733c,
+ 0xca35b7d915458ef540ade6068dfe2f44e8fa733c
+ );
+ }
+ function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s, uint blockExpired, bytes32 salt)
+ returns (address)
+ {
+ require(hash == keccak256(blockExpired, salt));
+ return ecrecover(hash, v, r, s);
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "C");
+ BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(0)));
+}
+
+BOOST_AUTO_TEST_CASE(failing_ecrecover_invalid_input_asm)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function f() returns (address) {
+ assembly {
+ mstore(mload(0x40), 0xca35b7d915458ef540ade6068dfe2f44e8fa733c)
+ }
+ return ecrecover(
+ 0x77e5189111eb6557e8a637b27ef8fbb15bc61d61c2f00cc48878f3a296e5e0ca,
+ 0, // invalid v value
+ 0x6944c77849b18048f6abe0db8084b0d0d0689cdddb53d2671c36967b58691ad4,
+ 0xef4f06ba4f78319baafd0424365777241af4dfd3da840471b4b4b087b7750d0d
+ );
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "C");
+ BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(0)));
+}
+
BOOST_AUTO_TEST_CASE(calling_nonexisting_contract_throws)
{
char const* sourceCode = R"(
@@ -9769,6 +9993,64 @@ BOOST_AUTO_TEST_CASE(inlineasm_empty_let)
BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(0), u256(0)));
}
+BOOST_AUTO_TEST_CASE(bare_call_invalid_address)
+{
+ char const* sourceCode = R"(
+ contract C {
+ /// Calling into non-existant account is successful (creates the account)
+ function f() external constant returns (bool) {
+ return address(0x4242).call();
+ }
+ function g() external constant returns (bool) {
+ return address(0x4242).callcode();
+ }
+ function h() external constant returns (bool) {
+ return address(0x4242).delegatecall();
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "C");
+ BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(1)));
+ BOOST_CHECK(callContractFunction("g()") == encodeArgs(u256(1)));
+ BOOST_CHECK(callContractFunction("h()") == encodeArgs(u256(1)));
+}
+
+BOOST_AUTO_TEST_CASE(delegatecall_return_value)
+{
+ char const* sourceCode = R"DELIMITER(
+ contract C {
+ uint value;
+ function set(uint _value) external {
+ value = _value;
+ }
+ function get() external constant returns (uint) {
+ return value;
+ }
+ function get_delegated() external constant returns (bool) {
+ return this.delegatecall(bytes4(sha3("get()")));
+ }
+ function assert0() external constant {
+ assert(value == 0);
+ }
+ function assert0_delegated() external constant returns (bool) {
+ return this.delegatecall(bytes4(sha3("assert0()")));
+ }
+ }
+ )DELIMITER";
+ compileAndRun(sourceCode, 0, "C");
+ BOOST_CHECK(callContractFunction("get()") == encodeArgs(u256(0)));
+ BOOST_CHECK(callContractFunction("assert0_delegated()") == encodeArgs(u256(1)));
+ BOOST_CHECK(callContractFunction("get_delegated()") == encodeArgs(u256(1)));
+ BOOST_CHECK(callContractFunction("set(uint256)", u256(1)) == encodeArgs());
+ BOOST_CHECK(callContractFunction("get()") == encodeArgs(u256(1)));
+ BOOST_CHECK(callContractFunction("assert0_delegated()") == encodeArgs(u256(0)));
+ BOOST_CHECK(callContractFunction("get_delegated()") == encodeArgs(u256(1)));
+ BOOST_CHECK(callContractFunction("set(uint256)", u256(42)) == encodeArgs());
+ BOOST_CHECK(callContractFunction("get()") == encodeArgs(u256(42)));
+ BOOST_CHECK(callContractFunction("assert0_delegated()") == encodeArgs(u256(0)));
+ BOOST_CHECK(callContractFunction("get_delegated()") == encodeArgs(u256(1)));
+}
+
BOOST_AUTO_TEST_SUITE_END()
}
diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp
index cd922cc8..51d60596 100644
--- a/test/libsolidity/SolidityNameAndTypeResolution.cpp
+++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp
@@ -249,7 +249,7 @@ BOOST_AUTO_TEST_CASE(double_stateVariable_declaration)
uint128 variable;
}
)";
- CHECK_ERROR(text, DeclarationError, "");
+ CHECK_ERROR(text, DeclarationError, "Identifier already declared.");
}
BOOST_AUTO_TEST_CASE(double_function_declaration)
@@ -260,7 +260,7 @@ BOOST_AUTO_TEST_CASE(double_function_declaration)
function fun() { }
}
)";
- CHECK_ERROR(text, DeclarationError, "");
+ CHECK_ERROR(text, DeclarationError, "Function with same name and arguments defined twice.");
}
BOOST_AUTO_TEST_CASE(double_variable_declaration)
@@ -273,7 +273,7 @@ BOOST_AUTO_TEST_CASE(double_variable_declaration)
}
}
)";
- CHECK_ERROR(text, DeclarationError, "");
+ CHECK_ERROR(text, DeclarationError, "Identifier already declared.");
}
BOOST_AUTO_TEST_CASE(name_shadowing)
@@ -308,7 +308,7 @@ BOOST_AUTO_TEST_CASE(undeclared_name)
}
}
)";
- CHECK_ERROR(text, DeclarationError, "");
+ CHECK_ERROR(text, DeclarationError, "Undeclared identifier.");
}
BOOST_AUTO_TEST_CASE(reference_to_later_declaration)
@@ -332,7 +332,7 @@ BOOST_AUTO_TEST_CASE(struct_definition_directly_recursive)
}
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Recursive struct definition.");
}
BOOST_AUTO_TEST_CASE(struct_definition_indirectly_recursive)
@@ -349,7 +349,7 @@ BOOST_AUTO_TEST_CASE(struct_definition_indirectly_recursive)
}
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Recursive struct definition.");
}
BOOST_AUTO_TEST_CASE(struct_definition_not_really_recursive)
@@ -406,7 +406,7 @@ BOOST_AUTO_TEST_CASE(type_checking_return_wrong_number)
function f() returns (bool r1, bool r2) { return 1 >= 2; }
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Different number of arguments in return statement than in returns declaration.");
}
BOOST_AUTO_TEST_CASE(type_checking_return_wrong_type)
@@ -416,7 +416,7 @@ BOOST_AUTO_TEST_CASE(type_checking_return_wrong_type)
function f() returns (uint256 r) { return 1 >= 2; }
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Return argument type bool is not implicitly convertible to expected type (type of first return variable) uint256.");
}
BOOST_AUTO_TEST_CASE(type_checking_function_call)
@@ -447,7 +447,7 @@ BOOST_AUTO_TEST_CASE(type_conversion_for_comparison_invalid)
function f() { int32(2) == uint64(2); }
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Operator == not compatible with types int32 and uint64");
}
BOOST_AUTO_TEST_CASE(type_inference_explicit_conversion)
@@ -491,7 +491,7 @@ BOOST_AUTO_TEST_CASE(balance_invalid)
}
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Expression has to be an lvalue.");
}
BOOST_AUTO_TEST_CASE(assignment_to_mapping)
@@ -508,7 +508,7 @@ BOOST_AUTO_TEST_CASE(assignment_to_mapping)
}
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Mappings cannot be assigned to.");
}
BOOST_AUTO_TEST_CASE(assignment_to_struct)
@@ -535,7 +535,7 @@ BOOST_AUTO_TEST_CASE(returns_in_constructor)
function test() returns (uint a) { }
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Non-empty \"returns\" directive for constructor.");
}
BOOST_AUTO_TEST_CASE(forward_function_reference)
@@ -624,7 +624,7 @@ BOOST_AUTO_TEST_CASE(function_no_implementation)
std::vector<ASTPointer<ASTNode>> nodes = sourceUnit->nodes();
ContractDefinition* contract = dynamic_cast<ContractDefinition*>(nodes[1].get());
BOOST_REQUIRE(contract);
- BOOST_CHECK(!contract->annotation().isFullyImplemented);
+ BOOST_CHECK(!contract->annotation().unimplementedFunctions.empty());
BOOST_CHECK(!contract->definedFunctions()[0]->isImplemented());
}
@@ -640,10 +640,10 @@ BOOST_AUTO_TEST_CASE(abstract_contract)
ContractDefinition* base = dynamic_cast<ContractDefinition*>(nodes[1].get());
ContractDefinition* derived = dynamic_cast<ContractDefinition*>(nodes[2].get());
BOOST_REQUIRE(base);
- BOOST_CHECK(!base->annotation().isFullyImplemented);
+ BOOST_CHECK(!base->annotation().unimplementedFunctions.empty());
BOOST_CHECK(!base->definedFunctions()[0]->isImplemented());
BOOST_REQUIRE(derived);
- BOOST_CHECK(derived->annotation().isFullyImplemented);
+ BOOST_CHECK(derived->annotation().unimplementedFunctions.empty());
BOOST_CHECK(derived->definedFunctions()[0]->isImplemented());
}
@@ -659,9 +659,9 @@ BOOST_AUTO_TEST_CASE(abstract_contract_with_overload)
ContractDefinition* base = dynamic_cast<ContractDefinition*>(nodes[1].get());
ContractDefinition* derived = dynamic_cast<ContractDefinition*>(nodes[2].get());
BOOST_REQUIRE(base);
- BOOST_CHECK(!base->annotation().isFullyImplemented);
+ BOOST_CHECK(!base->annotation().unimplementedFunctions.empty());
BOOST_REQUIRE(derived);
- BOOST_CHECK(!derived->annotation().isFullyImplemented);
+ BOOST_CHECK(!derived->annotation().unimplementedFunctions.empty());
}
BOOST_AUTO_TEST_CASE(create_abstract_contract)
@@ -674,45 +674,7 @@ BOOST_AUTO_TEST_CASE(create_abstract_contract)
function foo() { b = new base(); }
}
)";
- CHECK_ERROR(text, TypeError, "");
-}
-
-BOOST_AUTO_TEST_CASE(abstract_contract_constructor_args_optional)
-{
- ASTPointer<SourceUnit> sourceUnit;
- char const* text = R"(
- contract BaseBase { function BaseBase(uint j); }
- contract base is BaseBase { function foo(); }
- contract derived is base {
- function derived(uint i) BaseBase(i){}
- function foo() {}
- }
- )";
- ETH_TEST_REQUIRE_NO_THROW(sourceUnit = parseAndAnalyse(text), "Parsing and name resolving failed");
- std::vector<ASTPointer<ASTNode>> nodes = sourceUnit->nodes();
- BOOST_CHECK_EQUAL(nodes.size(), 4);
- ContractDefinition* derived = dynamic_cast<ContractDefinition*>(nodes[3].get());
- BOOST_REQUIRE(derived);
- BOOST_CHECK(!derived->annotation().isFullyImplemented);
-}
-
-BOOST_AUTO_TEST_CASE(abstract_contract_constructor_args_not_provided)
-{
- ASTPointer<SourceUnit> sourceUnit;
- char const* text = R"(
- contract BaseBase { function BaseBase(uint); }
- contract base is BaseBase { function foo(); }
- contract derived is base {
- function derived(uint) {}
- function foo() {}
- }
- )";
- ETH_TEST_REQUIRE_NO_THROW(sourceUnit = parseAndAnalyse(text), "Parsing and name resolving failed");
- std::vector<ASTPointer<ASTNode>> nodes = sourceUnit->nodes();
- BOOST_CHECK_EQUAL(nodes.size(), 4);
- ContractDefinition* derived = dynamic_cast<ContractDefinition*>(nodes[3].get());
- BOOST_REQUIRE(derived);
- BOOST_CHECK(!derived->annotation().isFullyImplemented);
+ CHECK_ERROR(text, TypeError, "Trying to create an instance of an abstract contract.");
}
BOOST_AUTO_TEST_CASE(redeclare_implemented_abstract_function_as_abstract)
@@ -723,7 +685,7 @@ BOOST_AUTO_TEST_CASE(redeclare_implemented_abstract_function_as_abstract)
contract derived is base { function foo() {} }
contract wrong is derived { function foo(); }
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Redeclaring an already implemented function as abstract");
}
BOOST_AUTO_TEST_CASE(implement_abstract_via_constructor)
@@ -738,7 +700,7 @@ BOOST_AUTO_TEST_CASE(implement_abstract_via_constructor)
BOOST_CHECK_EQUAL(nodes.size(), 3);
ContractDefinition* derived = dynamic_cast<ContractDefinition*>(nodes[2].get());
BOOST_REQUIRE(derived);
- BOOST_CHECK(!derived->annotation().isFullyImplemented);
+ BOOST_CHECK(!derived->annotation().unimplementedFunctions.empty());
}
BOOST_AUTO_TEST_CASE(function_canonical_signature)
@@ -855,7 +817,7 @@ BOOST_AUTO_TEST_CASE(function_external_call_not_allowed_conversion)
function g (C c) external {}
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Invalid type for argument in function call. Invalid implicit conversion from address to contract C requested.");
}
BOOST_AUTO_TEST_CASE(function_internal_allowed_conversion)
@@ -889,7 +851,7 @@ BOOST_AUTO_TEST_CASE(function_internal_not_allowed_conversion)
}
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Invalid type for argument in function call. Invalid implicit conversion from address to contract C requested.");
}
BOOST_AUTO_TEST_CASE(hash_collision_in_interface)
@@ -900,7 +862,7 @@ BOOST_AUTO_TEST_CASE(hash_collision_in_interface)
function tgeo() { }
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Function signature hash collision for tgeo()");
}
BOOST_AUTO_TEST_CASE(inheritance_basic)
@@ -934,7 +896,7 @@ BOOST_AUTO_TEST_CASE(cyclic_inheritance)
contract A is B { }
contract B is A { }
)";
- CHECK_ERROR_ALLOW_MULTI(text, TypeError, "");
+ CHECK_ERROR_ALLOW_MULTI(text, TypeError, "Definition of base has to precede definition of derived contract");
}
BOOST_AUTO_TEST_CASE(legal_override_direct)
@@ -962,16 +924,25 @@ BOOST_AUTO_TEST_CASE(illegal_override_visibility)
contract B { function f() internal {} }
contract C is B { function f() public {} }
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Overriding function visibility differs");
}
-BOOST_AUTO_TEST_CASE(illegal_override_constness)
+BOOST_AUTO_TEST_CASE(illegal_override_remove_constness)
{
char const* text = R"(
contract B { function f() constant {} }
contract C is B { function f() {} }
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Overriding function should be declared constant.");
+}
+
+BOOST_AUTO_TEST_CASE(illegal_override_add_constness)
+{
+ char const* text = R"(
+ contract B { function f() {} }
+ contract C is B { function f() constant {} }
+ )";
+ CHECK_ERROR(text, TypeError, "Overriding function should not be declared constant.");
}
BOOST_AUTO_TEST_CASE(complex_inheritance)
@@ -1041,7 +1012,7 @@ BOOST_AUTO_TEST_CASE(implicit_base_to_derived_conversion)
function f() { B b = A(1); }
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Type contract A is not implicitly convertible to expected type contract B.");
}
BOOST_AUTO_TEST_CASE(super_excludes_current_contract)
@@ -1058,7 +1029,7 @@ BOOST_AUTO_TEST_CASE(super_excludes_current_contract)
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Member \"f\" not found or not visible after argument-dependent lookup in contract super B");
}
BOOST_AUTO_TEST_CASE(function_modifier_invocation)
@@ -1081,7 +1052,7 @@ BOOST_AUTO_TEST_CASE(invalid_function_modifier_type)
modifier mod1(uint a) { if (a > 0) _; }
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Invalid type for argument in modifier invocation. Invalid implicit conversion from bool to uint256 requested.");
}
BOOST_AUTO_TEST_CASE(function_modifier_invocation_parameters)
@@ -1144,7 +1115,7 @@ BOOST_AUTO_TEST_CASE(illegal_modifier_override)
contract A { modifier mod(uint a) { _; } }
contract B is A { modifier mod(uint8 a) { _; } }
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Override changes modifier signature.");
}
BOOST_AUTO_TEST_CASE(modifier_overrides_function)
@@ -1155,7 +1126,7 @@ BOOST_AUTO_TEST_CASE(modifier_overrides_function)
)";
// Error: Identifier already declared.
// Error: Override changes modifier to function.
- CHECK_ERROR_ALLOW_MULTI(text, DeclarationError, "Identifier already declared");
+ CHECK_ERROR_ALLOW_MULTI(text, DeclarationError, "Identifier already declared.");
}
BOOST_AUTO_TEST_CASE(function_overrides_modifier)
@@ -1166,7 +1137,7 @@ BOOST_AUTO_TEST_CASE(function_overrides_modifier)
)";
// Error: Identifier already declared.
// Error: Override changes function to modifier.
- CHECK_ERROR_ALLOW_MULTI(text, DeclarationError, "");
+ CHECK_ERROR_ALLOW_MULTI(text, DeclarationError, "Identifier already declared.");
}
BOOST_AUTO_TEST_CASE(modifier_returns_value)
@@ -1232,7 +1203,7 @@ BOOST_AUTO_TEST_CASE(function_clash_with_state_variable_accessor)
function foo() {}
}
)";
- CHECK_ERROR(text, DeclarationError, "");
+ CHECK_ERROR(text, DeclarationError, "Identifier already declared.");
}
BOOST_AUTO_TEST_CASE(private_state_variable)
@@ -1267,7 +1238,7 @@ BOOST_AUTO_TEST_CASE(missing_state_variable)
}
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Member \"stateVar\" not found or not visible after argument-dependent lookup in type(contract Scope)");
}
@@ -1293,7 +1264,7 @@ BOOST_AUTO_TEST_CASE(struct_accessor_one_array_only)
Data public data;
}
)";
- CHECK_ERROR(sourceCode, TypeError, "");
+ CHECK_ERROR(sourceCode, TypeError, "Internal type is not allowed for public state variables.");
}
BOOST_AUTO_TEST_CASE(base_class_state_variable_internal_member)
@@ -1322,7 +1293,7 @@ BOOST_AUTO_TEST_CASE(state_variable_member_of_wrong_class1)
function foo() returns (uint256) { return Parent2.m_aMember1; }
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Member \"m_aMember1\" not found or not visible after argument-dependent lookup in type(contract Parent2)");
}
BOOST_AUTO_TEST_CASE(state_variable_member_of_wrong_class2)
@@ -1339,7 +1310,7 @@ BOOST_AUTO_TEST_CASE(state_variable_member_of_wrong_class2)
uint256 public m_aMember3;
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Member \"m_aMember2\" not found or not visible after argument-dependent lookup in type(contract Child)");
}
BOOST_AUTO_TEST_CASE(fallback_function)
@@ -1361,7 +1332,7 @@ BOOST_AUTO_TEST_CASE(fallback_function_with_arguments)
function(uint a) { x = 2; }
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Fallback function cannot take parameters.");
}
BOOST_AUTO_TEST_CASE(fallback_function_in_library)
@@ -1371,7 +1342,7 @@ BOOST_AUTO_TEST_CASE(fallback_function_in_library)
function() {}
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Libraries cannot have fallback functions.");
}
BOOST_AUTO_TEST_CASE(fallback_function_with_return_parameters)
@@ -1381,7 +1352,7 @@ BOOST_AUTO_TEST_CASE(fallback_function_with_return_parameters)
function() returns (uint) { }
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Fallback function cannot return values.");
}
BOOST_AUTO_TEST_CASE(fallback_function_with_constant_modifier)
@@ -1392,7 +1363,7 @@ BOOST_AUTO_TEST_CASE(fallback_function_with_constant_modifier)
function() constant { x = 2; }
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Fallback function cannot be declared constant.");
}
BOOST_AUTO_TEST_CASE(fallback_function_twice)
@@ -1404,7 +1375,7 @@ BOOST_AUTO_TEST_CASE(fallback_function_twice)
function() { x = 3; }
}
)";
- CHECK_ERROR_ALLOW_MULTI(text, DeclarationError, "");
+ CHECK_ERROR_ALLOW_MULTI(text, DeclarationError, "Function with same name and arguments defined twice.");
}
BOOST_AUTO_TEST_CASE(fallback_function_inheritance)
@@ -1439,7 +1410,7 @@ BOOST_AUTO_TEST_CASE(event_too_many_indexed)
event e(uint indexed a, bytes3 indexed b, bool indexed c, uint indexed d);
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "More than 3 indexed arguments for event.");
}
BOOST_AUTO_TEST_CASE(anonymous_event_four_indexed)
@@ -1459,7 +1430,7 @@ BOOST_AUTO_TEST_CASE(anonymous_event_too_many_indexed)
event e(uint indexed a, bytes3 indexed b, bool indexed c, uint indexed d, uint indexed e) anonymous;
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "More than 4 indexed arguments for anonymous event.");
}
BOOST_AUTO_TEST_CASE(events_with_same_name)
@@ -1578,7 +1549,7 @@ BOOST_AUTO_TEST_CASE(access_to_internal_function)
function g() { c(0).f(); }
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Member \"f\" not found or not visible after argument-dependent lookup in contract c");
}
BOOST_AUTO_TEST_CASE(access_to_default_state_variable_visibility)
@@ -1591,7 +1562,7 @@ BOOST_AUTO_TEST_CASE(access_to_default_state_variable_visibility)
function g() { c(0).a(); }
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Member \"a\" not found or not visible after argument-dependent lookup in contract c");
}
BOOST_AUTO_TEST_CASE(access_to_internal_state_variable)
@@ -1619,7 +1590,7 @@ BOOST_AUTO_TEST_CASE(error_count_in_named_args)
}
}
)";
- CHECK_ERROR(sourceCode, TypeError, "");
+ CHECK_ERROR(sourceCode, TypeError, "Wrong argument count for function call: 1 arguments given but expected 2.");
}
BOOST_AUTO_TEST_CASE(empty_in_named_args)
@@ -1634,7 +1605,7 @@ BOOST_AUTO_TEST_CASE(empty_in_named_args)
}
}
)";
- CHECK_ERROR(sourceCode, TypeError, "");
+ CHECK_ERROR(sourceCode, TypeError, "Wrong argument count for function call: 0 arguments given but expected 2.");
}
BOOST_AUTO_TEST_CASE(duplicate_parameter_names_in_named_args)
@@ -1649,7 +1620,7 @@ BOOST_AUTO_TEST_CASE(duplicate_parameter_names_in_named_args)
}
}
)";
- CHECK_ERROR(sourceCode, TypeError, "");
+ CHECK_ERROR(sourceCode, TypeError, "Duplicate named argument.");
}
BOOST_AUTO_TEST_CASE(invalid_parameter_names_in_named_args)
@@ -1664,7 +1635,7 @@ BOOST_AUTO_TEST_CASE(invalid_parameter_names_in_named_args)
}
}
)";
- CHECK_ERROR(sourceCode, TypeError, "");
+ CHECK_ERROR(sourceCode, TypeError, "Named argument does not match function declaration.");
}
BOOST_AUTO_TEST_CASE(empty_name_input_parameter)
@@ -1718,7 +1689,7 @@ BOOST_AUTO_TEST_CASE(empty_name_return_parameter_with_named_one)
}
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Different number of arguments in return statement than in returns declaration.");
}
BOOST_AUTO_TEST_CASE(disallow_declaration_of_void_type)
@@ -1728,7 +1699,7 @@ BOOST_AUTO_TEST_CASE(disallow_declaration_of_void_type)
function f() { var (x) = f(); }
}
)";
- CHECK_ERROR(sourceCode, TypeError, "");
+ CHECK_ERROR(sourceCode, TypeError, "Not enough components (0) in value to assign all variables (1).");
}
BOOST_AUTO_TEST_CASE(overflow_caused_by_ether_units)
@@ -1751,7 +1722,7 @@ BOOST_AUTO_TEST_CASE(overflow_caused_by_ether_units)
uint256 a;
}
)";
- CHECK_ERROR(sourceCode, TypeError, "");
+ CHECK_ERROR(sourceCode, TypeError, "Type int_const 115792089237316195423570985008687907853269984665640564039458000000000000000000 is not implicitly convertible to expected type uint256.");
}
BOOST_AUTO_TEST_CASE(exp_operator_exponent_too_big)
@@ -1761,7 +1732,7 @@ BOOST_AUTO_TEST_CASE(exp_operator_exponent_too_big)
function f() returns(uint d) { return 2 ** 10000000000; }
}
)";
- CHECK_ERROR(sourceCode, TypeError, "");
+ CHECK_ERROR(sourceCode, TypeError, "Operator ** not compatible with types int_const 2 and int_const 10000000000");
}
BOOST_AUTO_TEST_CASE(exp_warn_literal_base)
@@ -1875,7 +1846,7 @@ BOOST_AUTO_TEST_CASE(enum_invalid_member_access)
ActionChoices choices;
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Member \"RunAroundWavingYourHands\" not found or not visible after argument-dependent lookup in type(enum test.ActionChoices)");
}
BOOST_AUTO_TEST_CASE(enum_invalid_direct_member_access)
@@ -1889,7 +1860,7 @@ BOOST_AUTO_TEST_CASE(enum_invalid_direct_member_access)
ActionChoices choices;
}
)";
- CHECK_ERROR(text, DeclarationError, "");
+ CHECK_ERROR(text, DeclarationError, "Undeclared identifier.");
}
BOOST_AUTO_TEST_CASE(enum_explicit_conversion_is_okay)
@@ -1935,7 +1906,7 @@ BOOST_AUTO_TEST_CASE(enum_implicit_conversion_is_not_okay_256)
uint256 a;
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Type enum test.ActionChoices is not implicitly convertible to expected type uint256.");
}
BOOST_AUTO_TEST_CASE(enum_implicit_conversion_is_not_okay_64)
@@ -1949,7 +1920,7 @@ BOOST_AUTO_TEST_CASE(enum_implicit_conversion_is_not_okay_64)
uint64 b;
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Type enum test.ActionChoices is not implicitly convertible to expected type uint64.");
}
BOOST_AUTO_TEST_CASE(enum_to_enum_conversion_is_not_okay)
@@ -1963,7 +1934,7 @@ BOOST_AUTO_TEST_CASE(enum_to_enum_conversion_is_not_okay)
}
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Explicit type conversion not allowed from \"enum test.Paper\" to \"enum test.Ground\".");
}
BOOST_AUTO_TEST_CASE(enum_duplicate_values)
@@ -1973,7 +1944,7 @@ BOOST_AUTO_TEST_CASE(enum_duplicate_values)
enum ActionChoices { GoLeft, GoRight, GoLeft, Sit }
}
)";
- CHECK_ERROR(text, DeclarationError, "");
+ CHECK_ERROR(text, DeclarationError, "Identifier already declared.");
}
BOOST_AUTO_TEST_CASE(enum_name_resolution_under_current_contract_name)
@@ -2003,7 +1974,7 @@ BOOST_AUTO_TEST_CASE(private_visibility)
function g() { f(); }
}
)";
- CHECK_ERROR(sourceCode, DeclarationError, "");
+ CHECK_ERROR(sourceCode, DeclarationError, "Undeclared identifier.");
}
BOOST_AUTO_TEST_CASE(private_visibility_via_explicit_base_access)
@@ -2016,7 +1987,7 @@ BOOST_AUTO_TEST_CASE(private_visibility_via_explicit_base_access)
function g() { base.f(); }
}
)";
- CHECK_ERROR(sourceCode, TypeError, "");
+ CHECK_ERROR(sourceCode, TypeError, "Member \"f\" not found or not visible after argument-dependent lookup in type(contract base)");
}
BOOST_AUTO_TEST_CASE(external_visibility)
@@ -2027,7 +1998,7 @@ BOOST_AUTO_TEST_CASE(external_visibility)
function g() { f(); }
}
)";
- CHECK_ERROR(sourceCode, DeclarationError, "");
+ CHECK_ERROR(sourceCode, DeclarationError, "Undeclared identifier.");
}
BOOST_AUTO_TEST_CASE(external_base_visibility)
@@ -2040,7 +2011,7 @@ BOOST_AUTO_TEST_CASE(external_base_visibility)
function g() { base.f(); }
}
)";
- CHECK_ERROR(sourceCode, TypeError, "");
+ CHECK_ERROR(sourceCode, TypeError, "Member \"f\" not found or not visible after argument-dependent lookup in type(contract base)");
}
BOOST_AUTO_TEST_CASE(external_argument_assign)
@@ -2050,7 +2021,7 @@ BOOST_AUTO_TEST_CASE(external_argument_assign)
function f(uint a) external { a = 1; }
}
)";
- CHECK_ERROR(sourceCode, TypeError, "");
+ CHECK_ERROR(sourceCode, TypeError, "Expression has to be an lvalue.");
}
BOOST_AUTO_TEST_CASE(external_argument_increment)
@@ -2060,7 +2031,7 @@ BOOST_AUTO_TEST_CASE(external_argument_increment)
function f(uint a) external { a++; }
}
)";
- CHECK_ERROR(sourceCode, TypeError, "");
+ CHECK_ERROR(sourceCode, TypeError, "Expression has to be an lvalue.");
}
BOOST_AUTO_TEST_CASE(external_argument_delete)
@@ -2070,7 +2041,7 @@ BOOST_AUTO_TEST_CASE(external_argument_delete)
function f(uint a) external { delete a; }
}
)";
- CHECK_ERROR(sourceCode, TypeError, "");
+ CHECK_ERROR(sourceCode, TypeError, "Expression has to be an lvalue.");
}
BOOST_AUTO_TEST_CASE(test_for_bug_override_function_with_bytearray_type)
@@ -2093,7 +2064,7 @@ BOOST_AUTO_TEST_CASE(array_with_nonconstant_length)
function f(uint a) { uint8[a] x; }
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal.");
}
BOOST_AUTO_TEST_CASE(array_with_negative_length)
@@ -2115,7 +2086,7 @@ BOOST_AUTO_TEST_CASE(array_copy_with_different_types1)
function f() { b = a; }
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Type bytes storage ref is not implicitly convertible to expected type uint256[] storage ref.");
}
BOOST_AUTO_TEST_CASE(array_copy_with_different_types2)
@@ -2127,7 +2098,7 @@ BOOST_AUTO_TEST_CASE(array_copy_with_different_types2)
function f() { b = a; }
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Type uint32[] storage ref is not implicitly convertible to expected type uint8[] storage ref.");
}
BOOST_AUTO_TEST_CASE(array_copy_with_different_types_conversion_possible)
@@ -2163,7 +2134,7 @@ BOOST_AUTO_TEST_CASE(array_copy_with_different_types_dynamic_static)
function f() { b = a; }
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Type uint256[] storage ref is not implicitly convertible to expected type uint256[80] storage ref.");
}
BOOST_AUTO_TEST_CASE(storage_variable_initialization_with_incorrect_type_int)
@@ -2173,7 +2144,7 @@ BOOST_AUTO_TEST_CASE(storage_variable_initialization_with_incorrect_type_int)
uint8 a = 1000;
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Type int_const 1000 is not implicitly convertible to expected type uint8.");
}
BOOST_AUTO_TEST_CASE(storage_variable_initialization_with_incorrect_type_string)
@@ -2183,7 +2154,7 @@ BOOST_AUTO_TEST_CASE(storage_variable_initialization_with_incorrect_type_string)
uint a = "abc";
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Type literal_string \"abc\" is not implicitly convertible to expected type uint256.");
}
BOOST_AUTO_TEST_CASE(test_fromElementaryTypeName)
@@ -2344,7 +2315,7 @@ BOOST_AUTO_TEST_CASE(assigning_value_to_const_variable)
uint constant x = 56;
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Cannot assign to a constant variable.");
}
BOOST_AUTO_TEST_CASE(assigning_state_to_const_variable)
@@ -2454,7 +2425,7 @@ BOOST_AUTO_TEST_CASE(uninitialized_const_variable)
uint constant y;
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Uninitialized \"constant\" variable.");
}
BOOST_AUTO_TEST_CASE(overloaded_function_cannot_resolve)
@@ -2466,7 +2437,7 @@ BOOST_AUTO_TEST_CASE(overloaded_function_cannot_resolve)
function g() returns(uint) { return f(3, 5); }
}
)";
- CHECK_ERROR(sourceCode, TypeError, "");
+ CHECK_ERROR(sourceCode, TypeError, "No matching declaration found after argument-dependent lookup.");
}
BOOST_AUTO_TEST_CASE(ambiguous_overloaded_function)
@@ -2479,7 +2450,7 @@ BOOST_AUTO_TEST_CASE(ambiguous_overloaded_function)
function g() returns(uint) { return f(1); }
}
)";
- CHECK_ERROR(sourceCode, TypeError, "");
+ CHECK_ERROR(sourceCode, TypeError, "No unique declaration found after argument-dependent lookup.");
}
BOOST_AUTO_TEST_CASE(assignment_of_nonoverloaded_function)
@@ -2502,7 +2473,7 @@ BOOST_AUTO_TEST_CASE(assignment_of_overloaded_function)
function g() returns(uint) { var x = f; return x(7); }
}
)";
- CHECK_ERROR(sourceCode, TypeError, "");
+ CHECK_ERROR(sourceCode, TypeError, "No matching declaration found after variable lookup.");
}
BOOST_AUTO_TEST_CASE(external_types_clash)
@@ -2516,7 +2487,7 @@ BOOST_AUTO_TEST_CASE(external_types_clash)
function f(uint8 a) { }
}
)";
- CHECK_ERROR(sourceCode, TypeError, "");
+ CHECK_ERROR(sourceCode, TypeError, "Function overload clash during conversion to external types for arguments.");
}
BOOST_AUTO_TEST_CASE(override_changes_return_types)
@@ -2529,7 +2500,7 @@ BOOST_AUTO_TEST_CASE(override_changes_return_types)
function f(uint a) returns (uint8) { }
}
)";
- CHECK_ERROR(sourceCode, TypeError, "");
+ CHECK_ERROR(sourceCode, TypeError, "Overriding function return types differ");
}
BOOST_AUTO_TEST_CASE(multiple_constructors)
@@ -2540,7 +2511,7 @@ BOOST_AUTO_TEST_CASE(multiple_constructors)
function test() {}
}
)";
- CHECK_ERROR(sourceCode, DeclarationError, "");
+ CHECK_ERROR(sourceCode, DeclarationError, "More than one constructor defined");
}
BOOST_AUTO_TEST_CASE(equal_overload)
@@ -2551,7 +2522,7 @@ BOOST_AUTO_TEST_CASE(equal_overload)
function test(uint a) external {}
}
)";
- CHECK_ERROR_ALLOW_MULTI(sourceCode, DeclarationError, "");
+ CHECK_ERROR_ALLOW_MULTI(sourceCode, DeclarationError, "Function with same name and arguments defined twice.");
}
BOOST_AUTO_TEST_CASE(uninitialized_var)
@@ -2561,7 +2532,7 @@ BOOST_AUTO_TEST_CASE(uninitialized_var)
function f() returns (uint) { var x; return 2; }
}
)";
- CHECK_ERROR(sourceCode, TypeError, "");
+ CHECK_ERROR(sourceCode, TypeError, "Assignment necessary for type detection.");
}
BOOST_AUTO_TEST_CASE(string)
@@ -2613,7 +2584,7 @@ BOOST_AUTO_TEST_CASE(string_index)
function f() { var a = s[2]; }
}
)";
- CHECK_ERROR(sourceCode, TypeError, "");
+ CHECK_ERROR(sourceCode, TypeError, "Index access for string is not possible.");
}
BOOST_AUTO_TEST_CASE(string_length)
@@ -2624,7 +2595,7 @@ BOOST_AUTO_TEST_CASE(string_length)
function f() { var a = s.length; }
}
)";
- CHECK_ERROR(sourceCode, TypeError, "");
+ CHECK_ERROR(sourceCode, TypeError, "Member \"length\" not found or not visible after argument-dependent lookup in string storage ref");
}
BOOST_AUTO_TEST_CASE(negative_integers_to_signed_out_of_bound)
@@ -2634,7 +2605,7 @@ BOOST_AUTO_TEST_CASE(negative_integers_to_signed_out_of_bound)
int8 public i = -129;
}
)";
- CHECK_ERROR(sourceCode, TypeError, "");
+ CHECK_ERROR(sourceCode, TypeError, "Type int_const -129 is not implicitly convertible to expected type int8.");
}
BOOST_AUTO_TEST_CASE(negative_integers_to_signed_min)
@@ -2654,7 +2625,7 @@ BOOST_AUTO_TEST_CASE(positive_integers_to_signed_out_of_bound)
int8 public j = 128;
}
)";
- CHECK_ERROR(sourceCode, TypeError, "");
+ CHECK_ERROR(sourceCode, TypeError, "Type int_const 128 is not implicitly convertible to expected type int8.");
}
BOOST_AUTO_TEST_CASE(positive_integers_to_signed_out_of_bound_max)
@@ -2674,7 +2645,7 @@ BOOST_AUTO_TEST_CASE(negative_integers_to_unsigned)
uint8 public x = -1;
}
)";
- CHECK_ERROR(sourceCode, TypeError, "");
+ CHECK_ERROR(sourceCode, TypeError, "Type int_const -1 is not implicitly convertible to expected type uint8.");
}
BOOST_AUTO_TEST_CASE(positive_integers_to_unsigned_out_of_bound)
@@ -2684,7 +2655,7 @@ BOOST_AUTO_TEST_CASE(positive_integers_to_unsigned_out_of_bound)
uint8 public x = 700;
}
)";
- CHECK_ERROR(sourceCode, TypeError, "");
+ CHECK_ERROR(sourceCode, TypeError, "Type int_const 700 is not implicitly convertible to expected type uint8.");
}
BOOST_AUTO_TEST_CASE(integer_boolean_operators)
@@ -2692,15 +2663,15 @@ BOOST_AUTO_TEST_CASE(integer_boolean_operators)
char const* sourceCode1 = R"(
contract test { function() { uint x = 1; uint y = 2; x || y; } }
)";
- CHECK_ERROR(sourceCode1, TypeError, "");
+ CHECK_ERROR(sourceCode1, TypeError, "Operator || not compatible with types uint256 and uint256");
char const* sourceCode2 = R"(
contract test { function() { uint x = 1; uint y = 2; x && y; } }
)";
- CHECK_ERROR(sourceCode2, TypeError, "");
+ CHECK_ERROR(sourceCode2, TypeError, "Operator && not compatible with types uint256 and uint256");
char const* sourceCode3 = R"(
contract test { function() { uint x = 1; !x; } }
)";
- CHECK_ERROR(sourceCode3, TypeError, "");
+ CHECK_ERROR(sourceCode3, TypeError, "Unary operator ! cannot be applied to type uint256");
}
BOOST_AUTO_TEST_CASE(exp_signed_variable)
@@ -2708,15 +2679,15 @@ BOOST_AUTO_TEST_CASE(exp_signed_variable)
char const* sourceCode1 = R"(
contract test { function() { uint x = 3; int y = -4; x ** y; } }
)";
- CHECK_ERROR(sourceCode1, TypeError, "");
+ CHECK_ERROR(sourceCode1, TypeError, "Operator ** not compatible with types uint256 and int256");
char const* sourceCode2 = R"(
contract test { function() { uint x = 3; int y = -4; y ** x; } }
)";
- CHECK_ERROR(sourceCode2, TypeError, "");
+ CHECK_ERROR(sourceCode2, TypeError, "Operator ** not compatible with types int256 and uint256");
char const* sourceCode3 = R"(
contract test { function() { int x = -3; int y = -4; x ** y; } }
)";
- CHECK_ERROR(sourceCode3, TypeError, "");
+ CHECK_ERROR(sourceCode3, TypeError, "Operator ** not compatible with types int256 and int256");
}
BOOST_AUTO_TEST_CASE(reference_compare_operators)
@@ -2724,11 +2695,11 @@ BOOST_AUTO_TEST_CASE(reference_compare_operators)
char const* sourceCode1 = R"(
contract test { bytes a; bytes b; function() { a == b; } }
)";
- CHECK_ERROR(sourceCode1, TypeError, "");
+ CHECK_ERROR(sourceCode1, TypeError, "Operator == not compatible with types bytes storage ref and bytes storage ref");
char const* sourceCode2 = R"(
contract test { struct s {uint a;} s x; s y; function() { x == y; } }
)";
- CHECK_ERROR(sourceCode2, TypeError, "");
+ CHECK_ERROR(sourceCode2, TypeError, "Operator == not compatible with types struct test.s storage ref and struct test.s storage ref");
}
BOOST_AUTO_TEST_CASE(overwrite_memory_location_external)
@@ -2738,7 +2709,7 @@ BOOST_AUTO_TEST_CASE(overwrite_memory_location_external)
function f(uint[] memory a) external {}
}
)";
- CHECK_ERROR(sourceCode, TypeError, "");
+ CHECK_ERROR(sourceCode, TypeError, "Location has to be calldata for external functions (remove the \"memory\" or \"storage\" keyword).");
}
BOOST_AUTO_TEST_CASE(overwrite_storage_location_external)
@@ -2748,7 +2719,7 @@ BOOST_AUTO_TEST_CASE(overwrite_storage_location_external)
function f(uint[] storage a) external {}
}
)";
- CHECK_ERROR(sourceCode, TypeError, "");
+ CHECK_ERROR(sourceCode, TypeError, "Location has to be calldata for external functions (remove the \"memory\" or \"storage\" keyword).");
}
BOOST_AUTO_TEST_CASE(storage_location_local_variables)
@@ -2775,7 +2746,7 @@ BOOST_AUTO_TEST_CASE(no_mappings_in_memory_array)
}
}
)";
- CHECK_ERROR(sourceCode, TypeError, "");
+ CHECK_ERROR(sourceCode, TypeError, "Type mapping(uint256 => uint256)[] memory is only valid in storage.");
}
BOOST_AUTO_TEST_CASE(assignment_mem_to_local_storage_variable)
@@ -2789,7 +2760,7 @@ BOOST_AUTO_TEST_CASE(assignment_mem_to_local_storage_variable)
}
}
)";
- CHECK_ERROR(sourceCode, TypeError, "");
+ CHECK_ERROR(sourceCode, TypeError, "Type uint256[] memory is not implicitly convertible to expected type uint256[] storage pointer.");
}
BOOST_AUTO_TEST_CASE(storage_assign_to_different_local_variable)
@@ -2806,7 +2777,7 @@ BOOST_AUTO_TEST_CASE(storage_assign_to_different_local_variable)
}
}
)";
- CHECK_ERROR(sourceCode, TypeError, "");
+ CHECK_ERROR(sourceCode, TypeError, "Type uint8[] storage pointer is not implicitly convertible to expected type uint256[] storage pointer.");
}
BOOST_AUTO_TEST_CASE(uninitialized_mapping_variable)
@@ -2846,7 +2817,7 @@ BOOST_AUTO_TEST_CASE(no_delete_on_storage_pointers)
}
}
)";
- CHECK_ERROR(sourceCode, TypeError, "");
+ CHECK_ERROR(sourceCode, TypeError, "Unary operator delete cannot be applied to type uint256[] storage pointer");
}
BOOST_AUTO_TEST_CASE(assignment_mem_storage_variable_directly)
@@ -2873,7 +2844,7 @@ BOOST_AUTO_TEST_CASE(function_argument_mem_to_storage)
}
}
)";
- CHECK_ERROR(sourceCode, TypeError, "");
+ CHECK_ERROR(sourceCode, TypeError, "Invalid type for argument in function call. Invalid implicit conversion from uint256[] memory to uint256[] storage pointer requested.");
}
BOOST_AUTO_TEST_CASE(function_argument_storage_to_mem)
@@ -2902,7 +2873,7 @@ BOOST_AUTO_TEST_CASE(mem_array_assignment_changes_base_type)
}
}
)";
- CHECK_ERROR(sourceCode, TypeError, "");
+ CHECK_ERROR(sourceCode, TypeError, "Type uint8[] memory is not implicitly convertible to expected type uint256[] memory.");
}
BOOST_AUTO_TEST_CASE(dynamic_return_types_not_possible)
@@ -2917,7 +2888,7 @@ BOOST_AUTO_TEST_CASE(dynamic_return_types_not_possible)
}
}
)";
- CHECK_ERROR(sourceCode, TypeError, "");
+ CHECK_ERROR(sourceCode, TypeError, "Explicit type conversion not allowed from \"inaccessible dynamic type\" to \"bytes storage pointer\".");
}
BOOST_AUTO_TEST_CASE(memory_arrays_not_resizeable)
@@ -2930,7 +2901,7 @@ BOOST_AUTO_TEST_CASE(memory_arrays_not_resizeable)
}
}
)";
- CHECK_ERROR(sourceCode, TypeError, "");
+ CHECK_ERROR(sourceCode, TypeError, "Expression has to be an lvalue.");
}
BOOST_AUTO_TEST_CASE(struct_constructor)
@@ -3000,7 +2971,7 @@ BOOST_AUTO_TEST_CASE(memory_structs_with_mappings)
}
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Member \"b\" is not available in struct Test.S memory outside of storage.");
}
BOOST_AUTO_TEST_CASE(string_bytes_conversion)
@@ -3026,7 +2997,7 @@ BOOST_AUTO_TEST_CASE(inheriting_from_library)
library Lib {}
contract Test is Lib {}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Libraries cannot be inherited from.");
}
BOOST_AUTO_TEST_CASE(inheriting_library)
@@ -3035,7 +3006,7 @@ BOOST_AUTO_TEST_CASE(inheriting_library)
contract Test {}
library Lib is Test {}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Library is not allowed to inherit.");
}
BOOST_AUTO_TEST_CASE(library_having_variables)
@@ -3043,7 +3014,7 @@ BOOST_AUTO_TEST_CASE(library_having_variables)
char const* text = R"(
library Lib { uint x; }
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Library cannot have non-constant state variables");
}
BOOST_AUTO_TEST_CASE(valid_library)
@@ -3076,7 +3047,7 @@ BOOST_AUTO_TEST_CASE(creating_contract_within_the_contract)
function f() { var x = new Test(); }
}
)";
- CHECK_ERROR(sourceCode, TypeError, "");
+ CHECK_ERROR(sourceCode, TypeError, "Circular reference for contract creation (cannot create instance of derived or same contract).");
}
BOOST_AUTO_TEST_CASE(array_out_of_bound_access)
@@ -3090,7 +3061,7 @@ BOOST_AUTO_TEST_CASE(array_out_of_bound_access)
}
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Out of bounds array access.");
}
BOOST_AUTO_TEST_CASE(literal_string_to_storage_pointer)
@@ -3100,7 +3071,7 @@ BOOST_AUTO_TEST_CASE(literal_string_to_storage_pointer)
function f() { string x = "abc"; }
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Type literal_string \"abc\" is not implicitly convertible to expected type string storage pointer.");
}
BOOST_AUTO_TEST_CASE(non_initialized_references)
@@ -3130,7 +3101,7 @@ BOOST_AUTO_TEST_CASE(keccak256_with_large_integer_constant)
function f() { keccak256(2**500); }
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Invalid rational number (too large or division by zero).");
}
BOOST_AUTO_TEST_CASE(cyclic_binary_dependency)
@@ -3140,7 +3111,7 @@ BOOST_AUTO_TEST_CASE(cyclic_binary_dependency)
contract B { function f() { new C(); } }
contract C { function f() { new A(); } }
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Circular reference for contract creation (cannot create instance of derived or same contract).");
}
BOOST_AUTO_TEST_CASE(cyclic_binary_dependency_via_inheritance)
@@ -3150,7 +3121,7 @@ BOOST_AUTO_TEST_CASE(cyclic_binary_dependency_via_inheritance)
contract B { function f() { new C(); } }
contract C { function f() { new A(); } }
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Definition of base has to precede definition of derived contract");
}
BOOST_AUTO_TEST_CASE(multi_variable_declaration_fail)
@@ -3190,7 +3161,7 @@ BOOST_AUTO_TEST_CASE(multi_variable_declaration_wildcards_fail_1)
function f() { var (a, b, ) = one(); }
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Not enough components (1) in value to assign all variables (2).");
}
BOOST_AUTO_TEST_CASE(multi_variable_declaration_wildcards_fail_2)
{
@@ -3200,7 +3171,7 @@ BOOST_AUTO_TEST_CASE(multi_variable_declaration_wildcards_fail_2)
function f() { var (a, , ) = one(); }
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Not enough components (1) in value to assign all variables (2).");
}
BOOST_AUTO_TEST_CASE(multi_variable_declaration_wildcards_fail_3)
@@ -3211,7 +3182,7 @@ BOOST_AUTO_TEST_CASE(multi_variable_declaration_wildcards_fail_3)
function f() { var (, , a) = one(); }
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Not enough components (1) in value to assign all variables (2).");
}
BOOST_AUTO_TEST_CASE(multi_variable_declaration_wildcards_fail_4)
@@ -3222,7 +3193,7 @@ BOOST_AUTO_TEST_CASE(multi_variable_declaration_wildcards_fail_4)
function f() { var (, a, b) = one(); }
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Not enough components (1) in value to assign all variables (2).");
}
BOOST_AUTO_TEST_CASE(tuples)
@@ -3250,7 +3221,7 @@ BOOST_AUTO_TEST_CASE(tuples_empty_components)
}
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Tuple component cannot be empty.");
}
BOOST_AUTO_TEST_CASE(multi_variable_declaration_wildcards_fail_5)
@@ -3261,7 +3232,7 @@ BOOST_AUTO_TEST_CASE(multi_variable_declaration_wildcards_fail_5)
function f() { var (,) = one(); }
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Wildcard both at beginning and end of variable declaration list is only allowed if the number of components is equal.");
}
BOOST_AUTO_TEST_CASE(multi_variable_declaration_wildcards_fail_6)
@@ -3272,7 +3243,7 @@ BOOST_AUTO_TEST_CASE(multi_variable_declaration_wildcards_fail_6)
function f() { var (a, b, c) = two(); }
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Not enough components (2) in value to assign all variables (3)");
}
BOOST_AUTO_TEST_CASE(tuple_assignment_from_void_function)
@@ -3340,7 +3311,7 @@ BOOST_AUTO_TEST_CASE(using_for_not_library)
using D for uint;
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Library name expected.");
}
BOOST_AUTO_TEST_CASE(using_for_function_exists)
@@ -3431,7 +3402,7 @@ BOOST_AUTO_TEST_CASE(using_for_mismatch)
}
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Member \"double\" not found or not visible after argument-dependent lookup in uint256");
}
BOOST_AUTO_TEST_CASE(using_for_not_used)
@@ -3447,7 +3418,7 @@ BOOST_AUTO_TEST_CASE(using_for_not_used)
}
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Member \"double\" not found or not visible after argument-dependent lookup in uint16");
}
BOOST_AUTO_TEST_CASE(library_memory_struct)
@@ -3458,7 +3429,7 @@ BOOST_AUTO_TEST_CASE(library_memory_struct)
function f() returns (S ) {}
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Internal type is not allowed for public or external functions.");
}
BOOST_AUTO_TEST_CASE(using_for_arbitrary_mismatch)
@@ -3473,7 +3444,7 @@ BOOST_AUTO_TEST_CASE(using_for_arbitrary_mismatch)
}
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Member \"double\" not found or not visible after argument-dependent lookup in uint256");
}
BOOST_AUTO_TEST_CASE(bound_function_in_var)
@@ -3520,7 +3491,7 @@ BOOST_AUTO_TEST_CASE(mapping_in_memory_array)
}
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Type cannot live outside storage.");
}
BOOST_AUTO_TEST_CASE(new_for_non_array)
@@ -3532,7 +3503,7 @@ BOOST_AUTO_TEST_CASE(new_for_non_array)
}
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Contract or array type expected.");
}
BOOST_AUTO_TEST_CASE(invalid_args_creating_memory_array)
@@ -3544,7 +3515,7 @@ BOOST_AUTO_TEST_CASE(invalid_args_creating_memory_array)
}
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Wrong argument count for function call: 0 arguments given but expected 1.");
}
BOOST_AUTO_TEST_CASE(function_overload_array_type)
@@ -3664,7 +3635,7 @@ BOOST_AUTO_TEST_CASE(invalid_types_in_inline_array)
}
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Unable to deduce common type for array elements.");
}
BOOST_AUTO_TEST_CASE(dynamic_inline_array)
@@ -3689,7 +3660,7 @@ BOOST_AUTO_TEST_CASE(lvalues_as_inline_array)
}
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Inline array type cannot be declared as LValue.");
}
BOOST_AUTO_TEST_CASE(break_not_in_loop)
@@ -3702,7 +3673,7 @@ BOOST_AUTO_TEST_CASE(break_not_in_loop)
}
}
)";
- CHECK_ERROR(text, SyntaxError, "");
+ CHECK_ERROR(text, SyntaxError, "\"break\" has to be in a \"for\" or \"while\" loop.");
}
BOOST_AUTO_TEST_CASE(continue_not_in_loop)
@@ -3715,7 +3686,7 @@ BOOST_AUTO_TEST_CASE(continue_not_in_loop)
}
}
)";
- CHECK_ERROR(text, SyntaxError, "");
+ CHECK_ERROR(text, SyntaxError, "\"continue\" has to be in a \"for\" or \"while\" loop.");
}
BOOST_AUTO_TEST_CASE(continue_not_in_loop_2)
@@ -3730,7 +3701,7 @@ BOOST_AUTO_TEST_CASE(continue_not_in_loop_2)
}
}
)";
- CHECK_ERROR(text, SyntaxError, "");
+ CHECK_ERROR(text, SyntaxError, "\"continue\" has to be in a \"for\" or \"while\" loop.");
}
BOOST_AUTO_TEST_CASE(invalid_different_types_for_conditional_expression)
@@ -3742,7 +3713,7 @@ BOOST_AUTO_TEST_CASE(invalid_different_types_for_conditional_expression)
}
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "True expression's type bool doesn't match false expression's type uint8.");
}
BOOST_AUTO_TEST_CASE(left_value_in_conditional_expression_not_supported_yet)
@@ -3756,7 +3727,7 @@ BOOST_AUTO_TEST_CASE(left_value_in_conditional_expression_not_supported_yet)
}
}
)";
- CHECK_ERROR_ALLOW_MULTI(text, TypeError, "");
+ CHECK_ERROR_ALLOW_MULTI(text, TypeError, "Conditional expression as left value is not supported yet.");
}
BOOST_AUTO_TEST_CASE(conditional_expression_with_different_struct)
@@ -3776,7 +3747,7 @@ BOOST_AUTO_TEST_CASE(conditional_expression_with_different_struct)
}
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "True expression's type struct C.s1 memory doesn't match false expression's type struct C.s2 memory.");
}
BOOST_AUTO_TEST_CASE(conditional_expression_with_different_function_type)
@@ -3791,7 +3762,7 @@ BOOST_AUTO_TEST_CASE(conditional_expression_with_different_function_type)
}
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "True expression's type function (bool) doesn't match false expression's type function ().");
}
BOOST_AUTO_TEST_CASE(conditional_expression_with_different_enum)
@@ -3809,7 +3780,7 @@ BOOST_AUTO_TEST_CASE(conditional_expression_with_different_enum)
}
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "True expression's type enum C.small doesn't match false expression's type enum C.big.");
}
BOOST_AUTO_TEST_CASE(conditional_expression_with_different_mapping)
@@ -3824,7 +3795,7 @@ BOOST_AUTO_TEST_CASE(conditional_expression_with_different_mapping)
}
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "True expression's type mapping(uint8 => uint8) doesn't match false expression's type mapping(uint32 => uint8).");
}
BOOST_AUTO_TEST_CASE(conditional_with_all_types)
@@ -3931,7 +3902,7 @@ BOOST_AUTO_TEST_CASE(constructor_call_invalid_arg_count)
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Wrong argument count for modifier invocation: 1 arguments given but expected 0.");
}
BOOST_AUTO_TEST_CASE(index_access_for_bytes)
@@ -4721,7 +4692,7 @@ BOOST_AUTO_TEST_CASE(modifier_without_underscore)
modifier m() {}
}
)";
- CHECK_ERROR(text, SyntaxError, "");
+ CHECK_ERROR(text, SyntaxError, "Modifier body does not contain '_'.");
}
BOOST_AUTO_TEST_CASE(payable_in_library)
@@ -4731,7 +4702,7 @@ BOOST_AUTO_TEST_CASE(payable_in_library)
function f() payable {}
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Library functions cannot be payable.");
}
BOOST_AUTO_TEST_CASE(payable_external)
@@ -4751,7 +4722,7 @@ BOOST_AUTO_TEST_CASE(payable_internal)
function f() payable internal {}
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Internal functions cannot be payable.");
}
BOOST_AUTO_TEST_CASE(payable_private)
@@ -4761,7 +4732,7 @@ BOOST_AUTO_TEST_CASE(payable_private)
function f() payable private {}
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Internal functions cannot be payable.");
}
BOOST_AUTO_TEST_CASE(illegal_override_payable)
@@ -4770,7 +4741,7 @@ BOOST_AUTO_TEST_CASE(illegal_override_payable)
contract B { function f() payable {} }
contract C is B { function f() {} }
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Overriding function should be declared payable.");
}
BOOST_AUTO_TEST_CASE(illegal_override_payable_nonpayable)
@@ -4779,7 +4750,7 @@ BOOST_AUTO_TEST_CASE(illegal_override_payable_nonpayable)
contract B { function f() {} }
contract C is B { function f() payable {} }
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Overriding function should not be declared payable.");
}
BOOST_AUTO_TEST_CASE(function_variable_mixin)
@@ -4797,16 +4768,7 @@ BOOST_AUTO_TEST_CASE(function_variable_mixin)
function checkOk() returns (bool) { return ok(); }
}
)";
- CHECK_ERROR(text, DeclarationError, "");
-}
-
-
-BOOST_AUTO_TEST_CASE(payable_constant_conflict)
-{
- char const* text = R"(
- contract C { function f() payable constant {} }
- )";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, DeclarationError, "Identifier already declared.");
}
BOOST_AUTO_TEST_CASE(calling_payable)
@@ -4830,7 +4792,7 @@ BOOST_AUTO_TEST_CASE(calling_nonpayable)
function f() { (new receiver()).nopay.value(10)(); }
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Member \"value\" not found or not visible after argument-dependent lookup in function () external - did you forget the \"payable\" modifier?");
}
BOOST_AUTO_TEST_CASE(non_payable_constructor)
@@ -4846,7 +4808,7 @@ BOOST_AUTO_TEST_CASE(non_payable_constructor)
}
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Member \"value\" not found or not visible after argument-dependent lookup in function () returns (contract C) - did you forget the \"payable\" modifier?");
}
BOOST_AUTO_TEST_CASE(warn_nonpresent_pragma)
@@ -4873,7 +4835,7 @@ BOOST_AUTO_TEST_CASE(constant_constructor)
function test() constant {}
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Constructor cannot be defined as constant.");
}
BOOST_AUTO_TEST_CASE(external_constructor)
@@ -4883,7 +4845,7 @@ BOOST_AUTO_TEST_CASE(external_constructor)
function test() external {}
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Constructor must be public or internal.");
}
BOOST_AUTO_TEST_CASE(invalid_array_as_statement)
@@ -4894,7 +4856,7 @@ BOOST_AUTO_TEST_CASE(invalid_array_as_statement)
function test(uint k) { S[k]; }
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Integer constant expected.");
}
BOOST_AUTO_TEST_CASE(using_directive_for_missing_selftype)
@@ -4913,7 +4875,7 @@ BOOST_AUTO_TEST_CASE(using_directive_for_missing_selftype)
}
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Member \"b\" not found or not visible after argument-dependent lookup in bytes memory");
}
BOOST_AUTO_TEST_CASE(function_type)
@@ -4961,7 +4923,7 @@ BOOST_AUTO_TEST_CASE(private_function_type)
}
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Invalid visibility, can only be \"external\" or \"internal\".");
}
BOOST_AUTO_TEST_CASE(public_function_type)
@@ -4973,7 +4935,7 @@ BOOST_AUTO_TEST_CASE(public_function_type)
}
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Invalid visibility, can only be \"external\" or \"internal\".");
}
BOOST_AUTO_TEST_CASE(payable_internal_function_type)
@@ -4983,7 +4945,7 @@ BOOST_AUTO_TEST_CASE(payable_internal_function_type)
function (uint) internal payable returns (uint) x;
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Only external function types can be payable.");
}
BOOST_AUTO_TEST_CASE(call_value_on_non_payable_function_type)
@@ -4996,7 +4958,7 @@ BOOST_AUTO_TEST_CASE(call_value_on_non_payable_function_type)
}
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Member \"value\" not found or not visible after argument-dependent lookup in function (uint256) external returns (uint256) - did you forget the \"payable\" modifier?");
}
BOOST_AUTO_TEST_CASE(external_function_type_returning_internal)
@@ -5006,7 +4968,7 @@ BOOST_AUTO_TEST_CASE(external_function_type_returning_internal)
function() external returns (function () internal) x;
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Internal type cannot be used for external function type.");
}
BOOST_AUTO_TEST_CASE(external_function_type_taking_internal)
@@ -5016,7 +4978,7 @@ BOOST_AUTO_TEST_CASE(external_function_type_taking_internal)
function(function () internal) external x;
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Internal type cannot be used for external function type.");
}
BOOST_AUTO_TEST_CASE(call_value_on_payable_function_type)
@@ -5042,7 +5004,7 @@ BOOST_AUTO_TEST_CASE(internal_function_as_external_parameter)
}
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Internal type is not allowed for public or external functions.");
}
BOOST_AUTO_TEST_CASE(internal_function_returned_from_public_function)
@@ -5054,7 +5016,7 @@ BOOST_AUTO_TEST_CASE(internal_function_returned_from_public_function)
}
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Internal type is not allowed for public or external functions.");
}
BOOST_AUTO_TEST_CASE(internal_function_as_external_parameter_in_library_internal)
@@ -5076,7 +5038,7 @@ BOOST_AUTO_TEST_CASE(internal_function_as_external_parameter_in_library_external
}
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Internal type is not allowed for public or external functions.");
}
BOOST_AUTO_TEST_CASE(function_type_arrays)
@@ -5127,7 +5089,7 @@ BOOST_AUTO_TEST_CASE(delete_function_type_invalid)
}
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Expression has to be an lvalue.");
}
BOOST_AUTO_TEST_CASE(delete_external_function_type_invalid)
@@ -5139,7 +5101,7 @@ BOOST_AUTO_TEST_CASE(delete_external_function_type_invalid)
}
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Expression has to be an lvalue.");
}
BOOST_AUTO_TEST_CASE(external_function_to_function_type_calldata_parameter)
@@ -5222,7 +5184,7 @@ BOOST_AUTO_TEST_CASE(shift_constant_left_negative_rvalue)
uint public a = 0x42 << -8;
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Operator << not compatible with types int_const 66 and int_const -8");
}
BOOST_AUTO_TEST_CASE(shift_constant_right_negative_rvalue)
@@ -5232,7 +5194,7 @@ BOOST_AUTO_TEST_CASE(shift_constant_right_negative_rvalue)
uint public a = 0x42 >> -8;
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Operator >> not compatible with types int_const 66 and int_const -8");
}
BOOST_AUTO_TEST_CASE(shift_constant_left_excessive_rvalue)
@@ -5242,7 +5204,7 @@ BOOST_AUTO_TEST_CASE(shift_constant_left_excessive_rvalue)
uint public a = 0x42 << 0x100000000;
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Operator << not compatible with types int_const 66 and int_const 4294967296");
}
BOOST_AUTO_TEST_CASE(shift_constant_right_excessive_rvalue)
@@ -5252,7 +5214,7 @@ BOOST_AUTO_TEST_CASE(shift_constant_right_excessive_rvalue)
uint public a = 0x42 >> 0x100000000;
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Operator >> not compatible with types int_const 66 and int_const 4294967296");
}
BOOST_AUTO_TEST_CASE(inline_assembly_unbalanced_positive_stack)
@@ -5447,7 +5409,7 @@ BOOST_AUTO_TEST_CASE(invalid_mobile_type)
}
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Invalid mobile type.");
}
BOOST_AUTO_TEST_CASE(warns_msg_value_in_non_payable_public_function)
@@ -5714,7 +5676,7 @@ BOOST_AUTO_TEST_CASE(interface_constructor)
function I();
}
)";
- CHECK_ERROR(text, TypeError, "Constructor cannot be defined in interfaces");
+ CHECK_ERROR_ALLOW_MULTI(text, TypeError, "Constructor cannot be defined in interfaces");
}
BOOST_AUTO_TEST_CASE(interface_functions)
@@ -6134,6 +6096,25 @@ BOOST_AUTO_TEST_CASE(shadowing_builtins_with_variables)
CHECK_WARNING(text, "shadows a builtin symbol");
}
+BOOST_AUTO_TEST_CASE(shadowing_builtins_with_storage_variables)
+{
+ char const* text = R"(
+ contract C {
+ uint msg;
+ }
+ )";
+ CHECK_WARNING(text, "shadows a builtin symbol");
+}
+
+BOOST_AUTO_TEST_CASE(shadowing_builtin_at_global_scope)
+{
+ char const* text = R"(
+ contract msg {
+ }
+ )";
+ CHECK_WARNING(text, "shadows a builtin symbol");
+}
+
BOOST_AUTO_TEST_CASE(shadowing_builtins_with_parameters)
{
char const* text = R"(
@@ -6190,6 +6171,28 @@ BOOST_AUTO_TEST_CASE(shadowing_builtins_ignores_constructor)
CHECK_SUCCESS_NO_WARNINGS(text);
}
+BOOST_AUTO_TEST_CASE(function_overload_is_not_shadowing)
+{
+ char const* text = R"(
+ contract C {
+ function f() {}
+ function f(uint) {}
+ }
+ )";
+ CHECK_SUCCESS_NO_WARNINGS(text);
+}
+
+BOOST_AUTO_TEST_CASE(function_override_is_not_shadowing)
+{
+ char const* text = R"(
+ contract D { function f() {} }
+ contract C is D {
+ function f(uint) {}
+ }
+ )";
+ CHECK_SUCCESS_NO_WARNINGS(text);
+}
+
BOOST_AUTO_TEST_CASE(callable_crash)
{
char const* text = R"(
@@ -6437,7 +6440,7 @@ BOOST_AUTO_TEST_CASE(using_this_in_constructor)
CHECK_WARNING(text, "\"this\" used in constructor");
}
-BOOST_AUTO_TEST_CASE(do_not_crash_on_not_lalue)
+BOOST_AUTO_TEST_CASE(do_not_crash_on_not_lvalue)
{
// This checks for a bug that caused a crash because of continued analysis.
char const* text = R"(
@@ -6451,6 +6454,198 @@ BOOST_AUTO_TEST_CASE(do_not_crash_on_not_lalue)
CHECK_ERROR_ALLOW_MULTI(text, TypeError, "is not callable");
}
+BOOST_AUTO_TEST_CASE(builtin_reject_gas)
+{
+ char const* text = R"(
+ contract C {
+ function f() {
+ keccak256.gas();
+ }
+ }
+ )";
+ CHECK_ERROR(text, TypeError, "Member \"gas\" not found or not visible after argument-dependent lookup");
+ text = R"(
+ contract C {
+ function f() {
+ sha256.gas();
+ }
+ }
+ )";
+ CHECK_ERROR(text, TypeError, "Member \"gas\" not found or not visible after argument-dependent lookup");
+ text = R"(
+ contract C {
+ function f() {
+ ripemd160.gas();
+ }
+ }
+ )";
+ CHECK_ERROR(text, TypeError, "Member \"gas\" not found or not visible after argument-dependent lookup");
+ text = R"(
+ contract C {
+ function f() {
+ ecrecover.gas();
+ }
+ }
+ )";
+ CHECK_ERROR(text, TypeError, "Member \"gas\" not found or not visible after argument-dependent lookup");
+}
+
+BOOST_AUTO_TEST_CASE(builtin_reject_value)
+{
+ char const* text = R"(
+ contract C {
+ function f() {
+ keccak256.value();
+ }
+ }
+ )";
+ CHECK_ERROR(text, TypeError, "Member \"value\" not found or not visible after argument-dependent lookup");
+ text = R"(
+ contract C {
+ function f() {
+ sha256.value();
+ }
+ }
+ )";
+ CHECK_ERROR(text, TypeError, "Member \"value\" not found or not visible after argument-dependent lookup");
+ text = R"(
+ contract C {
+ function f() {
+ ripemd160.value();
+ }
+ }
+ )";
+ CHECK_ERROR(text, TypeError, "Member \"value\" not found or not visible after argument-dependent lookup");
+ text = R"(
+ contract C {
+ function f() {
+ ecrecover.value();
+ }
+ }
+ )";
+ CHECK_ERROR(text, TypeError, "Member \"value\" not found or not visible after argument-dependent lookup");
+}
+
+BOOST_AUTO_TEST_CASE(constructor_without_implementation)
+{
+ char const* text = R"(
+ contract C {
+ function C();
+ }
+ )";
+ CHECK_ERROR(text, TypeError, "Constructor must be implemented if declared.");
+}
+
+BOOST_AUTO_TEST_CASE(large_storage_array_fine)
+{
+ char const* text = R"(
+ contract C {
+ uint[2**64 - 1] x;
+ }
+ )";
+ CHECK_SUCCESS_NO_WARNINGS(text);
+}
+
+BOOST_AUTO_TEST_CASE(large_storage_array_simple)
+{
+ char const* text = R"(
+ contract C {
+ uint[2**64] x;
+ }
+ )";
+ CHECK_WARNING(text, "covers a large part of storage and thus makes collisions likely");
+}
+
+BOOST_AUTO_TEST_CASE(large_storage_arrays_combined)
+{
+ char const* text = R"(
+ contract C {
+ uint[200][200][2**30][][2**30] x;
+ }
+ )";
+ CHECK_WARNING(text, "covers a large part of storage and thus makes collisions likely");
+}
+
+BOOST_AUTO_TEST_CASE(large_storage_arrays_struct)
+{
+ char const* text = R"(
+ contract C {
+ struct S { uint[2**30] x; uint[2**50] y; }
+ S[2**20] x;
+ }
+ )";
+ CHECK_WARNING(text, "covers a large part of storage and thus makes collisions likely");
+}
+
+BOOST_AUTO_TEST_CASE(large_storage_array_mapping)
+{
+ char const* text = R"(
+ contract C {
+ mapping(uint => uint[2**100]) x;
+ }
+ )";
+ CHECK_WARNING(text, "covers a large part of storage and thus makes collisions likely");
+}
+
+BOOST_AUTO_TEST_CASE(library_function_without_implementation)
+{
+ char const* text = R"(
+ library L {
+ function f();
+ }
+ )";
+ CHECK_SUCCESS_NO_WARNINGS(text);
+ text = R"(
+ library L {
+ function f() internal;
+ }
+ )";
+ CHECK_ERROR(text, TypeError, "Internal library function must be implemented if declared.");
+ text = R"(
+ library L {
+ function f() private;
+ }
+ )";
+ CHECK_ERROR(text, TypeError, "Internal library function must be implemented if declared.");
+}
+
+BOOST_AUTO_TEST_CASE(experimental_pragma)
+{
+ char const* text = R"(
+ pragma experimental;
+ )";
+ CHECK_ERROR(text, SyntaxError, "Experimental feature name is missing.");
+ text = R"(
+ pragma experimental 123;
+ )";
+ CHECK_ERROR(text, SyntaxError, "Unsupported experimental feature name.");
+ text = R"(
+ pragma experimental unsupportedName;
+ )";
+ CHECK_ERROR(text, SyntaxError, "Unsupported experimental feature name.");
+ text = R"(
+ pragma experimental "unsupportedName";
+ )";
+ CHECK_ERROR(text, SyntaxError, "Unsupported experimental feature name.");
+ text = R"(
+ pragma experimental "";
+ )";
+ CHECK_ERROR(text, SyntaxError, "Empty experimental feature name is invalid.");
+ text = R"(
+ pragma experimental unsupportedName unsupportedName;
+ )";
+ CHECK_ERROR(text, SyntaxError, "Stray arguments.");
+ text = R"(
+ pragma experimental __test;
+ )";
+ CHECK_WARNING(text, "Experimental features are turned on. Do not use experimental features on live deployments.");
+// text = R"(
+// pragma experimental __test;
+// pragma experimental __test;
+// )";
+// CHECK_ERROR_ALLOW_MULTI(text, SyntaxError, "Duplicate experimental feature name.");
+}
+
BOOST_AUTO_TEST_SUITE_END()
}
diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp
index 78edd4d1..30dc80d9 100644
--- a/test/libsolidity/SolidityParser.cpp
+++ b/test/libsolidity/SolidityParser.cpp
@@ -898,25 +898,31 @@ BOOST_AUTO_TEST_CASE(multiple_visibility_specifiers)
contract c {
uint private internal a;
})";
- CHECK_PARSE_ERROR(text, "Visibility already specified");
+ CHECK_PARSE_ERROR(text, "Visibility already specified as \"private\".");
+ text = R"(
+ contract c {
+ function f() private external {}
+ })";
+ CHECK_PARSE_ERROR(text, "Visibility already specified as \"private\".");
}
-BOOST_AUTO_TEST_CASE(multiple_payable_specifiers)
+BOOST_AUTO_TEST_CASE(multiple_statemutability_specifiers)
{
char const* text = R"(
contract c {
function f() payable payable {}
})";
- CHECK_PARSE_ERROR(text, "Multiple \"payable\" specifiers.");
-}
-
-BOOST_AUTO_TEST_CASE(multiple_constant_specifiers)
-{
- char const* text = R"(
+ CHECK_PARSE_ERROR(text, "State mutability already specified as \"payable\".");
+ text = R"(
contract c {
function f() constant constant {}
})";
- CHECK_PARSE_ERROR(text, "Multiple \"constant\" specifiers.");
+ CHECK_PARSE_ERROR(text, "State mutability already specified as \"view\".");
+ text = R"(
+ contract c {
+ function f() payable constant {}
+ })";
+ CHECK_PARSE_ERROR(text, "State mutability already specified as \"payable\".");
}
BOOST_AUTO_TEST_CASE(literal_constants_with_ether_subdenominations)
@@ -1182,6 +1188,18 @@ BOOST_AUTO_TEST_CASE(tuples)
BOOST_CHECK(successParse(text));
}
+BOOST_AUTO_TEST_CASE(tuples_without_commas)
+{
+ char const* text = R"(
+ contract C {
+ function f() {
+ var a = (2 2);
+ }
+ }
+ )";
+ CHECK_PARSE_ERROR(text, "Expected token Comma");
+}
+
BOOST_AUTO_TEST_CASE(member_access_parser_ambiguity)
{
char const* text = R"(
@@ -1345,6 +1363,42 @@ BOOST_AUTO_TEST_CASE(conditional_with_assignment)
BOOST_CHECK(successParse(text));
}
+BOOST_AUTO_TEST_CASE(recursion_depth1)
+{
+ string text("contract C { bytes");
+ for (size_t i = 0; i < 30000; i++)
+ text += "[";
+ CHECK_PARSE_ERROR(text.c_str(), "Maximum recursion depth reached during parsing");
+}
+
+BOOST_AUTO_TEST_CASE(recursion_depth2)
+{
+ string text("contract C { function f() {");
+ for (size_t i = 0; i < 30000; i++)
+ text += "{";
+ CHECK_PARSE_ERROR(text, "Maximum recursion depth reached during parsing");
+}
+
+BOOST_AUTO_TEST_CASE(recursion_depth3)
+{
+ string text("contract C { function f() { uint x = f(");
+ for (size_t i = 0; i < 30000; i++)
+ text += "(";
+ CHECK_PARSE_ERROR(text, "Maximum recursion depth reached during parsing");
+}
+
+BOOST_AUTO_TEST_CASE(recursion_depth4)
+{
+ string text("contract C { function f() { uint a;");
+ for (size_t i = 0; i < 30000; i++)
+ text += "(";
+ text += "a";
+ for (size_t i = 0; i < 30000; i++)
+ text += "++)";
+ text += "}}";
+ CHECK_PARSE_ERROR(text, "Maximum recursion depth reached during parsing");
+}
+
BOOST_AUTO_TEST_CASE(declaring_fixed_and_ufixed_variables)
{
char const* text = R"(