aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt9
-rw-r--r--Changelog.md11
-rw-r--r--appveyor.yml3
-rw-r--r--cmake/templates/license.h.in5
-rw-r--r--docs/abi-spec.rst345
-rw-r--r--docs/assembly.rst17
-rw-r--r--docs/contracts.rst5
-rw-r--r--docs/contributing.rst2
-rw-r--r--docs/grammar.txt9
-rw-r--r--docs/index.rst4
-rw-r--r--docs/layout-of-source-files.rst2
-rw-r--r--docs/types.rst5
-rw-r--r--libdevcore/UTF8.cpp2
-rw-r--r--libevmasm/Assembly.cpp19
-rw-r--r--libevmasm/Assembly.h11
-rw-r--r--libevmasm/AssemblyItem.h8
-rw-r--r--libevmasm/ConstantOptimiser.cpp5
-rw-r--r--libevmasm/GasMeter.cpp9
-rw-r--r--libevmasm/Instruction.cpp10
-rw-r--r--libevmasm/Instruction.h4
-rw-r--r--libevmasm/PeepholeOptimiser.cpp23
-rw-r--r--libevmasm/SemanticInformation.cpp7
-rw-r--r--libjulia/backends/evm/EVMCodeTransform.cpp55
-rw-r--r--libjulia/backends/evm/EVMCodeTransform.h31
-rw-r--r--liblll/CodeFragment.cpp11
-rw-r--r--liblll/Compiler.cpp5
-rw-r--r--liblll/CompilerState.cpp6
-rw-r--r--liblll/Parser.cpp2
-rw-r--r--libsolidity/analysis/NameAndTypeResolver.cpp22
-rw-r--r--libsolidity/analysis/NameAndTypeResolver.h3
-rw-r--r--libsolidity/analysis/ReferencesResolver.cpp2
-rw-r--r--libsolidity/analysis/TypeChecker.cpp5
-rw-r--r--libsolidity/ast/ASTJsonConverter.cpp1
-rw-r--r--libsolidity/codegen/CompilerContext.h8
-rw-r--r--libsolidity/inlineasm/AsmAnalysis.cpp30
-rw-r--r--libsolidity/inlineasm/AsmAnalysis.h9
-rw-r--r--libsolidity/inlineasm/AsmParser.cpp13
-rw-r--r--libsolidity/inlineasm/AsmScope.cpp2
-rw-r--r--libsolidity/inlineasm/AsmScope.h19
-rw-r--r--libsolidity/interface/AssemblyStack.cpp16
-rw-r--r--libsolidity/interface/AssemblyStack.h8
-rw-r--r--libsolidity/interface/CompilerStack.cpp8
-rw-r--r--libsolidity/interface/CompilerStack.h3
-rw-r--r--libsolidity/parsing/Parser.cpp48
-rw-r--r--scripts/bytecodecompare/storebytecode.bat6
-rw-r--r--solc/CommandLineInterface.cpp50
-rw-r--r--solc/jsonCompiler.cpp17
-rw-r--r--test/liblll/EndToEndTest.cpp71
-rw-r--r--test/liblll/Parser.cpp8
-rw-r--r--test/libsolidity/ASTJSON.cpp30
-rw-r--r--test/libsolidity/InlineAssembly.cpp42
-rw-r--r--test/libsolidity/JSONCompiler.cpp10
-rw-r--r--test/libsolidity/SolidityNameAndTypeResolution.cpp56
-rw-r--r--test/libsolidity/SolidityOptimizer.cpp26
-rw-r--r--test/libsolidity/SolidityParser.cpp11
-rw-r--r--test/libsolidity/StandardCompiler.cpp7
56 files changed, 986 insertions, 170 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index e53917cb..d6ed6643 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -24,6 +24,15 @@ include(EthExecutableHelper)
# Include utils
include(EthUtils)
+# Create license.h from LICENSE.txt and template
+# Converting to char array is required due to MSVC's string size limit.
+file(READ ${CMAKE_SOURCE_DIR}/LICENSE.txt LICENSE_TEXT HEX)
+string(REGEX MATCHALL ".." LICENSE_TEXT "${LICENSE_TEXT}")
+string(REGEX REPLACE ";" ",\n\t0x" LICENSE_TEXT "${LICENSE_TEXT}")
+set(LICENSE_TEXT "0x${LICENSE_TEXT}")
+
+configure_file("${CMAKE_SOURCE_DIR}/cmake/templates/license.h.in" "license.h")
+
include(EthOptions)
configure_project(TESTS)
diff --git a/Changelog.md b/Changelog.md
index b398e014..de65421a 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -1,14 +1,19 @@
### 0.4.12 (unreleased)
Features:
- * Assembler: renamed ``SHA3`` to `KECCAK256``.
- * AST: export all attributes to Json format
+ * Assembly: renamed ``SHA3`` to ``KECCAK256``.
+ * Assembly: Display auxiliary data in the assembly output.
+ * Assembly: Add ``CREATE2`` (EIP86), ``STATICCALL`` (EIP214), ``RETURNDATASIZE`` and ``RETURNDATACOPY`` (EIP211) instructions.
+ * AST: export all attributes to JSON format.
* Inline Assembly: Present proper error message when not supplying enough arguments to a functional
instruction.
* Inline Assembly: introduce ``keccak256`` as an opcode. ``sha3`` is still a valid alias.
Bugfixes:
- * Unused variable warnings no longer issued for variables used inside inline assembly
+ * Type Checker: Make UTF8-validation a bit more sloppy to include more valid sequences.
+ * Fixed crash concerning non-callable types.
+ * Unused variable warnings no longer issued for variables used inside inline assembly.
+ * Inline Assembly: Enforce function arguments when parsing functional instructions.
### 0.4.11 (2017-05-03)
diff --git a/appveyor.yml b/appveyor.yml
index 22ec30a0..55c80a21 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -65,7 +65,8 @@ build_script:
- msbuild solidity.sln /p:Configuration=%CONFIGURATION% /m:%NUMBER_OF_PROCESSORS% /v:minimal
- cd %APPVEYOR_BUILD_FOLDER%
- scripts\release.bat %CONFIGURATION%
- - scripts\bytecodecompare\storebytecode.bat %CONFIGURATION% %APPVEYOR_REPO_COMMIT%
+ - ps: $bytecodedir = git show -s --format="%cd-%H" --date=short
+ - ps: scripts\bytecodecompare\storebytecode.bat $Env:CONFIGURATION $bytecodedir
test_script:
- cd %APPVEYOR_BUILD_FOLDER%
diff --git a/cmake/templates/license.h.in b/cmake/templates/license.h.in
new file mode 100644
index 00000000..6cdbc38d
--- /dev/null
+++ b/cmake/templates/license.h.in
@@ -0,0 +1,5 @@
+#pragma once
+
+static char const licenseText[] = {
+ @LICENSE_TEXT@, 0
+};
diff --git a/docs/abi-spec.rst b/docs/abi-spec.rst
new file mode 100644
index 00000000..e39c8861
--- /dev/null
+++ b/docs/abi-spec.rst
@@ -0,0 +1,345 @@
+.. index:: abi, application binary interface
+
+.. _ABI:
+
+******************************************
+Application Binary Interface Specification
+******************************************
+
+Basic design
+============
+
+The Application Binary Interface is the standard way to interact with contracts in the Ethereum ecosystem, both
+from outside the blockchain and for contract-to-contract interaction. Data is encoded following its type,
+according to this specification.
+
+We assume the Application Binary Interface (ABI) is strongly typed, known at compilation time and static. No introspection mechanism will be provided. We assert that all contracts will have the interface definitions of any contracts they call available at compile-time.
+
+This specification does not address contracts whose interface is dynamic or otherwise known only at run-time. Should these cases become important they can be adequately handled as facilities built within the Ethereum ecosystem.
+
+Function Selector
+=================
+
+The first four bytes of the call data for a function call specifies the function to be called. It is the
+first (left, high-order in big-endian) four bytes of the Keccak (SHA-3) hash of the signature of the function. The signature is defined as the canonical expression of the basic prototype, i.e.
+the function name with the parenthesised list of parameter types. Parameter types are split by a single comma - no spaces are used.
+
+Argument Encoding
+=================
+
+Starting from the fifth byte, the encoded arguments follow. This encoding is also used in other places, e.g. the return values and also event arguments are encoded in the same way, without the four bytes specifying the function.
+
+Types
+=====
+
+The following elementary types exist:
+
+- `uint<M>`: unsigned integer type of `M` bits, `0 < M <= 256`, `M % 8 == 0`. e.g. `uint32`, `uint8`, `uint256`.
+
+- `int<M>`: two's complement signed integer type of `M` bits, `0 < M <= 256`, `M % 8 == 0`.
+
+- `address`: equivalent to `uint160`, except for the assumed interpretation and language typing.
+
+- `uint`, `int`: synonyms for `uint256`, `int256` respectively (not to be used for computing the function selector).
+
+- `bool`: equivalent to `uint8` restricted to the values 0 and 1
+
+- `fixed<M>x<N>`: signed fixed-point decimal number of `M` bits, `0 < M <= 256`, `M % 8 ==0`, and `0 < N <= 80`, which denotes the value `v` as `v / (10 ** N)`.
+
+- `ufixed<M>x<N>`: unsigned variant of `fixed<M>x<N>`.
+
+- `fixed`, `ufixed`: synonyms for `fixed128x19`, `ufixed128x19` respectively (not to be used for computing the function selector).
+
+- `bytes<M>`: binary type of `M` bytes, `0 < M <= 32`.
+
+- `function`: equivalent to `bytes24`: an address, followed by a function selector
+
+The following (fixed-size) array type exists:
+
+- `<type>[M]`: a fixed-length array of the given fixed-length type.
+
+The following non-fixed-size types exist:
+
+- `bytes`: dynamic sized byte sequence.
+
+- `string`: dynamic sized unicode string assumed to be UTF-8 encoded.
+
+- `<type>[]`: a variable-length array of the given fixed-length type.
+
+Types can be combined to anonymous structs by enclosing a finite non-negative number
+of them inside parentheses, separated by commas:
+
+- `(T1,T2,...,Tn)`: anonymous struct (ordered tuple) consisting of the types `T1`, ..., `Tn`, `n >= 0`
+
+It is possible to form structs of structs, arrays of structs and so on.
+
+
+Formal Specification of the Encoding
+====================================
+
+We will now formally specify the encoding, such that it will have the following
+properties, which are especially useful if some arguments are nested arrays:
+
+Properties:
+
+ 1. The number of reads necessary to access a value is at most the depth of the value inside the argument array structure, i.e. four reads are needed to retrieve `a_i[k][l][r]`. In a previous version of the ABI, the number of reads scaled linearly with the total number of dynamic parameters in the worst case.
+
+ 2. The data of a variable or array element is not interleaved with other data and it is relocatable, i.e. it only uses relative "addresses"
+
+We distinguish static and dynamic types. Static types are encoded in-place and dynamic types are encoded at a separately allocated location after the current block.
+
+**Definition:** The following types are called "dynamic":
+* `bytes`
+* `string`
+* `T[]` for any `T`
+* `T[k]` for any dynamic `T` and any `k > 0`
+
+All other types are called "static".
+
+**Definition:** `len(a)` is the number of bytes in a binary string `a`.
+The type of `len(a)` is assumed to be `uint256`.
+
+We define `enc`, the actual encoding, as a mapping of values of the ABI types to binary strings such
+that `len(enc(X))` depends on the value of `X` if and only if the type of `X` is dynamic.
+
+**Definition:** For any ABI value `X`, we recursively define `enc(X)`, depending
+on the type of `X` being
+
+- `(T1,...,Tk)` for `k >= 0` and any types `T1`, ..., `Tk`
+
+ `enc(X) = head(X(1)) ... head(X(k-1)) tail(X(0)) ... tail(X(k-1))`
+
+ where `X(i)` is the `ith` component of the value, and
+ `head` and `tail` are defined for `Ti` being a static type as
+
+ `head(X(i)) = enc(X(i))` and `tail(X(i)) = ""` (the empty string)
+
+ and as
+
+ `head(X(i)) = enc(len(head(X(0)) ... head(X(k-1)) tail(X(0)) ... tail(X(i-1))))`
+ `tail(X(i)) = enc(X(i))`
+
+ otherwise, i.e. if `Ti` is a dynamic type.
+
+ Note that in the dynamic case, `head(X(i))` is well-defined since the lengths of
+ the head parts only depend on the types and not the values. Its value is the offset
+ of the beginning of `tail(X(i))` relative to the start of `enc(X)`.
+
+- `T[k]` for any `T` and `k`:
+
+ `enc(X) = enc((X[0], ..., X[k-1]))`
+
+ i.e. it is encoded as if it were an anonymous struct with `k` elements
+ of the same type.
+
+- `T[]` where `X` has `k` elements (`k` is assumed to be of type `uint256`):
+
+ `enc(X) = enc(k) enc([X[1], ..., X[k]])`
+
+ i.e. it is encoded as if it were an array of static size `k`, prefixed with
+ the number of elements.
+
+- `bytes`, of length `k` (which is assumed to be of type `uint256`):
+
+ `enc(X) = enc(k) pad_right(X)`, i.e. the number of bytes is encoded as a
+ `uint256` followed by the actual value of `X` as a byte sequence, followed by
+ the minimum number of zero-bytes such that `len(enc(X))` is a multiple of 32.
+
+- `string`:
+
+ `enc(X) = enc(enc_utf8(X))`, i.e. `X` is utf-8 encoded and this value is interpreted as of `bytes` type and encoded further. Note that the length used in this subsequent encoding is the number of bytes of the utf-8 encoded string, not its number of characters.
+
+- `uint<M>`: `enc(X)` is the big-endian encoding of `X`, padded on the higher-order (left) side with zero-bytes such that the length is a multiple of 32 bytes.
+- `address`: as in the `uint160` case
+- `int<M>`: `enc(X)` is the big-endian two's complement encoding of `X`, padded on the higher-oder (left) side with `0xff` for negative `X` and with zero bytes for positive `X` such that the length is a multiple of 32 bytes.
+- `bool`: as in the `uint8` case, where `1` is used for `true` and `0` for `false`
+- `fixed<M>x<N>`: `enc(X)` is `enc(X * 10**N)` where `X * 10**N` is interpreted as a `int256`.
+- `fixed`: as in the `fixed128x19` case
+- `ufixed<M>x<N>`: `enc(X)` is `enc(X * 10**N)` where `X * 10**N` is interpreted as a `uint256`.
+- `ufixed`: as in the `ufixed128x19` case
+- `bytes<M>`: `enc(X)` is the sequence of bytes in `X` padded with zero-bytes to a length of 32.
+
+Note that for any `X`, `len(enc(X))` is a multiple of 32.
+
+Function Selector and Argument Encoding
+=======================================
+
+All in all, a call to the function `f` with parameters `a_1, ..., a_n` is encoded as
+
+ `function_selector(f) enc((a_1, ..., a_n))`
+
+and the return values `v_1, ..., v_k` of `f` are encoded as
+
+ `enc((v_1, ..., v_k))`
+
+i.e. the values are combined into an anonymous struct and encoded.
+
+Examples
+========
+
+Given the contract:
+
+::
+
+ contract Foo {
+ function bar(bytes3[2] xy) {}
+ function baz(uint32 x, bool y) returns (bool r) { r = x > 32 || y; }
+ function sam(bytes name, bool z, uint[] data) {}
+ }
+
+
+Thus for our `Foo` example if we wanted to call `baz` with the parameters `69` and `true`, we would pass 68 bytes total, which can be broken down into:
+
+- `0xcdcd77c0`: the Method ID. This is derived as the first 4 bytes of the Keccak hash of the ASCII form of the signature `baz(uint32,bool)`.
+- `0x0000000000000000000000000000000000000000000000000000000000000045`: the first parameter, a uint32 value `69` padded to 32 bytes
+- `0x0000000000000000000000000000000000000000000000000000000000000001`: the second parameter - boolean `true`, padded to 32 bytes
+
+In total::
+
+ 0xcdcd77c000000000000000000000000000000000000000000000000000000000000000450000000000000000000000000000000000000000000000000000000000000001
+
+It returns a single `bool`. If, for example, it were to return `false`, its output would be the single byte array `0x0000000000000000000000000000000000000000000000000000000000000000`, a single bool.
+
+If we wanted to call `bar` with the argument `["abc", "def"]`, we would pass 68 bytes total, broken down into:
+
+- `0xfce353f6`: the Method ID. This is derived from the signature `bar(bytes3[2])`.
+- `0x6162630000000000000000000000000000000000000000000000000000000000`: the first part of the first parameter, a `bytes3` value `"abc"` (left-aligned).
+- `0x6465660000000000000000000000000000000000000000000000000000000000`: the second part of the first parameter, a `bytes3` value `"def"` (left-aligned).
+
+In total::
+
+ 0xfce353f661626300000000000000000000000000000000000000000000000000000000006465660000000000000000000000000000000000000000000000000000000000
+
+If we wanted to call `sam` with the arguments `"dave"`, `true` and `[1,2,3]`, we would pass 292 bytes total, broken down into:
+- `0xa5643bf2`: the Method ID. This is derived from the signature `sam(bytes,bool,uint256[])`. Note that `uint` is replaced with its canonical representation `uint256`.
+- `0x0000000000000000000000000000000000000000000000000000000000000060`: the location of the data part of the first parameter (dynamic type), measured in bytes from the start of the arguments block. In this case, `0x60`.
+- `0x0000000000000000000000000000000000000000000000000000000000000001`: the second parameter: boolean true.
+- `0x00000000000000000000000000000000000000000000000000000000000000a0`: the location of the data part of the third parameter (dynamic type), measured in bytes. In this case, `0xa0`.
+- `0x0000000000000000000000000000000000000000000000000000000000000004`: the data part of the first argument, it starts with the length of the byte array in elements, in this case, 4.
+- `0x6461766500000000000000000000000000000000000000000000000000000000`: the contents of the first argument: the UTF-8 (equal to ASCII in this case) encoding of `"dave"`, padded on the right to 32 bytes.
+- `0x0000000000000000000000000000000000000000000000000000000000000003`: the data part of the third argument, it starts with the length of the array in elements, in this case, 3.
+- `0x0000000000000000000000000000000000000000000000000000000000000001`: the first entry of the third parameter.
+- `0x0000000000000000000000000000000000000000000000000000000000000002`: the second entry of the third parameter.
+- `0x0000000000000000000000000000000000000000000000000000000000000003`: the third entry of the third parameter.
+
+In total::
+
+ 0xa5643bf20000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000464617665000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003
+
+Use of Dynamic Types
+====================
+
+A call to a function with the signature `f(uint,uint32[],bytes10,bytes)` with values `(0x123, [0x456, 0x789], "1234567890", "Hello, world!")` is encoded in the following way:
+
+We take the first four bytes of `sha3("f(uint256,uint32[],bytes10,bytes)")`, i.e. `0x8be65246`.
+Then we encode the head parts of all four arguments. For the static types `uint256` and `bytes10`, these are directly the values we want to pass, whereas for the dynamic types `uint32[]` and `bytes`, we use the offset in bytes to the start of their data area, measured from the start of the value encoding (i.e. not counting the first four bytes containing the hash of the function signature). These are:
+
+ - `0x0000000000000000000000000000000000000000000000000000000000000123` (`0x123` padded to 32 bytes)
+ - `0x0000000000000000000000000000000000000000000000000000000000000080` (offset to start of data part of second parameter, 4*32 bytes, exactly the size of the head part)
+ - `0x3132333435363738393000000000000000000000000000000000000000000000` (`"1234567890"` padded to 32 bytes on the right)
+ - `0x00000000000000000000000000000000000000000000000000000000000000e0` (offset to start of data part of fourth parameter = offset to start of data part of first dynamic parameter + size of data part of first dynamic parameter = 4\*32 + 3\*32 (see below))
+
+After this, the data part of the first dynamic argument, `[0x456, 0x789]` follows:
+
+ - `0x0000000000000000000000000000000000000000000000000000000000000002` (number of elements of the array, 2)
+ - `0x0000000000000000000000000000000000000000000000000000000000000456` (first element)
+ - `0x0000000000000000000000000000000000000000000000000000000000000789` (second element)
+
+Finally, we encode the data part of the second dynamic argument, `"Hello, world!"`:
+
+ - `0x000000000000000000000000000000000000000000000000000000000000000d` (number of elements (bytes in this case): 13)
+ - `0x48656c6c6f2c20776f726c642100000000000000000000000000000000000000` (`"Hello, world!"` padded to 32 bytes on the right)
+
+All together, the encoding is (newline after function selector and each 32-bytes for clarity):
+
+::
+
+ 0x8be65246
+ 0000000000000000000000000000000000000000000000000000000000000123
+ 0000000000000000000000000000000000000000000000000000000000000080
+ 3132333435363738393000000000000000000000000000000000000000000000
+ 00000000000000000000000000000000000000000000000000000000000000e0
+ 0000000000000000000000000000000000000000000000000000000000000002
+ 0000000000000000000000000000000000000000000000000000000000000456
+ 0000000000000000000000000000000000000000000000000000000000000789
+ 000000000000000000000000000000000000000000000000000000000000000d
+ 48656c6c6f2c20776f726c642100000000000000000000000000000000000000
+
+Events
+======
+
+Events are an abstraction of the Ethereum logging/event-watching protocol. Log entries provide the contract's address, a series of up to four topics and some arbitrary length binary data. Events leverage the existing function ABI in order to interpret this (together with an interface spec) as a properly typed structure.
+
+Given an event name and series of event parameters, we split them into two sub-series: those which are indexed and those which are not. Those which are indexed, which may number up to 3, are used alongside the Keccak hash of the event signature to form the topics of the log entry. Those which as not indexed form the byte array of the event.
+
+In effect, a log entry using this ABI is described as:
+
+- `address`: the address of the contract (intrinsically provided by Ethereum);
+- `topics[0]`: `keccak(EVENT_NAME+"("+EVENT_ARGS.map(canonical_type_of).join(",")+")")` (`canonical_type_of` is a function that simply returns the canonical type of a given argument, e.g. for `uint indexed foo`, it would return `uint256`). If the event is declared as `anonymous` the `topics[0]` is not generated;
+- `topics[n]`: `EVENT_INDEXED_ARGS[n - 1]` (`EVENT_INDEXED_ARGS` is the series of `EVENT_ARGS` that are indexed);
+- `data`: `abi_serialise(EVENT_NON_INDEXED_ARGS)` (`EVENT_NON_INDEXED_ARGS` is the series of `EVENT_ARGS` that are not indexed, `abi_serialise` is the ABI serialisation function used for returning a series of typed values from a function, as described above).
+
+JSON
+====
+
+The JSON format for a contract's interface is given by an array of function and/or event descriptions. A function description is a JSON object with the fields:
+
+- `type`: `"function"`, `"constructor"`, or `"fallback"` (the :ref:`unnamed "default" function <fallback-function>`);
+- `name`: the name of the function;
+- `inputs`: an array of objects, each of which contains:
+ * `name`: the name of the parameter;
+ * `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`.
+
+`type` can be omitted, defaulting to `"function"`.
+
+Constructor and fallback function never have `name` or `outputs`. Fallback function doesn't have `inputs` either.
+
+Sending non-zero ether to non-payable function will throw. Don't do it.
+
+An event description is a JSON object with fairly similar fields:
+
+- `type`: always `"event"`
+- `name`: the name of the event;
+- `inputs`: an array of objects, each of which contains:
+ * `name`: the name of the parameter;
+ * `type`: the canonical type of the parameter.
+ * `indexed`: `true` if the field is part of the log's topics, `false` if it one of the log's data segment.
+- `anonymous`: `true` if the event was declared as `anonymous`.
+
+For example,
+
+::
+
+ contract Test {
+ function Test(){ b = 0x12345678901234567890123456789012; }
+ event Event(uint indexed a, bytes32 b)
+ event Event2(uint indexed a, bytes32 b)
+ function foo(uint a) { Event(a, b); }
+ bytes32 b;
+ }
+
+would result in the JSON:
+
+.. code:: json
+
+ [{
+ "type":"event",
+ "inputs": [{"name":"a","type":"uint256","indexed":true},{"name":"b","type":"bytes32","indexed":false}],
+ "name":"Event"
+ }, {
+ "type":"event",
+ "inputs": [{"name":"a","type":"uint256","indexed":true},{"name":"b","type":"bytes32","indexed":false}],
+ "name":"Event2"
+ }, {
+ "type":"event",
+ "inputs": [{"name":"a","type":"uint256","indexed":true},{"name":"b","type":"bytes32","indexed":false}],
+ "name":"Event2"
+ }, {
+ "type":"function",
+ "inputs": [{"name":"a","type":"uint256"}],
+ "name":"foo",
+ "outputs": []
+ }]
diff --git a/docs/assembly.rst b/docs/assembly.rst
index 90e70031..394fc9f5 100644
--- a/docs/assembly.rst
+++ b/docs/assembly.rst
@@ -234,9 +234,17 @@ In the grammar, opcodes are represented as pre-defined identifiers.
+-------------------------+------+-----------------------------------------------------------------+
| extcodecopy(a, t, f, s) | `-` | like codecopy(t, f, s) but take code at address a |
+-------------------------+------+-----------------------------------------------------------------+
+| returndatasize | | size of the last returndata |
++-------------------------+------+-----------------------------------------------------------------+
+| returndatacopy(t, f, s) | `-` | copy s bytes from returndata at position f to mem at position t |
++-------------------------+------+-----------------------------------------------------------------+
| create(v, p, s) | | create new contract with code mem[p..(p+s)) and send v wei |
| | | and return the new address |
+-------------------------+------+-----------------------------------------------------------------+
+| create2(v, n, p, s) | | create new contract with code mem[p..(p+s)) at address |
+| | | keccak256(<address> . n . keccak256(mem[p..(p+s))) and send v |
+| | | wei and return the new address |
++-------------------------+------+-----------------------------------------------------------------+
| call(g, a, v, in, | | call contract at address a with input mem[in..(in+insize)) |
| insize, out, outsize) | | providing g gas and v wei and output area |
| | | mem[out..(out+outsize)) returning 0 on error (eg. out of gas) |
@@ -248,6 +256,9 @@ In the grammar, opcodes are represented as pre-defined identifiers.
| delegatecall(g, a, in, | | identical to `callcode` but also keep ``caller`` |
| insize, out, outsize) | | and ``callvalue`` |
+-------------------------+------+-----------------------------------------------------------------+
+| staticcall(g, a, in, | | identical to `call(g, a, 0, in, insize, out, outsize)` but do |
+| insize, out, outsize) | | not allow state modifications |
++-------------------------+------+-----------------------------------------------------------------+
| return(p, s) | `-` | end execution, return data mem[p..(p+s)) |
+-------------------------+------+-----------------------------------------------------------------+
| revert(p, s) | `-` | end execution, revert state changes, return data mem[p..(p+s)) |
@@ -314,8 +325,10 @@ would be written as follows
mstore(0x80, add(mload(0x80), 3))
-Functional style and instructional style can be mixed, but any opcode inside a
-functional style expression has to return exactly one stack slot (most of the opcodes do).
+Functional style expressions cannot use instructional style internally, i.e.
+``1 2 mstore(0x80, add)`` is not valid assembly, it has to be written as
+``mstore(0x80, add(2, 1))``. For opcodes that do not take arguments, the
+parentheses can be omitted.
Note that the order of arguments is reversed in functional-style as opposed to the instruction-style
way. If you use functional-style, the first argument will end up on the stack top.
diff --git a/docs/contracts.rst b/docs/contracts.rst
index a1192d4e..356fa90d 100644
--- a/docs/contracts.rst
+++ b/docs/contracts.rst
@@ -458,6 +458,8 @@ value types and strings.
}
+.. _constant-functions:
+
******************
Constant Functions
******************
@@ -509,7 +511,8 @@ In particular, the following operations will consume more gas than the stipend p
Please ensure you test your fallback function thoroughly to ensure the execution cost is less than 2300 gas before deploying a contract.
.. warning::
- Contracts that receive Ether but do not define a fallback function
+ Contracts that receive Ether directly (without a function call, i.e. using ``send`` or ``transfer``)
+ but do not define a fallback function
throw an exception, sending back the Ether (this was different
before Solidity v0.4.0). So if you want your contract to receive Ether,
you have to implement a fallback function.
diff --git a/docs/contributing.rst b/docs/contributing.rst
index 42204d5c..1f869dbb 100644
--- a/docs/contributing.rst
+++ b/docs/contributing.rst
@@ -12,7 +12,7 @@ In particular, we need help in the following areas:
* Improving the documentation
* Responding to questions from other users on `StackExchange
- <http://ethereum.stackexchange.com/>`_ and the `Solidity Gitter
+ <https://ethereum.stackexchange.com>`_ and the `Solidity Gitter
<https://gitter.im/ethereum/solidity>`_
* Fixing and responding to `Solidity's GitHub issues
<https://github.com/ethereum/solidity/issues>`_, especially those tagged as
diff --git a/docs/grammar.txt b/docs/grammar.txt
index dc188572..09e492f3 100644
--- a/docs/grammar.txt
+++ b/docs/grammar.txt
@@ -7,7 +7,7 @@ ImportDirective = 'import' StringLiteral ('as' Identifier)? ';'
| 'import' ('*' | Identifier) ('as' Identifier)? 'from' StringLiteral ';'
| 'import' '{' Identifier ('as' Identifier)? ( ',' Identifier ('as' Identifier)? )* '}' 'from' StringLiteral ';'
-ContractDefinition = ( 'contract' | 'library' ) Identifier
+ContractDefinition = ( 'contract' | 'library' | 'interface' ) Identifier
( 'is' InheritanceSpecifier (',' InheritanceSpecifier )* )?
'{' ContractPart* '}'
@@ -62,7 +62,7 @@ WhileStatement = 'while' '(' Expression ')' Statement
PlaceholderStatement = '_'
SimpleStatement = VariableDefinition | ExpressionStatement
ForStatement = 'for' '(' (SimpleStatement)? ';' (Expression)? ';' (ExpressionStatement)? ')' Statement
-InlineAssemblyStatement = 'assembly' InlineAssemblyBlock
+InlineAssemblyStatement = 'assembly' StringLiteral? InlineAssemblyBlock
DoWhileStatement = 'do' Statement 'while' '(' Expression ')'
Continue = 'continue'
Break = 'break'
@@ -137,7 +137,8 @@ Ufixed = 'ufixed' | 'ufixed0x8' | 'ufixed0x16' | 'ufixed0x24' | 'ufixed0x32' | '
InlineAssemblyBlock = '{' AssemblyItem* '}'
-AssemblyItem = Identifier | FunctionalAssemblyExpression | InlineAssemblyBlock | AssemblyLocalBinding | AssemblyAssignment | NumberLiteral | StringLiteral | HexLiteral
+AssemblyItem = Identifier | FunctionalAssemblyExpression | InlineAssemblyBlock | AssemblyLocalBinding | AssemblyAssignment | AssemblyLabel | NumberLiteral | StringLiteral | HexLiteral
AssemblyLocalBinding = 'let' Identifier ':=' FunctionalAssemblyExpression
-AssemblyAssignment = Identifier ':=' FunctionalAssemblyExpression | '=:' Identifier
+AssemblyAssignment = ( Identifier ':=' FunctionalAssemblyExpression ) | ( '=:' Identifier )
+AssemblyLabel = Identifier ':'
FunctionalAssemblyExpression = Identifier '(' AssemblyItem? ( ',' AssemblyItem )* ')'
diff --git a/docs/index.rst b/docs/index.rst
index 4b48b91c..3cdda62d 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -59,6 +59,9 @@ Available Solidity Integrations
* `Atom Solidity Linter <https://atom.io/packages/linter-solidity>`_
Plugin for the Atom editor that provides Solidity linting.
+* `Atom Solium Linter <https://atom.io/packages/linter-solium>`_
+ Configurable Solidty linter for Atom using Solium as a base.
+
* `Solium <https://github.com/duaraghav8/Solium/>`_
A commandline linter for Solidity which strictly follows the rules prescribed by the `Solidity Style Guide <http://solidity.readthedocs.io/en/latest/style-guide.html>`_.
@@ -142,6 +145,7 @@ Contents
solidity-in-depth.rst
security-considerations.rst
using-the-compiler.rst
+ abi-spec.rst
style-guide.rst
common-patterns.rst
bugs.rst
diff --git a/docs/layout-of-source-files.rst b/docs/layout-of-source-files.rst
index 715b29ae..f4bef82b 100644
--- a/docs/layout-of-source-files.rst
+++ b/docs/layout-of-source-files.rst
@@ -185,7 +185,7 @@ Additionally, there is another type of comment called a natspec comment,
for which the documentation is not yet written. They are written with a
triple slash (``///``) or a double asterisk block(``/** ... */``) and
they should be used directly above function declarations or statements.
-You can use Doxygen-style tags inside these comments to document
+You can use `Doxygen <https://en.wikipedia.org/wiki/Doxygen>`_-style tags inside these comments to document
functions, annotate conditions for formal verification, and provide a
**confirmation text** which is shown to users when they attempt to invoke a
function.
diff --git a/docs/types.rst b/docs/types.rst
index 7b6e1775..0a0bffea 100644
--- a/docs/types.rst
+++ b/docs/types.rst
@@ -655,7 +655,8 @@ Members
contract ArrayContract {
uint[2**20] m_aLotOfIntegers;
- // Note that the following is not a pair of arrays but an array of pairs.
+ // Note that the following is not a pair of dynamic arrays but a
+ // dynamic array of pairs (i.e. of fixed size arrays of length two).
bool[2][] m_pairsOfFlags;
// newPairs is stored in memory - the default for function arguments
@@ -795,7 +796,7 @@ Mapping types are declared as ``mapping(_KeyType => _ValueType)``.
Here ``_KeyType`` can be almost any type except for a mapping, a dynamically sized array, a contract, an enum and a struct.
``_ValueType`` can actually be any type, including mappings.
-Mappings can be seen as hashtables which are virtually initialized such that
+Mappings can be seen as `hash tables <https://en.wikipedia.org/wiki/Hash_table>`_ which are virtually initialized such that
every possible key exists and is mapped to a value whose byte-representation is
all zeros: a type's :ref:`default value <default-value>`. The similarity ends here, though: The key data is not actually stored
in a mapping, only its ``keccak256`` hash used to look up the value.
diff --git a/libdevcore/UTF8.cpp b/libdevcore/UTF8.cpp
index 9fbf4b45..449ccc5d 100644
--- a/libdevcore/UTF8.cpp
+++ b/libdevcore/UTF8.cpp
@@ -40,7 +40,7 @@ bool validateUTF8(std::string const& _input, size_t& _invalidPosition)
continue;
size_t count = 0;
- switch(_input[i] & 0xe0) {
+ switch(_input[i] & 0xf0) {
case 0xc0: count = 1; break;
case 0xe0: count = 2; break;
case 0xf0: count = 3; break;
diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp
index ea061a30..92a4c2a4 100644
--- a/libevmasm/Assembly.cpp
+++ b/libevmasm/Assembly.cpp
@@ -40,7 +40,7 @@ void Assembly::append(Assembly const& _a)
auto newDeposit = m_deposit + _a.deposit();
for (AssemblyItem i: _a.m_items)
{
- if (i.type() == Tag || (i.type() == PushTag && i != errorTag()))
+ if (i.type() == Tag || i.type() == PushTag)
i.setData(i.data() + m_usedTags);
else if (i.type() == PushSub || i.type() == PushSubSize)
i.setData(i.data() + m_subs.size());
@@ -72,13 +72,6 @@ void Assembly::append(Assembly const& _a, int _deposit)
}
}
-string Assembly::out() const
-{
- stringstream ret;
- stream(ret);
- return ret.str();
-}
-
unsigned Assembly::bytesRequired(unsigned subTagSize) const
{
for (unsigned tagSize = subTagSize; true; ++tagSize)
@@ -216,6 +209,9 @@ ostream& Assembly::streamAsm(ostream& _out, string const& _prefix, StringMap con
}
}
+ if (m_auxiliaryData.size() > 0)
+ _out << endl << _prefix << "auxdata: 0x" << toHex(m_auxiliaryData) << endl;
+
return _out;
}
@@ -315,8 +311,13 @@ Json::Value Assembly::streamAsmJson(ostream& _out, StringMap const& _sourceCodes
data[hexStr.str()] = m_subs[i]->stream(_out, "", _sourceCodes, true);
}
root[".data"] = data;
- _out << root;
}
+
+ if (m_auxiliaryData.size() > 0)
+ root[".auxdata"] = toHex(m_auxiliaryData);
+
+ _out << root;
+
return root;
}
diff --git a/libevmasm/Assembly.h b/libevmasm/Assembly.h
index 528c9e74..13d82e1a 100644
--- a/libevmasm/Assembly.h
+++ b/libevmasm/Assembly.h
@@ -69,7 +69,13 @@ public:
AssemblyItem appendJumpI() { auto ret = append(newPushTag()); append(solidity::Instruction::JUMPI); return ret; }
AssemblyItem appendJump(AssemblyItem const& _tag) { auto ret = append(_tag.pushTag()); append(solidity::Instruction::JUMP); return ret; }
AssemblyItem appendJumpI(AssemblyItem const& _tag) { auto ret = append(_tag.pushTag()); append(solidity::Instruction::JUMPI); return ret; }
- AssemblyItem errorTag() { return AssemblyItem(PushTag, 0); }
+
+ /// Adds a subroutine to the code (in the data section) and pushes its size (via a tag)
+ /// on the stack. @returns the pushsub assembly item.
+ AssemblyItem appendSubroutine(AssemblyPointer const& _assembly) { auto sub = newSub(_assembly); append(newPushSubSize(size_t(sub.data()))); return sub; }
+ void pushSubroutineSize(size_t _subRoutine) { append(newPushSubSize(_subRoutine)); }
+ /// Pushes the offset of the subroutine.
+ void pushSubroutineOffset(size_t _subRoutine) { append(AssemblyItem(PushSub, _subRoutine)); }
/// Appends @a _data literally to the very end of the bytecode.
void appendAuxiliaryDataToEnd(bytes const& _data) { m_auxiliaryData += _data; }
@@ -85,10 +91,7 @@ public:
void ignored() { m_baseDeposit = m_deposit; }
void endIgnored() { m_deposit = m_baseDeposit; m_baseDeposit = 0; }
- void popTo(int _deposit) { while (m_deposit > _deposit) append(solidity::Instruction::POP); }
-
void injectStart(AssemblyItem const& _i);
- std::string out() const;
int deposit() const { return m_deposit; }
void adjustDeposit(int _adjustment) { m_deposit += _adjustment; if (asserts(m_deposit >= 0)) BOOST_THROW_EXCEPTION(InvalidDeposit()); }
void setDeposit(int _deposit) { m_deposit = _deposit; if (asserts(m_deposit >= 0)) BOOST_THROW_EXCEPTION(InvalidDeposit()); }
diff --git a/libevmasm/AssemblyItem.h b/libevmasm/AssemblyItem.h
index 464368fb..d38db927 100644
--- a/libevmasm/AssemblyItem.h
+++ b/libevmasm/AssemblyItem.h
@@ -148,6 +148,14 @@ private:
using AssemblyItems = std::vector<AssemblyItem>;
+inline size_t bytesRequired(AssemblyItems const& _items, size_t _addressLength)
+{
+ size_t size = 0;
+ for (AssemblyItem const& item: _items)
+ size += item.bytesRequired(_addressLength);
+ return size;
+}
+
std::ostream& operator<<(std::ostream& _out, AssemblyItem const& _item);
inline std::ostream& operator<<(std::ostream& _out, AssemblyItems const& _items)
{
diff --git a/libevmasm/ConstantOptimiser.cpp b/libevmasm/ConstantOptimiser.cpp
index 0c093ebf..2ecbfa7f 100644
--- a/libevmasm/ConstantOptimiser.cpp
+++ b/libevmasm/ConstantOptimiser.cpp
@@ -99,10 +99,7 @@ bigint ConstantOptimisationMethod::dataGas(bytes const& _data) const
size_t ConstantOptimisationMethod::bytesRequired(AssemblyItems const& _items)
{
- size_t size = 0;
- for (AssemblyItem const& item: _items)
- size += item.bytesRequired(3); // assume 3 byte addresses
- return size;
+ return eth::bytesRequired(_items, 3); // assume 3 byte addresses
}
void ConstantOptimisationMethod::replaceConstants(
diff --git a/libevmasm/GasMeter.cpp b/libevmasm/GasMeter.cpp
index 260b7439..c96c6ca5 100644
--- a/libevmasm/GasMeter.cpp
+++ b/libevmasm/GasMeter.cpp
@@ -103,6 +103,7 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _
break;
case Instruction::CALLDATACOPY:
case Instruction::CODECOPY:
+ case Instruction::RETURNDATACOPY:
gas += memoryGas(0, -2);
gas += wordGas(GasCosts::copyGas, m_state->relativeStackElement(-2));
break;
@@ -128,6 +129,7 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _
case Instruction::CALL:
case Instruction::CALLCODE:
case Instruction::DELEGATECALL:
+ case Instruction::STATICCALL:
{
if (_includeExternalCosts)
// We assume that we do not know the target contract and thus, the consumption is infinite.
@@ -141,8 +143,10 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _
gas = GasConsumption::infinite();
if (_item.instruction() == Instruction::CALL)
gas += GasCosts::callNewAccountGas; // We very rarely know whether the address exists.
- int valueSize = _item.instruction() == Instruction::DELEGATECALL ? 0 : 1;
- if (!classes.knownZero(m_state->relativeStackElement(-1 - valueSize)))
+ int valueSize = 1;
+ if (_item.instruction() == Instruction::DELEGATECALL || _item.instruction() == Instruction::STATICCALL)
+ valueSize = 0;
+ else if (!classes.knownZero(m_state->relativeStackElement(-1 - valueSize)))
gas += GasCosts::callValueTransferGas;
gas += memoryGas(-2 - valueSize, -3 - valueSize);
gas += memoryGas(-4 - valueSize, -5 - valueSize);
@@ -154,6 +158,7 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _
gas += GasCosts::callNewAccountGas; // We very rarely know whether the address exists.
break;
case Instruction::CREATE:
+ case Instruction::CREATE2:
if (_includeExternalCosts)
// We assume that we do not know the target contract and thus, the consumption is infinite.
gas = GasConsumption::infinite();
diff --git a/libevmasm/Instruction.cpp b/libevmasm/Instruction.cpp
index 25eab60b..8feb733a 100644
--- a/libevmasm/Instruction.cpp
+++ b/libevmasm/Instruction.cpp
@@ -67,6 +67,8 @@ const std::map<std::string, Instruction> dev::solidity::c_instructions =
{ "GASPRICE", Instruction::GASPRICE },
{ "EXTCODESIZE", Instruction::EXTCODESIZE },
{ "EXTCODECOPY", Instruction::EXTCODECOPY },
+ { "RETURNDATASIZE", Instruction::RETURNDATASIZE },
+ { "RETURNDATACOPY", Instruction::RETURNDATACOPY },
{ "BLOCKHASH", Instruction::BLOCKHASH },
{ "COINBASE", Instruction::COINBASE },
{ "TIMESTAMP", Instruction::TIMESTAMP },
@@ -157,8 +159,10 @@ const std::map<std::string, Instruction> dev::solidity::c_instructions =
{ "CREATE", Instruction::CREATE },
{ "CALL", Instruction::CALL },
{ "CALLCODE", Instruction::CALLCODE },
+ { "STATICCALL", Instruction::STATICCALL },
{ "RETURN", Instruction::RETURN },
{ "DELEGATECALL", Instruction::DELEGATECALL },
+ { "CREATE2", Instruction::CREATE2 },
{ "REVERT", Instruction::REVERT },
{ "INVALID", Instruction::INVALID },
{ "SELFDESTRUCT", Instruction::SELFDESTRUCT }
@@ -203,6 +207,8 @@ static const std::map<Instruction, InstructionInfo> c_instructionInfo =
{ Instruction::GASPRICE, { "GASPRICE", 0, 0, 1, false, Tier::Base } },
{ Instruction::EXTCODESIZE, { "EXTCODESIZE", 0, 1, 1, false, Tier::ExtCode } },
{ Instruction::EXTCODECOPY, { "EXTCODECOPY", 0, 4, 0, true, Tier::ExtCode } },
+ { Instruction::RETURNDATASIZE, {"RETURNDATASIZE", 0, 0, 1, false, Tier::Base } },
+ { Instruction::RETURNDATACOPY, {"RETURNDATACOPY", 0, 3, 0, true, Tier::VeryLow } },
{ Instruction::BLOCKHASH, { "BLOCKHASH", 0, 1, 1, false, Tier::Ext } },
{ Instruction::COINBASE, { "COINBASE", 0, 0, 1, false, Tier::Base } },
{ Instruction::TIMESTAMP, { "TIMESTAMP", 0, 0, 1, false, Tier::Base } },
@@ -295,7 +301,9 @@ static const std::map<Instruction, InstructionInfo> c_instructionInfo =
{ Instruction::CALLCODE, { "CALLCODE", 0, 7, 1, true, Tier::Special } },
{ Instruction::RETURN, { "RETURN", 0, 2, 0, true, Tier::Zero } },
{ Instruction::DELEGATECALL, { "DELEGATECALL", 0, 6, 1, true, Tier::Special } },
- { Instruction::REVERT, { "REVERT", 0, 2, 0, true, Tier::Zero } },
+ { Instruction::STATICCALL, { "STATICCALL", 0, 6, 1, true, Tier::Special } },
+ { Instruction::CREATE2, { "CREATE2", 0, 4, 1, true, Tier::Special } },
+ { Instruction::REVERT, { "REVERT", 0, 2, 0, true, Tier::Zero } },
{ Instruction::INVALID, { "INVALID", 0, 0, 0, true, Tier::Zero } },
{ Instruction::SELFDESTRUCT, { "SELFDESTRUCT", 0, 1, 0, true, Tier::Special } }
};
diff --git a/libevmasm/Instruction.h b/libevmasm/Instruction.h
index 09d1e58b..89a25fb7 100644
--- a/libevmasm/Instruction.h
+++ b/libevmasm/Instruction.h
@@ -77,6 +77,8 @@ enum class Instruction: uint8_t
GASPRICE, ///< get price of gas in current environment
EXTCODESIZE, ///< get external code size (from another contract)
EXTCODECOPY, ///< copy external code (from another contract)
+ RETURNDATASIZE = 0x3d, ///< get size of return data buffer
+ RETURNDATACOPY = 0x3e, ///< copy return data in current environment to memory
BLOCKHASH = 0x40, ///< get hash of most recent complete block
COINBASE, ///< get the block's coinbase address
@@ -185,6 +187,8 @@ enum class Instruction: uint8_t
CALLCODE, ///< message-call with another account's code only
RETURN, ///< halt execution returning output data
DELEGATECALL, ///< like CALLCODE but keeps caller's value and sender
+ STATICCALL = 0xfa, ///< like CALL but disallow state modifications
+ CREATE2 = 0xfb, ///< create new account with associated code at address `sha3(sender + salt + sha3(init code)) % 2**160`
REVERT = 0xfd, ///< halt execution, revert state and return output data
INVALID = 0xfe, ///< invalid instruction for expressing runtime errors (e.g., division-by-zero)
diff --git a/libevmasm/PeepholeOptimiser.cpp b/libevmasm/PeepholeOptimiser.cpp
index 6c92d76b..e94a8ba4 100644
--- a/libevmasm/PeepholeOptimiser.cpp
+++ b/libevmasm/PeepholeOptimiser.cpp
@@ -136,6 +136,21 @@ struct DoubleSwap: SimplePeepholeOptimizerMethod<DoubleSwap, 2>
}
};
+struct DoublePush: SimplePeepholeOptimizerMethod<DoublePush, 2>
+{
+ static bool applySimple(AssemblyItem const& _push1, AssemblyItem const& _push2, std::back_insert_iterator<AssemblyItems> _out)
+ {
+ if (_push1.type() == Push && _push2.type() == Push && _push1.data() == _push2.data())
+ {
+ *_out = _push1;
+ *_out = {Instruction::DUP1, _push2.location()};
+ return true;
+ }
+ else
+ return false;
+ }
+};
+
struct JumpToNext: SimplePeepholeOptimizerMethod<JumpToNext, 3>
{
static size_t applySimple(
@@ -235,13 +250,15 @@ bool PeepholeOptimiser::optimise()
{
OptimiserState state {m_items, 0, std::back_inserter(m_optimisedItems)};
while (state.i < m_items.size())
- applyMethods(state, PushPop(), OpPop(), DoubleSwap(), JumpToNext(), UnreachableCode(), TagConjunctions(), Identity());
- if (m_optimisedItems.size() < m_items.size())
+ applyMethods(state, PushPop(), OpPop(), DoublePush(), DoubleSwap(), JumpToNext(), UnreachableCode(), TagConjunctions(), Identity());
+ if (m_optimisedItems.size() < m_items.size() || (
+ m_optimisedItems.size() == m_items.size() &&
+ eth::bytesRequired(m_optimisedItems, 3) < eth::bytesRequired(m_items, 3)
+ ))
{
m_items = std::move(m_optimisedItems);
return true;
}
else
return false;
-
}
diff --git a/libevmasm/SemanticInformation.cpp b/libevmasm/SemanticInformation.cpp
index 61586e7b..f63f0c61 100644
--- a/libevmasm/SemanticInformation.cpp
+++ b/libevmasm/SemanticInformation.cpp
@@ -137,12 +137,16 @@ bool SemanticInformation::isDeterministic(AssemblyItem const& _item)
case Instruction::CALL:
case Instruction::CALLCODE:
case Instruction::DELEGATECALL:
+ case Instruction::STATICCALL:
case Instruction::CREATE:
+ case Instruction::CREATE2:
case Instruction::GAS:
case Instruction::PC:
case Instruction::MSIZE: // depends on previous writes and reads, not only on content
case Instruction::BALANCE: // depends on previous calls
case Instruction::EXTCODESIZE:
+ case Instruction::RETURNDATACOPY: // depends on previous calls
+ case Instruction::RETURNDATASIZE:
return false;
default:
return true;
@@ -156,11 +160,13 @@ bool SemanticInformation::invalidatesMemory(Instruction _instruction)
case Instruction::CALLDATACOPY:
case Instruction::CODECOPY:
case Instruction::EXTCODECOPY:
+ case Instruction::RETURNDATACOPY:
case Instruction::MSTORE:
case Instruction::MSTORE8:
case Instruction::CALL:
case Instruction::CALLCODE:
case Instruction::DELEGATECALL:
+ case Instruction::STATICCALL:
return true;
default:
return false;
@@ -175,6 +181,7 @@ bool SemanticInformation::invalidatesStorage(Instruction _instruction)
case Instruction::CALLCODE:
case Instruction::DELEGATECALL:
case Instruction::CREATE:
+ case Instruction::CREATE2:
case Instruction::SSTORE:
return true;
default:
diff --git a/libjulia/backends/evm/EVMCodeTransform.cpp b/libjulia/backends/evm/EVMCodeTransform.cpp
index cd6fd276..7c14eb8b 100644
--- a/libjulia/backends/evm/EVMCodeTransform.cpp
+++ b/libjulia/backends/evm/EVMCodeTransform.cpp
@@ -64,8 +64,7 @@ void CodeTransform::operator()(VariableDeclaration const& _varDecl)
for (auto const& variable: _varDecl.variables)
{
auto& var = boost::get<Scope::Variable>(m_scope->identifiers.at(variable.name));
- var.stackHeight = height++;
- var.active = true;
+ m_context->variableStackHeights[&var] = height++;
}
checkStackHeight(&_varDecl);
}
@@ -91,8 +90,7 @@ void CodeTransform::operator()(Label const& _label)
solAssert(m_scope, "");
solAssert(m_scope->identifiers.count(_label.name), "");
Scope::Label& label = boost::get<Scope::Label>(m_scope->identifiers.at(_label.name));
- assignLabelIdIfUnset(label.id);
- m_assembly.appendLabel(*label.id);
+ m_assembly.appendLabel(labelID(label));
checkStackHeight(&_label);
}
@@ -120,12 +118,11 @@ void CodeTransform::operator()(FunctionCall const& _call)
for (auto const& arg: _call.arguments | boost::adaptors::reversed)
visitExpression(arg);
m_assembly.setSourceLocation(_call.location);
- assignLabelIdIfUnset(function->id);
if (m_evm15)
- m_assembly.appendJumpsub(*function->id, function->arguments.size(), function->returns.size());
+ m_assembly.appendJumpsub(functionEntryID(*function), function->arguments.size(), function->returns.size());
else
{
- m_assembly.appendJumpTo(*function->id, function->returns.size() - function->arguments.size() - 1);
+ m_assembly.appendJumpTo(functionEntryID(*function), function->returns.size() - function->arguments.size() - 1);
m_assembly.appendLabel(returnLabel);
m_stackAdjustment--;
}
@@ -181,8 +178,7 @@ void CodeTransform::operator()(assembly::Identifier const& _identifier)
},
[=](Scope::Label& _label)
{
- assignLabelIdIfUnset(_label.id);
- m_assembly.appendLabelReference(*_label.id);
+ m_assembly.appendLabelReference(labelID(_label));
},
[=](Scope::Function&)
{
@@ -282,7 +278,6 @@ void CodeTransform::operator()(FunctionDefinition const& _function)
solAssert(m_scope, "");
solAssert(m_scope->identifiers.count(_function.name), "");
Scope::Function& function = boost::get<Scope::Function>(m_scope->identifiers.at(_function.name));
- assignLabelIdIfUnset(function.id);
int const localStackAdjustment = m_evm15 ? 0 : 1;
int height = localStackAdjustment;
@@ -292,8 +287,7 @@ void CodeTransform::operator()(FunctionDefinition const& _function)
for (auto const& v: _function.arguments | boost::adaptors::reversed)
{
auto& var = boost::get<Scope::Variable>(varScope->identifiers.at(v.name));
- var.stackHeight = height++;
- var.active = true;
+ m_context->variableStackHeights[&var] = height++;
}
m_assembly.setSourceLocation(_function.location);
@@ -303,25 +297,24 @@ void CodeTransform::operator()(FunctionDefinition const& _function)
if (m_evm15)
{
m_assembly.appendJumpTo(afterFunction, -stackHeightBefore);
- m_assembly.appendBeginsub(*function.id, _function.arguments.size());
+ m_assembly.appendBeginsub(functionEntryID(function), _function.arguments.size());
}
else
{
m_assembly.appendJumpTo(afterFunction, -stackHeightBefore + height);
- m_assembly.appendLabel(*function.id);
+ m_assembly.appendLabel(functionEntryID(function));
}
m_stackAdjustment += localStackAdjustment;
for (auto const& v: _function.returns)
{
auto& var = boost::get<Scope::Variable>(varScope->identifiers.at(v.name));
- var.stackHeight = height++;
- var.active = true;
+ m_context->variableStackHeights[&var] = height++;
// Preset stack slots for return variables to zero.
m_assembly.appendConstant(u256(0));
}
- CodeTransform(m_assembly, m_info, m_evm15, m_identifierAccess, localStackAdjustment)
+ CodeTransform(m_assembly, m_info, m_evm15, m_identifierAccess, localStackAdjustment, m_context)
.run(_function.body);
{
@@ -367,7 +360,7 @@ void CodeTransform::operator()(FunctionDefinition const& _function)
void CodeTransform::operator()(Block const& _block)
{
- CodeTransform(m_assembly, m_info, m_evm15, m_identifierAccess, m_stackAdjustment).run(_block);
+ CodeTransform(m_assembly, m_info, m_evm15, m_identifierAccess, m_stackAdjustment, m_context).run(_block);
}
AbstractAssembly::LabelID CodeTransform::labelFromIdentifier(Identifier const& _identifier)
@@ -377,8 +370,7 @@ AbstractAssembly::LabelID CodeTransform::labelFromIdentifier(Identifier const& _
[=](Scope::Variable&) { solAssert(false, "Expected label"); },
[&](Scope::Label& _label)
{
- assignLabelIdIfUnset(_label.id);
- label = *_label.id;
+ label = labelID(_label);
},
[=](Scope::Function&) { solAssert(false, "Expected label"); }
)))
@@ -388,6 +380,20 @@ AbstractAssembly::LabelID CodeTransform::labelFromIdentifier(Identifier const& _
return label;
}
+AbstractAssembly::LabelID CodeTransform::labelID(Scope::Label const& _label)
+{
+ if (!m_context->labelIDs.count(&_label))
+ m_context->labelIDs[&_label] = m_assembly.newLabelId();
+ return m_context->labelIDs[&_label];
+}
+
+AbstractAssembly::LabelID CodeTransform::functionEntryID(Scope::Function const& _function)
+{
+ if (!m_context->functionEntryIDs.count(&_function))
+ m_context->functionEntryIDs[&_function] = m_assembly.newLabelId();
+ return m_context->functionEntryIDs[&_function];
+}
+
void CodeTransform::visitExpression(Statement const& _expression)
{
int height = m_assembly.stackHeight();
@@ -418,7 +424,8 @@ void CodeTransform::generateAssignment(Identifier const& _variableName)
int CodeTransform::variableHeightDiff(solidity::assembly::Scope::Variable const& _var, bool _forSwap)
{
- int heightDiff = m_assembly.stackHeight() - _var.stackHeight;
+ solAssert(m_context->variableStackHeights.count(&_var), "");
+ int heightDiff = m_assembly.stackHeight() - m_context->variableStackHeights[&_var];
if (heightDiff <= (_forSwap ? 1 : 0) || heightDiff > (_forSwap ? 17 : 16))
{
solUnimplemented(
@@ -446,9 +453,3 @@ void CodeTransform::checkStackHeight(void const* _astElement)
to_string(m_assembly.stackHeight() - m_stackAdjustment)
);
}
-
-void CodeTransform::assignLabelIdIfUnset(boost::optional<AbstractAssembly::LabelID>& _labelId)
-{
- if (!_labelId)
- _labelId.reset(m_assembly.newLabelId());
-}
diff --git a/libjulia/backends/evm/EVMCodeTransform.h b/libjulia/backends/evm/EVMCodeTransform.h
index f65948dc..202f5051 100644
--- a/libjulia/backends/evm/EVMCodeTransform.h
+++ b/libjulia/backends/evm/EVMCodeTransform.h
@@ -64,7 +64,14 @@ public:
solidity::assembly::AsmAnalysisInfo& _analysisInfo,
bool _evm15 = false,
ExternalIdentifierAccess const& _identifierAccess = ExternalIdentifierAccess()
- ): CodeTransform(_assembly, _analysisInfo, _evm15, _identifierAccess, _assembly.stackHeight())
+ ): CodeTransform(
+ _assembly,
+ _analysisInfo,
+ _evm15,
+ _identifierAccess,
+ _assembly.stackHeight(),
+ std::make_shared<Context>()
+ )
{
}
@@ -72,18 +79,28 @@ public:
void run(solidity::assembly::Block const& _block);
protected:
+ struct Context
+ {
+ using Scope = solidity::assembly::Scope;
+ std::map<Scope::Label const*, AbstractAssembly::LabelID> labelIDs;
+ std::map<Scope::Function const*, AbstractAssembly::LabelID> functionEntryIDs;
+ std::map<Scope::Variable const*, int> variableStackHeights;
+ };
+
CodeTransform(
julia::AbstractAssembly& _assembly,
solidity::assembly::AsmAnalysisInfo& _analysisInfo,
bool _evm15,
ExternalIdentifierAccess const& _identifierAccess,
- int _stackAdjustment
+ int _stackAdjustment,
+ std::shared_ptr<Context> _context
):
m_assembly(_assembly),
m_info(_analysisInfo),
m_evm15(_evm15),
m_identifierAccess(_identifierAccess),
- m_stackAdjustment(_stackAdjustment)
+ m_stackAdjustment(_stackAdjustment),
+ m_context(_context)
{}
public:
@@ -102,6 +119,10 @@ public:
private:
AbstractAssembly::LabelID labelFromIdentifier(solidity::assembly::Identifier const& _identifier);
+ /// @returns the label ID corresponding to the given label, allocating a new one if
+ /// necessary.
+ AbstractAssembly::LabelID labelID(solidity::assembly::Scope::Label const& _label);
+ AbstractAssembly::LabelID functionEntryID(solidity::assembly::Scope::Function const& _function);
/// Generates code for an expression that is supposed to return a single value.
void visitExpression(solidity::assembly::Statement const& _expression);
@@ -116,9 +137,6 @@ private:
void checkStackHeight(void const* _astElement);
- /// Assigns the label's or function's id to a value taken from eth::Assembly if it has not yet been set.
- void assignLabelIdIfUnset(boost::optional<AbstractAssembly::LabelID>& _labelId);
-
julia::AbstractAssembly& m_assembly;
solidity::assembly::AsmAnalysisInfo& m_info;
solidity::assembly::Scope* m_scope = nullptr;
@@ -129,6 +147,7 @@ private:
/// for inline assembly and different stack heights depending on the EVM backend used
/// (EVM 1.0 or 1.5).
int m_stackAdjustment = 0;
+ std::shared_ptr<Context> m_context;
};
}
diff --git a/liblll/CodeFragment.cpp b/liblll/CodeFragment.cpp
index 2b8822a6..9f37bc65 100644
--- a/liblll/CodeFragment.cpp
+++ b/liblll/CodeFragment.cpp
@@ -515,8 +515,7 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s)
requireMaxSize(3);
requireDeposit(1, 1);
- auto subPush = m_asm.newSub(make_shared<Assembly>(code[0].assembly(ns)));
- m_asm.append(m_asm.newPushSubSize(subPush.data()));
+ auto subPush = m_asm.appendSubroutine(make_shared<Assembly>(code[0].assembly(ns)));
m_asm.append(Instruction::DUP1);
if (code.size() == 3)
{
@@ -571,11 +570,9 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s)
{
for (auto const& i: code)
m_asm.append(i.m_asm);
- m_asm.popTo(1);
- }
- else if (us == "PANIC")
- {
- m_asm.appendJump(m_asm.errorTag());
+ // Leave only the last item on stack.
+ while (m_asm.deposit() > 1)
+ m_asm.append(Instruction::POP);
}
else if (us == "BYTECODESIZE")
{
diff --git a/liblll/Compiler.cpp b/liblll/Compiler.cpp
index ea8b27af..05376cd5 100644
--- a/liblll/Compiler.cpp
+++ b/liblll/Compiler.cpp
@@ -69,10 +69,11 @@ std::string dev::eth::compileLLLToAsm(std::string const& _src, bool _opt, std::v
{
CompilerState cs;
cs.populateStandard();
- string ret = CodeFragment::compile(_src, cs).assembly(cs).optimise(_opt).out();
+ stringstream ret;
+ CodeFragment::compile(_src, cs).assembly(cs).optimise(_opt).stream(ret);
for (auto i: cs.treesToKill)
killBigints(i);
- return ret;
+ return ret.str();
}
catch (Exception const& _e)
{
diff --git a/liblll/CompilerState.cpp b/liblll/CompilerState.cpp
index 88e43e18..c22242a3 100644
--- a/liblll/CompilerState.cpp
+++ b/liblll/CompilerState.cpp
@@ -45,6 +45,7 @@ CodeFragment const& CompilerState::getDef(std::string const& _s)
void CompilerState::populateStandard()
{
static const string s = "{"
+ "(def 'panic () (asm INVALID))"
"(def 'allgas (- (gas) 21))"
"(def 'send (to value) (call allgas to value 0 0 0 0))"
"(def 'send (gaslimit to value) (call gaslimit to value 0 0 0 0))"
@@ -55,10 +56,10 @@ void CompilerState::populateStandard()
"(def 'msg (to data) { [0]:data (msg allgas to 0 0 32) })"
"(def 'create (value code) { [0]:(msize) (create value @0 (lll code @0)) })"
"(def 'create (code) { [0]:(msize) (create 0 @0 (lll code @0)) })"
+ "(def 'sha3 (loc len) (keccak256 loc len))"
"(def 'sha3 (val) { [0]:val (sha3 0 32) })"
"(def 'sha3pair (a b) { [0]:a [32]:b (sha3 0 64) })"
"(def 'sha3trip (a b c) { [0]:a [32]:b [64]:c (sha3 0 96) })"
- "(def 'keccak256 (loc len) (sha3 loc len))"
"(def 'return (val) { [0]:val (return 0 32) })"
"(def 'returnlll (code) (return 0 (lll code 0)) )"
"(def 'makeperm (name pos) { (def name (sload pos)) (def name (v) (sstore pos v)) } )"
@@ -73,6 +74,9 @@ void CompilerState::populateStandard()
"(def 'szabo 1000000000000)"
"(def 'finney 1000000000000000)"
"(def 'ether 1000000000000000000)"
+ // these could be replaced by native instructions once supported by EVM
+ "(def 'shl (val shift) (mul val (exp 2 shift)))"
+ "(def 'shr (val shift) (div val (exp 2 shift)))"
"}";
CodeFragment::compile(s, *this);
}
diff --git a/liblll/Parser.cpp b/liblll/Parser.cpp
index 44d2a2ae..a3962df4 100644
--- a/liblll/Parser.cpp
+++ b/liblll/Parser.cpp
@@ -109,7 +109,7 @@ void dev::eth::parseTreeLLL(string const& _s, sp::utree& o_out)
qi::rule<it, space_type, sp::utree::list_type()> mstore = '[' > element > ']' > -qi::lit(":") > element;
qi::rule<it, space_type, sp::utree::list_type()> sstore = qi::lit("[[") > element > qi::lit("]]") > -qi::lit(":") > element;
qi::rule<it, space_type, sp::utree::list_type()> calldataload = qi::lit("$") > element;
- qi::rule<it, space_type, sp::utree::list_type()> list = '(' > +element > ')';
+ qi::rule<it, space_type, sp::utree::list_type()> list = '(' > *element > ')';
qi::rule<it, space_type, sp::utree()> extra = sload[tagNode<2>()] | mload[tagNode<1>()] | sstore[tagNode<4>()] | mstore[tagNode<3>()] | seq[tagNode<5>()] | calldataload[tagNode<6>()];
element = atom | list | extra;
diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp
index 2742dcf2..aac90311 100644
--- a/libsolidity/analysis/NameAndTypeResolver.cpp
+++ b/libsolidity/analysis/NameAndTypeResolver.cpp
@@ -26,6 +26,8 @@
#include <libsolidity/analysis/TypeChecker.h>
#include <libsolidity/interface/ErrorReporter.h>
+#include <boost/algorithm/string.hpp>
+
using namespace std;
namespace dev
@@ -232,6 +234,26 @@ vector<Declaration const*> NameAndTypeResolver::cleanedDeclarations(
return uniqueFunctions;
}
+void NameAndTypeResolver::warnVariablesNamedLikeInstructions()
+{
+ for (auto const& instruction: c_instructions)
+ {
+ string const instructionName{boost::algorithm::to_lower_copy(instruction.first)};
+ auto declarations = nameFromCurrentScope(instructionName);
+ for (Declaration const* const declaration: declarations)
+ {
+ solAssert(!!declaration, "");
+ if (dynamic_cast<MagicVariableDeclaration const* const>(declaration))
+ // Don't warn the user for what the user did not.
+ continue;
+ m_errorReporter.warning(
+ declaration->location(),
+ "Variable is shadowed in inline assembly by an instruction of the same name"
+ );
+ }
+ }
+}
+
bool NameAndTypeResolver::resolveNamesAndTypesInternal(ASTNode& _node, bool _resolveInsideCode)
{
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(&_node))
diff --git a/libsolidity/analysis/NameAndTypeResolver.h b/libsolidity/analysis/NameAndTypeResolver.h
index 0441867d..84628778 100644
--- a/libsolidity/analysis/NameAndTypeResolver.h
+++ b/libsolidity/analysis/NameAndTypeResolver.h
@@ -90,6 +90,9 @@ public:
std::vector<Declaration const*> const& _declarations
);
+ /// Generate and store warnings about variables that are named like instructions.
+ void warnVariablesNamedLikeInstructions();
+
private:
/// Internal version of @a resolveNamesAndTypes (called from there) throws exceptions on fatal errors.
bool resolveNamesAndTypesInternal(ASTNode& _node, bool _resolveInsideCode = true);
diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp
index edf2fc02..2a5f27df 100644
--- a/libsolidity/analysis/ReferencesResolver.cpp
+++ b/libsolidity/analysis/ReferencesResolver.cpp
@@ -162,6 +162,8 @@ void ReferencesResolver::endVisit(ArrayTypeName const& _typeName)
bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly)
{
+ m_resolver.warnVariablesNamedLikeInstructions();
+
// Errors created in this stage are completely ignored because we do not yet know
// the type and size of external identifiers, which would result in false errors.
// The only purpose of this step is to fill the inline assembly annotation with
diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp
index b1911ef0..2a8d1ff6 100644
--- a/libsolidity/analysis/TypeChecker.cpp
+++ b/libsolidity/analysis/TypeChecker.cpp
@@ -1287,14 +1287,11 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
membersRemovedForStructConstructor = structType.membersMissingInMemory();
_functionCall.annotation().isPure = isPure;
}
- else
- {
- functionType = dynamic_pointer_cast<FunctionType const>(expressionType);
+ else if ((functionType = dynamic_pointer_cast<FunctionType const>(expressionType)))
_functionCall.annotation().isPure =
isPure &&
_functionCall.expression().annotation().isPure &&
functionType->isPure();
- }
if (!functionType)
{
diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp
index 1de2e801..4ad1f962 100644
--- a/libsolidity/ast/ASTJsonConverter.cpp
+++ b/libsolidity/ast/ASTJsonConverter.cpp
@@ -252,6 +252,7 @@ bool ASTJsonConverter::visit(ContractDefinition const& _node)
{
setJsonNode(_node, "ContractDefinition", {
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("linearizedBaseContracts", getContainerIds(_node.annotation().linearizedBaseContracts)),
diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h
index c37142c9..030b35a6 100644
--- a/libsolidity/codegen/CompilerContext.h
+++ b/libsolidity/codegen/CompilerContext.h
@@ -141,8 +141,6 @@ public:
CompilerContext& appendInvalid();
/// Appends a conditional INVALID instruction
CompilerContext& appendConditionalInvalid();
- /// Returns an "ErrorTag"
- eth::AssemblyItem errorTag() { return m_asm->errorTag(); }
/// Appends a JUMP to a specific tag
CompilerContext& appendJumpTo(eth::AssemblyItem const& _tag) { m_asm->appendJump(_tag); return *this; }
/// Appends pushing of a new tag and @returns the new tag.
@@ -151,10 +149,10 @@ public:
eth::AssemblyItem newTag() { return m_asm->newTag(); }
/// Adds a subroutine to the code (in the data section) and pushes its size (via a tag)
/// on the stack. @returns the pushsub assembly item.
- eth::AssemblyItem addSubroutine(eth::AssemblyPointer const& _assembly) { auto sub = m_asm->newSub(_assembly); m_asm->append(m_asm->newPushSubSize(size_t(sub.data()))); return sub; }
- void pushSubroutineSize(size_t _subRoutine) { m_asm->append(m_asm->newPushSubSize(_subRoutine)); }
+ eth::AssemblyItem addSubroutine(eth::AssemblyPointer const& _assembly) { return m_asm->appendSubroutine(_assembly); }
+ void pushSubroutineSize(size_t _subRoutine) { m_asm->pushSubroutineSize(_subRoutine); }
/// Pushes the offset of the subroutine.
- void pushSubroutineOffset(size_t _subRoutine) { m_asm->append(eth::AssemblyItem(eth::PushSub, _subRoutine)); }
+ void pushSubroutineOffset(size_t _subRoutine) { m_asm->pushSubroutineOffset(_subRoutine); }
/// Pushes the size of the final program
void appendProgramSize() { m_asm->appendProgramSize(); }
/// Adds data to the data section, pushes a reference to the stack
diff --git a/libsolidity/inlineasm/AsmAnalysis.cpp b/libsolidity/inlineasm/AsmAnalysis.cpp
index 13852880..1a529118 100644
--- a/libsolidity/inlineasm/AsmAnalysis.cpp
+++ b/libsolidity/inlineasm/AsmAnalysis.cpp
@@ -29,6 +29,7 @@
#include <libsolidity/interface/Utils.h>
#include <boost/range/adaptor/reversed.hpp>
+#include <boost/algorithm/string.hpp>
#include <memory>
#include <functional>
@@ -65,6 +66,7 @@ bool AsmAnalyzer::operator()(assembly::Instruction const& _instruction)
auto const& info = instructionInfo(_instruction.instruction);
m_stackHeight += info.ret - info.args;
m_info.stackHeightInfo[&_instruction] = m_stackHeight;
+ warnOnFutureInstruction(_instruction.instruction, _instruction.location);
return true;
}
@@ -91,7 +93,7 @@ bool AsmAnalyzer::operator()(assembly::Identifier const& _identifier)
if (m_currentScope->lookup(_identifier.name, Scope::Visitor(
[&](Scope::Variable const& _var)
{
- if (!_var.active)
+ if (!m_activeVariables.count(&_var))
{
m_errorReporter.declarationError(
_identifier.location,
@@ -149,6 +151,7 @@ bool AsmAnalyzer::operator()(FunctionalInstruction const& _instr)
if (!(*this)(_instr.instruction))
success = false;
m_info.stackHeightInfo[&_instr] = m_stackHeight;
+ warnOnFutureInstruction(_instr.instruction.instruction, _instr.location);
return success;
}
@@ -185,7 +188,7 @@ bool AsmAnalyzer::operator()(assembly::VariableDeclaration const& _varDecl)
for (auto const& variable: _varDecl.variables)
{
expectValidType(variable.type, variable.location);
- boost::get<Scope::Variable>(m_currentScope->identifiers.at(variable.name)).active = true;
+ m_activeVariables.insert(&boost::get<Scope::Variable>(m_currentScope->identifiers.at(variable.name)));
}
m_info.stackHeightInfo[&_varDecl] = m_stackHeight;
return success;
@@ -199,7 +202,7 @@ bool AsmAnalyzer::operator()(assembly::FunctionDefinition const& _funDef)
for (auto const& var: _funDef.arguments + _funDef.returns)
{
expectValidType(var.type, var.location);
- boost::get<Scope::Variable>(varScope.identifiers.at(var.name)).active = true;
+ m_activeVariables.insert(&boost::get<Scope::Variable>(varScope.identifiers.at(var.name)));
}
int const stackHeight = m_stackHeight;
@@ -382,7 +385,7 @@ bool AsmAnalyzer::checkAssignment(assembly::Identifier const& _variable, size_t
m_errorReporter.typeError(_variable.location, "Assignment requires variable.");
success = false;
}
- else if (!boost::get<Scope::Variable>(*var).active)
+ else if (!m_activeVariables.count(&boost::get<Scope::Variable>(*var)))
{
m_errorReporter.declarationError(
_variable.location,
@@ -431,7 +434,6 @@ Scope& AsmAnalyzer::scope(Block const* _block)
solAssert(scopePtr, "Scope requested but not present.");
return *scopePtr;
}
-
void AsmAnalyzer::expectValidType(string const& type, SourceLocation const& _location)
{
if (!m_julia)
@@ -443,3 +445,21 @@ void AsmAnalyzer::expectValidType(string const& type, SourceLocation const& _loc
"\"" + type + "\" is not a valid type (user defined types are not yet supported)."
);
}
+
+void AsmAnalyzer::warnOnFutureInstruction(solidity::Instruction _instr, SourceLocation const& _location)
+{
+ static set<solidity::Instruction> futureInstructions{
+ solidity::Instruction::CREATE2,
+ solidity::Instruction::RETURNDATACOPY,
+ solidity::Instruction::RETURNDATASIZE,
+ solidity::Instruction::STATICCALL
+ };
+ if (futureInstructions.count(_instr))
+ m_errorReporter.warning(
+ _location,
+ "The \"" +
+ boost::to_lower_copy(instructionInfo(_instr).name)
+ + "\" instruction is only available after " +
+ "the Metropolis hard fork. Before that it acts as an invalid instruction."
+ );
+}
diff --git a/libsolidity/inlineasm/AsmAnalysis.h b/libsolidity/inlineasm/AsmAnalysis.h
index e7748bcf..2516722a 100644
--- a/libsolidity/inlineasm/AsmAnalysis.h
+++ b/libsolidity/inlineasm/AsmAnalysis.h
@@ -22,6 +22,8 @@
#include <libsolidity/interface/Exceptions.h>
+#include <libsolidity/inlineasm/AsmScope.h>
+
#include <libjulia/backends/evm/AbstractAssembly.h>
#include <boost/variant.hpp>
@@ -51,9 +53,6 @@ struct FunctionCall;
struct Switch;
using Statement = boost::variant<Instruction, Literal, Label, StackAssignment, Identifier, Assignment, FunctionCall, FunctionalInstruction, VariableDeclaration, FunctionDefinition, Switch, Block>;
-
-struct Scope;
-
struct AsmAnalysisInfo;
/**
@@ -97,10 +96,14 @@ private:
Scope& scope(assembly::Block const* _block);
void expectValidType(std::string const& type, SourceLocation const& _location);
+ void warnOnFutureInstruction(solidity::Instruction _instr, SourceLocation const& _location);
int m_stackHeight = 0;
julia::ExternalIdentifierAccess::Resolver m_resolver;
Scope* m_currentScope = nullptr;
+ /// Variables that are active at the current point in assembly (as opposed to
+ /// "part of the scope but not yet declared")
+ std::set<Scope::Variable const*> m_activeVariables;
AsmAnalysisInfo& m_info;
ErrorReporter& m_errorReporter;
bool m_julia = false;
diff --git a/libsolidity/inlineasm/AsmParser.cpp b/libsolidity/inlineasm/AsmParser.cpp
index 68a9cb03..f9b073ba 100644
--- a/libsolidity/inlineasm/AsmParser.cpp
+++ b/libsolidity/inlineasm/AsmParser.cpp
@@ -174,6 +174,19 @@ assembly::Case Parser::parseCase()
assembly::Statement Parser::parseExpression()
{
Statement operation = parseElementaryOperation(true);
+ if (operation.type() == typeid(Instruction))
+ {
+ Instruction const& instr = boost::get<Instruction>(operation);
+ int args = instructionInfo(instr.instruction).args;
+ if (args > 0 && currentToken() != Token::LParen)
+ fatalParserError(string(
+ "Expected token \"(\" (\"" +
+ instructionNames().at(instr.instruction) +
+ "\" expects " +
+ boost::lexical_cast<string>(args) +
+ " arguments)"
+ ));
+ }
if (currentToken() == Token::LParen)
return parseCall(std::move(operation));
else
diff --git a/libsolidity/inlineasm/AsmScope.cpp b/libsolidity/inlineasm/AsmScope.cpp
index 7a086846..1db5ca41 100644
--- a/libsolidity/inlineasm/AsmScope.cpp
+++ b/libsolidity/inlineasm/AsmScope.cpp
@@ -46,7 +46,7 @@ bool Scope::registerFunction(string const& _name, std::vector<JuliaType> const&
{
if (exists(_name))
return false;
- identifiers[_name] = Function(_arguments, _returns);
+ identifiers[_name] = Function{_arguments, _returns};
return true;
}
diff --git a/libsolidity/inlineasm/AsmScope.h b/libsolidity/inlineasm/AsmScope.h
index ad321f77..de9119e0 100644
--- a/libsolidity/inlineasm/AsmScope.h
+++ b/libsolidity/inlineasm/AsmScope.h
@@ -65,27 +65,12 @@ struct Scope
using JuliaType = std::string;
using LabelID = size_t;
- struct Variable
- {
- /// Used during code generation to store the stack height. @todo move there.
- int stackHeight = 0;
- /// Used during analysis to check whether we already passed the declaration inside the block.
- /// @todo move there.
- bool active = false;
- JuliaType type;
- };
-
- struct Label
- {
- boost::optional<LabelID> id;
- };
-
+ struct Variable { JuliaType type; };
+ struct Label { };
struct Function
{
- Function(std::vector<JuliaType> const& _arguments, std::vector<JuliaType> const& _returns): arguments(_arguments), returns(_returns) {}
std::vector<JuliaType> arguments;
std::vector<JuliaType> returns;
- boost::optional<LabelID> id;
};
using Identifier = boost::variant<Variable, Label, Function>;
diff --git a/libsolidity/interface/AssemblyStack.cpp b/libsolidity/interface/AssemblyStack.cpp
index 75877881..7dc1edc7 100644
--- a/libsolidity/interface/AssemblyStack.cpp
+++ b/libsolidity/interface/AssemblyStack.cpp
@@ -77,7 +77,7 @@ bool AssemblyStack::analyzeParsed()
return m_analysisSuccessful;
}
-eth::LinkerObject AssemblyStack::assemble(Machine _machine) const
+MachineAssemblyObject AssemblyStack::assemble(Machine _machine) const
{
solAssert(m_analysisSuccessful, "");
solAssert(m_parserResult, "");
@@ -87,21 +87,29 @@ eth::LinkerObject AssemblyStack::assemble(Machine _machine) const
{
case Machine::EVM:
{
+ MachineAssemblyObject object;
eth::Assembly assembly;
assembly::CodeGenerator::assemble(*m_parserResult, *m_analysisInfo, assembly);
- return assembly.assemble();
+ object.bytecode = make_shared<eth::LinkerObject>(assembly.assemble());
+ ostringstream tmp;
+ assembly.stream(tmp);
+ object.assembly = tmp.str();
+ return object;
}
case Machine::EVM15:
{
+ MachineAssemblyObject object;
julia::EVMAssembly assembly(true);
julia::CodeTransform(assembly, *m_analysisInfo, true).run(*m_parserResult);
- return assembly.finalize();
+ object.bytecode = make_shared<eth::LinkerObject>(assembly.finalize());
+ /// TOOD: fill out text representation
+ return object;
}
case Machine::eWasm:
solUnimplemented("eWasm backend is not yet implemented.");
}
// unreachable
- return eth::LinkerObject();
+ return MachineAssemblyObject();
}
string AssemblyStack::print() const
diff --git a/libsolidity/interface/AssemblyStack.h b/libsolidity/interface/AssemblyStack.h
index ee2a334c..2ae596ed 100644
--- a/libsolidity/interface/AssemblyStack.h
+++ b/libsolidity/interface/AssemblyStack.h
@@ -38,6 +38,12 @@ struct AsmAnalysisInfo;
struct Block;
}
+struct MachineAssemblyObject
+{
+ std::shared_ptr<eth::LinkerObject> bytecode;
+ std::string assembly;
+};
+
/*
* Full assembly stack that can support EVM-assembly and JULIA as input and EVM, EVM1.5 and
* eWasm as output.
@@ -64,7 +70,7 @@ public:
bool analyze(assembly::Block const& _block, Scanner const* _scanner = nullptr);
/// Run the assembly step (should only be called after parseAndAnalyze).
- eth::LinkerObject assemble(Machine _machine) const;
+ MachineAssemblyObject assemble(Machine _machine) const;
/// @returns the errors generated during parsing, analysis (and potentially assembly).
ErrorList const& errors() const { return m_errors; }
diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp
index aca9ce39..b09108b0 100644
--- a/libsolidity/interface/CompilerStack.cpp
+++ b/libsolidity/interface/CompilerStack.cpp
@@ -481,6 +481,14 @@ Json::Value const& CompilerStack::natspec(Contract const& _contract, Documentati
return *(*doc);
}
+Json::Value CompilerStack::functionHashes(ContractDefinition const& _contract)
+{
+ Json::Value functionHashes(Json::objectValue);
+ for (auto const& it: _contract.interfaceFunctions())
+ functionHashes[it.second->externalSignature()] = toHex(it.first.ref());
+ return functionHashes;
+}
+
string const& CompilerStack::onChainMetadata(string const& _contractName) const
{
if (m_stackState != CompilationSuccessful)
diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h
index bffdeabd..3250429b 100644
--- a/libsolidity/interface/CompilerStack.h
+++ b/libsolidity/interface/CompilerStack.h
@@ -177,6 +177,9 @@ public:
/// @param type The type of the documentation to get.
/// Can be one of 4 types defined at @c DocumentationType
Json::Value const& natspec(std::string const& _contractName, DocumentationType _type) const;
+
+ Json::Value functionHashes(ContractDefinition const& _contract);
+
std::string const& onChainMetadata(std::string const& _contractName) const;
void useMetadataLiteralSources(bool _metadataLiteralSources) { m_metadataLiteralSources = _metadataLiteralSources; }
diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp
index ec27f89b..88b41f20 100644
--- a/libsolidity/parsing/Parser.cpp
+++ b/libsolidity/parsing/Parser.cpp
@@ -347,8 +347,12 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _forceEmptyN
else if (Token::isVisibilitySpecifier(token))
{
if (result.visibility != Declaration::Visibility::Default)
- fatalParserError(string("Multiple visibility specifiers."));
- result.visibility = parseVisibilitySpecifier(token);
+ {
+ parserError(string("Multiple visibility specifiers."));
+ m_scanner->next();
+ }
+ else
+ result.visibility = parseVisibilitySpecifier(token);
}
else
break;
@@ -501,8 +505,12 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
if (_options.isStateVariable && Token::isVariableVisibilitySpecifier(token))
{
if (visibility != Declaration::Visibility::Default)
- fatalParserError(string("Visibility already specified."));
- visibility = parseVisibilitySpecifier(token);
+ {
+ parserError(string("Visibility already specified."));
+ m_scanner->next();
+ }
+ else
+ visibility = parseVisibilitySpecifier(token);
}
else
{
@@ -513,14 +521,15 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
else if (_options.allowLocationSpecifier && Token::isLocationSpecifier(token))
{
if (location != VariableDeclaration::Location::Default)
- fatalParserError(string("Location already specified."));
- if (!type)
- fatalParserError(string("Location specifier needs explicit type name."));
- location = (
- token == Token::Memory ?
- VariableDeclaration::Location::Memory :
- VariableDeclaration::Location::Storage
- );
+ parserError(string("Location already specified."));
+ else if (!type)
+ parserError(string("Location specifier needs explicit type name."));
+ else
+ location = (
+ token == Token::Memory ?
+ VariableDeclaration::Location::Memory :
+ VariableDeclaration::Location::Storage
+ );
}
else
break;
@@ -702,7 +711,7 @@ ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar)
else if (token == Token::Var)
{
if (!_allowVar)
- fatalParserError(string("Expected explicit type name."));
+ parserError(string("Expected explicit type name."));
m_scanner->next();
}
else if (token == Token::Function)
@@ -1328,16 +1337,21 @@ pair<vector<ASTPointer<Expression>>, vector<ASTPointer<ASTString>>> Parser::pars
{
// call({arg1 : 1, arg2 : 2 })
expectToken(Token::LBrace);
+
+ bool first = true;
while (m_scanner->currentToken() != Token::RBrace)
{
+ if (!first)
+ expectToken(Token::Comma);
+
+ if (m_scanner->currentToken() == Token::RBrace)
+ fatalParserError("Unexpected trailing comma.");
+
ret.second.push_back(expectIdentifierToken());
expectToken(Token::Colon);
ret.first.push_back(parseExpression());
- if (m_scanner->currentToken() == Token::Comma)
- expectToken(Token::Comma);
- else
- break;
+ first = false;
}
expectToken(Token::RBrace);
}
diff --git a/scripts/bytecodecompare/storebytecode.bat b/scripts/bytecodecompare/storebytecode.bat
index 969a42a4..aa829d20 100644
--- a/scripts/bytecodecompare/storebytecode.bat
+++ b/scripts/bytecodecompare/storebytecode.bat
@@ -20,7 +20,7 @@ REM Copyright (c) 2017 solidity contributors.
REM ---------------------------------------------------------------------------
set CONFIGURATION=%1
-set COMMIT=%2
+set DIRECTORY=%2
mkdir bytecode
cd bytecode
@@ -33,8 +33,8 @@ git config user.name "travis"
git config user.email "chris@ethereum.org"
git clean -f -d -x
-mkdir %COMMIT%
-set REPORT=%COMMIT%/windows.txt
+if not exist %DIRECTORY% mkdir %DIRECTORY%
+set REPORT=%DIRECTORY%/windows.txt
cp ../report.txt %REPORT%
git add %REPORT%
git commit -a -m "Added report."
diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp
index b2e257ee..e37922c6 100644
--- a/solc/CommandLineInterface.cpp
+++ b/solc/CommandLineInterface.cpp
@@ -23,6 +23,7 @@
#include "CommandLineInterface.h"
#include "solidity/BuildInfo.h"
+#include "license.h"
#include <libsolidity/interface/Version.h>
#include <libsolidity/parsing/Scanner.h>
@@ -94,6 +95,7 @@ static string const g_strHelp = "help";
static string const g_strInputFile = "input-file";
static string const g_strInterface = "interface";
static string const g_strJulia = "julia";
+static string const g_strLicense = "license";
static string const g_strLibraries = "libraries";
static string const g_strLink = "link";
static string const g_strMetadata = "metadata";
@@ -163,6 +165,7 @@ static set<string> const g_combinedJsonArgs
g_strNatspecUser,
g_strNatspecDev,
g_strOpcodes,
+ g_strSignatureHashes,
g_strSrcMap,
g_strSrcMapRuntime
};
@@ -186,6 +189,13 @@ static void version()
exit(0);
}
+static void license()
+{
+ // This is a static variable generated by cmake from LICENSE.txt
+ cout << licenseText << endl;
+ exit(0);
+}
+
static bool needsHumanTargetedStdout(po::variables_map const& _args)
{
if (_args.count(g_argGas))
@@ -510,8 +520,12 @@ void CommandLineInterface::createFile(string const& _fileName, string const& _da
bool CommandLineInterface::parseArguments(int _argc, char** _argv)
{
// Declare the supported options.
- po::options_description desc(
- R"(solc, the Solidity commandline compiler.
+ po::options_description desc(R"(solc, the Solidity commandline compiler.
+
+This program comes with ABSOLUTELY NO WARRANTY. This is free software, and you
+are welcome to redistribute it under certain conditions. See 'solc --license'
+for details.
+
Usage: solc [options] [input_file...]
Compiles the given Solidity input files (or the standard input if none given or
"-" is used as a file name) and outputs the components specified in the options
@@ -523,10 +537,12 @@ Example:
Allowed options)",
po::options_description::m_default_line_length,
- po::options_description::m_default_line_length - 23);
+ po::options_description::m_default_line_length - 23
+ );
desc.add_options()
(g_argHelp.c_str(), "Show help message and exit.")
(g_argVersion.c_str(), "Show version and exit.")
+ (g_strLicense.c_str(), "Show licensing information and exit.")
(g_argOptimize.c_str(), "Enable bytecode optimizer.")
(
g_argOptimizeRuns.c_str(),
@@ -633,6 +649,12 @@ Allowed options)",
return false;
}
+ if (m_args.count(g_strLicense))
+ {
+ license();
+ return false;
+ }
+
if (m_args.count(g_argCombinedJson))
{
vector<string> requests;
@@ -866,6 +888,8 @@ void CommandLineInterface::handleCombinedJSON()
auto map = m_compiler->runtimeSourceMapping(contractName);
contractData[g_strSrcMapRuntime] = map ? *map : "";
}
+ if (requests.count(g_strSignatureHashes))
+ contractData[g_strSignatureHashes] = m_compiler->functionHashes(m_compiler->contractDefinition(contractName));
if (requests.count(g_strNatspecDev))
contractData[g_strNatspecDev] = dev::jsonCompactPrint(m_compiler->natspec(contractName, DocumentationType::NatspecDev));
if (requests.count(g_strNatspecUser))
@@ -1082,9 +1106,14 @@ bool CommandLineInterface::assemble(
"eWasm";
cout << endl << "======= " << src.first << " (" << machine << ") =======" << endl;
AssemblyStack& stack = assemblyStacks[src.first];
+
+ cout << endl << "Pretty printed source:" << endl;
+ cout << stack.print() << endl;
+
+ MachineAssemblyObject object;
try
{
- cout << stack.assemble(_targetMachine).toHex() << endl;
+ object = stack.assemble(_targetMachine);
}
catch (Exception const& _exception)
{
@@ -1096,7 +1125,18 @@ bool CommandLineInterface::assemble(
cerr << "Unknown exception while assembling." << endl;
return false;
}
- cout << stack.print() << endl;
+
+ cout << endl << "Binary representation:" << endl;
+ if (object.bytecode)
+ cout << object.bytecode->toHex() << endl;
+ else
+ cerr << "No binary representation found." << endl;
+
+ cout << endl << "Text representation:" << endl;
+ if (!object.assembly.empty())
+ cout << object.assembly << endl;
+ else
+ cerr << "No text representation found." << endl;
}
return true;
diff --git a/solc/jsonCompiler.cpp b/solc/jsonCompiler.cpp
index 1505a43d..c01c8061 100644
--- a/solc/jsonCompiler.cpp
+++ b/solc/jsonCompiler.cpp
@@ -41,6 +41,8 @@
#include <libsolidity/ast/ASTJsonConverter.h>
#include <libsolidity/interface/Version.h>
+#include "license.h"
+
using namespace std;
using namespace dev;
using namespace solidity;
@@ -86,14 +88,6 @@ ReadFile::Callback wrapReadCallback(CStyleReadFileCallback _readCallback = nullp
return readCallback;
}
-Json::Value functionHashes(ContractDefinition const& _contract)
-{
- Json::Value functionHashes(Json::objectValue);
- for (auto const& it: _contract.interfaceFunctions())
- functionHashes[it.second->externalSignature()] = toHex(it.first.ref());
- return functionHashes;
-}
-
/// Translates a gas value as a string to a JSON number or null
Json::Value gasToJson(Json::Value const& _value)
{
@@ -198,7 +192,7 @@ string compile(StringMap const& _sources, bool _optimize, CStyleReadFileCallback
contractData["runtimeBytecode"] = compiler.runtimeObject(contractName).toHex();
contractData["opcodes"] = solidity::disassemble(compiler.object(contractName).bytecode);
contractData["metadata"] = compiler.onChainMetadata(contractName);
- contractData["functionHashes"] = functionHashes(compiler.contractDefinition(contractName));
+ contractData["functionHashes"] = compiler.functionHashes(compiler.contractDefinition(contractName));
contractData["gasEstimates"] = estimateGas(compiler, contractName);
auto sourceMap = compiler.sourceMapping(contractName);
contractData["srcmap"] = sourceMap ? *sourceMap : "";
@@ -305,6 +299,11 @@ static string s_outputBuffer;
extern "C"
{
+extern char const* license()
+{
+ /// TOOD: include the copyright information on the top.
+ return licenseText;
+}
extern char const* version()
{
return VersionString.c_str();
diff --git a/test/liblll/EndToEndTest.cpp b/test/liblll/EndToEndTest.cpp
index c7c1fd3b..3928ff45 100644
--- a/test/liblll/EndToEndTest.cpp
+++ b/test/liblll/EndToEndTest.cpp
@@ -272,13 +272,82 @@ BOOST_AUTO_TEST_CASE(assembly_codecopy)
(seq
(lit 0x00 "abcdef")
(asm
- 0x06 0x16 0x20 codecopy
+ 0x06 6 codesize sub 0x20 codecopy
0x20 0x20 return)))
)";
compileAndRun(sourceCode);
BOOST_CHECK(callFallback() == encodeArgs(string("abcdef")));
}
+BOOST_AUTO_TEST_CASE(zeroarg_macro)
+{
+ char const* sourceCode = R"(
+ (returnlll
+ (seq
+ (def 'zeroarg () (seq (mstore 0 0x1234) (return 0 32)))
+ (zeroarg)))
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callFallback() == encodeArgs(u256(0x1234)));
+}
+
+BOOST_AUTO_TEST_CASE(keccak256_32bytes)
+{
+ char const* sourceCode = R"(
+ (returnlll
+ (seq
+ (mstore 0x00 0x01)
+ (return (keccak256 0x00 0x20))))
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callFallback() == encodeArgs(
+ fromHex("b10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6")));
+}
+
+BOOST_AUTO_TEST_CASE(sha3_two_args)
+{
+ char const* sourceCode = R"(
+ (returnlll
+ (seq
+ (mstore 0x00 0x01)
+ (return (sha3 0x00 0x20))))
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callFallback() == encodeArgs(
+ fromHex("b10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6")));
+}
+
+BOOST_AUTO_TEST_CASE(sha3_one_arg)
+{
+ char const* sourceCode = R"(
+ (returnlll
+ (return (sha3 0x01)))
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callFallback() == encodeArgs(
+ fromHex("b10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6")));
+}
+
+BOOST_AUTO_TEST_CASE(shift_left)
+{
+ char const* sourceCode = R"(
+ (returnlll
+ (return (shl 1 8)))
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callFallback() == encodeArgs(u256(256)));
+}
+
+BOOST_AUTO_TEST_CASE(shift_right)
+{
+ char const* sourceCode = R"(
+ (returnlll
+ (return (shr 65536 8)))
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callFallback() == encodeArgs(u256(256)));
+}
+
BOOST_AUTO_TEST_SUITE_END()
}
diff --git a/test/liblll/Parser.cpp b/test/liblll/Parser.cpp
index 0d5d9ea5..fc977b81 100644
--- a/test/liblll/Parser.cpp
+++ b/test/liblll/Parser.cpp
@@ -171,7 +171,13 @@ BOOST_AUTO_TEST_CASE(list)
BOOST_CHECK_EQUAL(parse(text), R"(( 1234 ))");
BOOST_CHECK(successParse("( 1234 5467 )"));
- BOOST_CHECK(!successParse("()"));
+ BOOST_CHECK(successParse("()"));
+}
+
+BOOST_AUTO_TEST_CASE(macro_with_zero_args)
+{
+ char const* text = "(def 'zeroargs () (asm INVALID))";
+ BOOST_CHECK(successParse(text));
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/test/libsolidity/ASTJSON.cpp b/test/libsolidity/ASTJSON.cpp
index df7fac51..4fb4f20c 100644
--- a/test/libsolidity/ASTJSON.cpp
+++ b/test/libsolidity/ASTJSON.cpp
@@ -228,6 +228,36 @@ BOOST_AUTO_TEST_CASE(function_type)
BOOST_CHECK_EQUAL(funType["attributes"]["visibility"], "external");
}
+BOOST_AUTO_TEST_CASE(documentation)
+{
+ CompilerStack c;
+ c.addSource("a", "/**This contract is empty*/ contract C {}");
+ c.addSource("b",
+ "/**This contract is empty"
+ " and has a line-breaking comment.*/"
+ "contract C {}"
+ );
+ c.parseAndAnalyze();
+ map<string, unsigned> sourceIndices;
+ sourceIndices["a"] = 0;
+ sourceIndices["b"] = 1;
+ Json::Value astJsonA = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a"));
+ Json::Value documentationA = astJsonA["children"][0]["attributes"]["documentation"];
+ BOOST_CHECK_EQUAL(documentationA, "This contract is empty");
+ Json::Value astJsonB = ASTJsonConverter(true, sourceIndices).toJson(c.ast("b"));
+ Json::Value documentationB = astJsonB["children"][0]["attributes"]["documentation"];
+ BOOST_CHECK_EQUAL(documentationB, "This contract is empty and has a line-breaking comment.");
+ //same tests for non-legacy mode
+ astJsonA = ASTJsonConverter(false, sourceIndices).toJson(c.ast("a"));
+ documentationA = astJsonA["nodes"][0]["documentation"];
+ BOOST_CHECK_EQUAL(documentationA, "This contract is empty");
+ astJsonB = ASTJsonConverter(false, sourceIndices).toJson(c.ast("b"));
+ documentationB = astJsonB["nodes"][0]["documentation"];
+ BOOST_CHECK_EQUAL(documentationB, "This contract is empty and has a line-breaking comment.");
+
+}
+
+
BOOST_AUTO_TEST_SUITE_END()
}
diff --git a/test/libsolidity/InlineAssembly.cpp b/test/libsolidity/InlineAssembly.cpp
index d2d320db..aae6dacd 100644
--- a/test/libsolidity/InlineAssembly.cpp
+++ b/test/libsolidity/InlineAssembly.cpp
@@ -213,6 +213,16 @@ BOOST_AUTO_TEST_CASE(functional)
BOOST_CHECK(successParse("{ let x := 2 add(7, mul(6, x)) mul(7, 8) add =: x }"));
}
+BOOST_AUTO_TEST_CASE(functional_partial)
+{
+ CHECK_PARSE_ERROR("{ let x := byte }", ParserError, "Expected token \"(\"");
+}
+
+BOOST_AUTO_TEST_CASE(functional_partial_success)
+{
+ BOOST_CHECK(successParse("{ let x := byte(1, 2) }"));
+}
+
BOOST_AUTO_TEST_CASE(functional_assignment)
{
BOOST_CHECK(successParse("{ let x := 2 x := 7 }"));
@@ -258,7 +268,7 @@ BOOST_AUTO_TEST_CASE(switch_duplicate_case)
BOOST_AUTO_TEST_CASE(switch_invalid_expression)
{
CHECK_PARSE_ERROR("{ switch {} default {} }", ParserError, "Literal, identifier or instruction expected.");
- CHECK_PARSE_ERROR("{ 1 2 switch mul default {} }", ParserError, "Instructions are not supported as expressions for switch.");
+ CHECK_PARSE_ERROR("{ switch calldatasize default {} }", ParserError, "Instructions are not supported as expressions for switch.");
}
BOOST_AUTO_TEST_CASE(switch_default_before_case)
@@ -542,6 +552,36 @@ BOOST_AUTO_TEST_CASE(keccak256)
BOOST_CHECK(successAssemble("{ pop(sha3(0, 0)) }"));
}
+BOOST_AUTO_TEST_CASE(returndatasize)
+{
+ BOOST_CHECK(successAssemble("{ let r := returndatasize }"));
+}
+
+BOOST_AUTO_TEST_CASE(returndatasize_functional)
+{
+ BOOST_CHECK(successAssemble("{ let r := returndatasize() }"));
+}
+
+BOOST_AUTO_TEST_CASE(returndatacopy)
+{
+ BOOST_CHECK(successAssemble("{ 64 32 0 returndatacopy }"));
+}
+
+BOOST_AUTO_TEST_CASE(returndatacopy_functional)
+{
+ BOOST_CHECK(successAssemble("{ returndatacopy(0, 32, 64) }"));
+}
+
+BOOST_AUTO_TEST_CASE(staticcall)
+{
+ BOOST_CHECK(successAssemble("{ pop(staticcall(10000, 0x123, 64, 0x10, 128, 0x10)) }"));
+}
+
+BOOST_AUTO_TEST_CASE(create2)
+{
+ BOOST_CHECK(successAssemble("{ pop(create2(10, 0x123, 32, 64)) }"));
+}
+
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_SUITE_END()
diff --git a/test/libsolidity/JSONCompiler.cpp b/test/libsolidity/JSONCompiler.cpp
index 6aec59ab..f5154395 100644
--- a/test/libsolidity/JSONCompiler.cpp
+++ b/test/libsolidity/JSONCompiler.cpp
@@ -90,11 +90,11 @@ BOOST_AUTO_TEST_CASE(basic_compilation)
BOOST_CHECK(result["sources"]["fileA"].isObject());
BOOST_CHECK(result["sources"]["fileA"]["AST"].isObject());
BOOST_CHECK(dev::jsonCompactPrint(result["sources"]["fileA"]["AST"]) ==
- "{\"attributes\":{\"absolutePath\":\"fileA\",\"exportedSymbols\":{\"A\":[1]}},"
- "\"children\":[{\"attributes\":{\"baseContracts\":[null],\"contractDependencies\":[null],"
- "\"contractKind\":\"contract\",\"fullyImplemented\":true,\"linearizedBaseContracts\":[1],"
- "\"name\":\"A\",\"nodes\":[null],\"scope\":2},\"id\":1,\"name\":\"ContractDefinition\","
- "\"src\":\"0:14:0\"}],\"id\":2,\"name\":\"SourceUnit\",\"src\":\"0:14:0\"}");
+ "{\"attributes\":{\"absolutePath\":\"fileA\",\"exportedSymbols\":{\"A\":[1]}},"
+ "\"children\":[{\"attributes\":{\"baseContracts\":[null],\"contractDependencies\":[null],"
+ "\"contractKind\":\"contract\",\"documentation\":null,\"fullyImplemented\":true,\"linearizedBaseContracts\":[1],"
+ "\"name\":\"A\",\"nodes\":[null],\"scope\":2},\"id\":1,\"name\":\"ContractDefinition\","
+ "\"src\":\"0:14:0\"}],\"id\":2,\"name\":\"SourceUnit\",\"src\":\"0:14:0\"}");
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp
index db5b9bf8..0b3cb481 100644
--- a/test/libsolidity/SolidityNameAndTypeResolution.cpp
+++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp
@@ -193,11 +193,16 @@ CHECK_ERROR_OR_WARNING(text, type, substring, false, false)
#define CHECK_ERROR_ALLOW_MULTI(text, type, substring) \
CHECK_ERROR_OR_WARNING(text, type, substring, false, true)
-// [checkWarning(text, type, substring)] asserts that the compilation down to typechecking
-// emits a warning of type [type] and with a message containing [substring].
+// [checkWarning(text, substring)] asserts that the compilation down to typechecking
+// emits a warning and with a message containing [substring].
#define CHECK_WARNING(text, substring) \
CHECK_ERROR_OR_WARNING(text, Warning, substring, true, false)
+// [checkWarningAllowMulti(text, substring)] aserts that the compilation down to typechecking
+// emits a warning and with a message containing [substring].
+#define CHECK_WARNING_ALLOW_MULTI(text, substring) \
+CHECK_ERROR_OR_WARNING(text, Warning, substring, true, true)
+
// [checkSuccess(text)] asserts that the compilation down to typechecking succeeds.
#define CHECK_SUCCESS(text) do { BOOST_CHECK(success((text))); } while(0)
@@ -2417,6 +2422,16 @@ BOOST_AUTO_TEST_CASE(invalid_utf8_explicit)
CHECK_ERROR(sourceCode, TypeError, "Explicit type conversion not allowed");
}
+BOOST_AUTO_TEST_CASE(large_utf8_codepoint)
+{
+ char const* sourceCode = R"(
+ contract C {
+ string s = "\xf0\x9f\xa6\x84";
+ }
+ )";
+ CHECK_SUCCESS(sourceCode);
+}
+
BOOST_AUTO_TEST_CASE(string_index)
{
char const* sourceCode = R"(
@@ -5780,6 +5795,43 @@ BOOST_AUTO_TEST_CASE(no_unused_inline_asm)
CHECK_SUCCESS_NO_WARNINGS(text);
}
+BOOST_AUTO_TEST_CASE(callable_crash)
+{
+ char const* text = R"(
+ contract C {
+ struct S { uint a; bool x; }
+ S public s;
+ function C() {
+ 3({a: 1, x: true});
+ }
+ }
+ )";
+ CHECK_ERROR(text, TypeError, "Type is not callable");
+}
+
+BOOST_AUTO_TEST_CASE(returndatacopy_as_variable)
+{
+ char const* text = R"(
+ contract c { function f() { uint returndatasize; assembly { returndatasize }}}
+ )";
+ CHECK_WARNING_ALLOW_MULTI(text, "Variable is shadowed in inline assembly by an instruction of the same name");
+}
+
+BOOST_AUTO_TEST_CASE(create2_as_variable)
+{
+ char const* text = R"(
+ contract c { function f() { uint create2; assembly { create2(0, 0, 0, 0) }}}
+ )";
+ CHECK_WARNING_ALLOW_MULTI(text, "Variable is shadowed in inline assembly by an instruction of the same name");
+}
+
+BOOST_AUTO_TEST_CASE(shadowing_warning_can_be_removed)
+{
+ char const* text = R"(
+ contract C {function f() {assembly {}}}
+ )";
+ CHECK_SUCCESS_NO_WARNINGS(text);
+}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/test/libsolidity/SolidityOptimizer.cpp b/test/libsolidity/SolidityOptimizer.cpp
index bdcdacff..7afbe04e 100644
--- a/test/libsolidity/SolidityOptimizer.cpp
+++ b/test/libsolidity/SolidityOptimizer.cpp
@@ -1189,6 +1189,32 @@ BOOST_AUTO_TEST_CASE(clear_unreachable_code)
);
}
+BOOST_AUTO_TEST_CASE(peephole_double_push)
+{
+ AssemblyItems items{
+ u256(0),
+ u256(0),
+ u256(5),
+ u256(5),
+ u256(4),
+ u256(5)
+ };
+ AssemblyItems expectation{
+ u256(0),
+ Instruction::DUP1,
+ u256(5),
+ Instruction::DUP1,
+ u256(4),
+ u256(5)
+ };
+ PeepholeOptimiser peepOpt(items);
+ BOOST_REQUIRE(peepOpt.optimise());
+ BOOST_CHECK_EQUAL_COLLECTIONS(
+ items.begin(), items.end(),
+ expectation.begin(), expectation.end()
+ );
+}
+
BOOST_AUTO_TEST_CASE(computing_constants)
{
char const* sourceCode = R"(
diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp
index 31dfada9..27231b9b 100644
--- a/test/libsolidity/SolidityParser.cpp
+++ b/test/libsolidity/SolidityParser.cpp
@@ -199,6 +199,17 @@ BOOST_AUTO_TEST_CASE(missing_argument_in_named_args)
CHECK_PARSE_ERROR(text, "Expected primary expression");
}
+BOOST_AUTO_TEST_CASE(trailing_comma_in_named_args)
+{
+ char const* text = R"(
+ contract test {
+ function a(uint a, uint b, uint c) returns (uint r) { r = a * 100 + b * 10 + c * 1; }
+ function b() returns (uint r) { r = a({a: 1, b: 2, c: 3, }); }
+ }
+ )";
+ CHECK_PARSE_ERROR(text, "Unexpected trailing comma");
+}
+
BOOST_AUTO_TEST_CASE(two_exact_functions)
{
char const* text = R"(
diff --git a/test/libsolidity/StandardCompiler.cpp b/test/libsolidity/StandardCompiler.cpp
index 050ca500..35644a4d 100644
--- a/test/libsolidity/StandardCompiler.cpp
+++ b/test/libsolidity/StandardCompiler.cpp
@@ -201,11 +201,12 @@ BOOST_AUTO_TEST_CASE(basic_compilation)
BOOST_CHECK(dev::test::bytecodeSansMetadata(contract["evm"]["bytecode"]["object"].asString()) ==
"60606040523415600b57fe5b5b60338060196000396000f30060606040525bfe00");
BOOST_CHECK(contract["evm"]["assembly"].isString());
- BOOST_CHECK(contract["evm"]["assembly"].asString() ==
+ BOOST_CHECK(contract["evm"]["assembly"].asString().find(
" /* \"fileA\":0:14 contract A { } */\n mstore(0x40, 0x60)\n jumpi(tag_1, iszero(callvalue))\n"
" invalid\ntag_1:\ntag_2:\n dataSize(sub_0)\n dup1\n dataOffset(sub_0)\n 0x0\n codecopy\n 0x0\n"
" return\nstop\n\nsub_0: assembly {\n /* \"fileA\":0:14 contract A { } */\n"
- " mstore(0x40, 0x60)\n tag_1:\n invalid\n}\n");
+ " mstore(0x40, 0x60)\n tag_1:\n invalid\n\n"
+ " auxdata: 0xa165627a7a72305820") != std::string::npos);
BOOST_CHECK(contract["evm"]["gasEstimates"].isObject());
BOOST_CHECK(dev::jsonCompactPrint(contract["evm"]["gasEstimates"]) ==
"{\"creation\":{\"codeDepositCost\":\"10200\",\"executionCost\":\"62\",\"totalCost\":\"10262\"}}");
@@ -217,7 +218,7 @@ BOOST_AUTO_TEST_CASE(basic_compilation)
BOOST_CHECK(dev::jsonCompactPrint(result["sources"]["fileA"]["legacyAST"]) ==
"{\"attributes\":{\"absolutePath\":\"fileA\",\"exportedSymbols\":{\"A\":[1]}},\"children\":"
"[{\"attributes\":{\"baseContracts\":[null],\"contractDependencies\":[null],\"contractKind\":\"contract\","
- "\"fullyImplemented\":true,\"linearizedBaseContracts\":[1],\"name\":\"A\",\"nodes\":[null],\"scope\":2},"
+ "\"documentation\":null,\"fullyImplemented\":true,\"linearizedBaseContracts\":[1],\"name\":\"A\",\"nodes\":[null],\"scope\":2},"
"\"id\":1,\"name\":\"ContractDefinition\",\"src\":\"0:14:0\"}],\"id\":2,\"name\":\"SourceUnit\",\"src\":\"0:14:0\"}");
}