aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Changelog.md5
-rw-r--r--circle.yml30
-rw-r--r--docs/abi-spec.rst6
-rw-r--r--docs/contracts.rst47
-rw-r--r--docs/miscellaneous.rst13
-rw-r--r--docs/units-and-global-variables.rst3
-rw-r--r--docs/using-the-compiler.rst1
-rw-r--r--libevmasm/Assembly.cpp4
-rw-r--r--libevmasm/Assembly.h5
-rw-r--r--libevmasm/ConstantOptimiser.cpp11
-rw-r--r--libevmasm/ConstantOptimiser.h4
-rw-r--r--libevmasm/GasMeter.cpp32
-rw-r--r--libevmasm/GasMeter.h44
-rw-r--r--libevmasm/PathGasMeter.cpp6
-rw-r--r--libevmasm/PathGasMeter.h8
-rw-r--r--liblll/Compiler.cpp17
-rw-r--r--liblll/Compiler.h9
-rw-r--r--libsolidity/analysis/GlobalContext.cpp1
-rw-r--r--libsolidity/analysis/ReferencesResolver.cpp3
-rw-r--r--libsolidity/analysis/StaticAnalyzer.cpp19
-rw-r--r--libsolidity/analysis/TypeChecker.cpp18
-rw-r--r--libsolidity/analysis/TypeChecker.h9
-rw-r--r--libsolidity/ast/ExperimentalFeatures.h1
-rw-r--r--libsolidity/ast/Types.h3
-rw-r--r--libsolidity/codegen/Compiler.h13
-rw-r--r--libsolidity/codegen/CompilerContext.cpp1
-rw-r--r--libsolidity/codegen/CompilerContext.h11
-rw-r--r--libsolidity/codegen/ContractCompiler.cpp4
-rw-r--r--libsolidity/codegen/ContractCompiler.h4
-rw-r--r--libsolidity/codegen/ExpressionCompiler.cpp28
-rw-r--r--libsolidity/formal/Z3Interface.cpp1
-rw-r--r--libsolidity/inlineasm/AsmAnalysis.cpp50
-rw-r--r--libsolidity/inlineasm/AsmAnalysis.h5
-rw-r--r--libsolidity/interface/AssemblyStack.cpp2
-rw-r--r--libsolidity/interface/AssemblyStack.h7
-rw-r--r--libsolidity/interface/CompilerStack.cpp23
-rw-r--r--libsolidity/interface/CompilerStack.h27
-rw-r--r--libsolidity/interface/EVMVersion.h93
-rw-r--r--libsolidity/interface/GasEstimator.cpp12
-rw-r--r--libsolidity/interface/GasEstimator.h22
-rw-r--r--libsolidity/interface/SourceReferenceFormatter.cpp4
-rw-r--r--libsolidity/interface/StandardCompiler.cpp8
-rw-r--r--lllc/main.cpp4
-rwxr-xr-xscripts/tests.sh43
-rw-r--r--solc/CommandLineInterface.cpp26
-rw-r--r--solc/CommandLineInterface.h3
-rw-r--r--test/ExecutionFramework.cpp9
-rw-r--r--test/ExecutionFramework.h3
-rw-r--r--test/Metadata.cpp3
-rw-r--r--test/RPCSession.cpp18
-rw-r--r--test/TestHelper.cpp25
-rw-r--r--test/TestHelper.h9
-rw-r--r--test/contracts/LLL_ENS.cpp2
-rw-r--r--test/contracts/LLL_ERC20.cpp2
-rw-r--r--test/fuzzer.cpp1
-rw-r--r--test/libevmasm/Optimiser.cpp4
-rw-r--r--test/libjulia/Common.cpp9
-rw-r--r--test/libjulia/Parser.cpp7
-rw-r--r--test/liblll/Compiler.cpp17
-rw-r--r--test/liblll/EndToEndTest.cpp276
-rw-r--r--test/liblll/ExecutionFramework.h2
-rw-r--r--test/libsolidity/ASTJSON.cpp21
-rw-r--r--test/libsolidity/AnalysisFramework.cpp3
-rw-r--r--test/libsolidity/Assembly.cpp17
-rw-r--r--test/libsolidity/GasMeter.cpp11
-rw-r--r--test/libsolidity/Imports.cpp27
-rw-r--r--test/libsolidity/InlineAssembly.cpp12
-rw-r--r--test/libsolidity/Metadata.cpp4
-rw-r--r--test/libsolidity/SolidityABIJSON.cpp2
-rw-r--r--test/libsolidity/SolidityEndToEndTest.cpp12
-rw-r--r--test/libsolidity/SolidityExecutionFramework.h1
-rw-r--r--test/libsolidity/SolidityExpressionCompiler.cpp37
-rw-r--r--test/libsolidity/SolidityNameAndTypeResolution.cpp86
-rw-r--r--test/libsolidity/SolidityNatspecJSON.cpp2
-rw-r--r--test/libsolidity/SolidityParser.cpp30
-rw-r--r--test/libsolidity/StandardCompiler.cpp37
-rw-r--r--test/libsolidity/ViewPureChecker.cpp8
77 files changed, 1053 insertions, 334 deletions
diff --git a/Changelog.md b/Changelog.md
index ebdb2e4e..dd8d6e49 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -3,12 +3,17 @@
Features:
* C99/C++-style scoping rules (instead of JavaScript function scoping) take effect as experimental v0.5.0 feature.
* Code Generator: Assert that ``k != 0`` for ``molmod(a, b, k)`` and ``addmod(a, b, k)`` as experimental 0.5.0 feature.
+ * Code Generator: Do not retain any gas in calls (except if EVM version is set to homestead).
+ * Interface: Provide ability to select target EVM version (homestead or byzantium, with byzantium being the default).
* Standard JSON: Reject badly formatted invalid JSON inputs.
* Type Checker: Disallow uninitialized storage pointers as experimental 0.5.0 feature.
* Support and recommend using ``emit EventName();`` to call events explicitly.
* Syntax Analyser: Do not warn about experimental features if they do not concern code generation.
+ * Syntax Analyser: Do not warn about ``pragma experimental "v0.5.0"`` since it does not affect code generation.
* Syntax Checker: Mark ``throw`` as an error as experimental 0.5.0 feature.
* Syntax Checker: Issue error if no visibility is specified on contract functions as experimental 0.5.0 feature.
+ * Type Checker: disallow combining hex numbers and unit denominations as experimental 0.5.0 feature.
+ * Improved messaging when error spans multiple lines of a sourcefile
Bugfixes:
* Assembly: Raise error on oversized number literals in assembly.
diff --git a/circle.yml b/circle.yml
index add8a815..263cb700 100644
--- a/circle.yml
+++ b/circle.yml
@@ -5,10 +5,6 @@ jobs:
- image: trzeci/emscripten:sdk-tag-1.37.21-64bit
steps:
- checkout
- - run:
- name: Init submodules
- command: |
- git submodule update --init
- restore_cache:
name: Restore Boost build
key: &boost-cache-key emscripten-boost-{{ checksum "scripts/travis-emscripten/install_deps.sh" }}{{ checksum "scripts/travis-emscripten/build_emscripten.sh" }}
@@ -95,23 +91,31 @@ jobs:
apt-get -qq update
apt-get -qy install ccache cmake libboost-all-dev libz3-dev
- run:
- name: Init submodules
- command: |
- git submodule update --init
- - run:
name: Store commit hash and prerelease
command: |
if [ "$CIRCLE_BRANCH" = release -o -n "$CIRCLE_TAG" ]; then echo -n > prerelease.txt; else date -u +"nightly.%Y.%-m.%-d" > prerelease.txt; fi
echo -n "$CIRCLE_SHA1" > commit_hash.txt
- restore_cache:
- key: ccache-{{ arch }}-{{ .Branch }}
- key: ccache-{{ arch }}
- key: ccache
+ key: ccache-{{ arch }}-{{ .Branch }}-{{ .Environment.CIRCLE_SHA1 }}
+ key: ccache-{{ arch }}-{{ .Branch }}-
+ key: ccache-{{ arch }}-develop-
+ key: ccache-{{ arch }}-
+ - run:
+ name: Configure ccache
+ command: ccache -M 200M && ccache -c && ccache -s && ccache -z
- run:
name: Build
- command: ./scripts/build.sh RelWithDebInfo
+ command: |
+ mkdir -p build
+ cd build
+ cmake .. -DCMAKE_BUILD_TYPE=RelWithDebInfo
+ make -j4
+ - run:
+ name: CCache statistics
+ command: ccache -s
- save_cache:
- key: ccache-{{ arch }}-{{ .Branch }}
+ key: ccache-{{ arch }}-{{ .Branch }}-{{ .Environment.CIRCLE_SHA1 }}
+ when: always
paths:
- ~/.ccache
- store_artifacts:
diff --git a/docs/abi-spec.rst b/docs/abi-spec.rst
index 07c8e0ce..4a61d91f 100644
--- a/docs/abi-spec.rst
+++ b/docs/abi-spec.rst
@@ -26,6 +26,10 @@ The first four bytes of the call data for a function call specifies the function
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.
+.. note::
+ The return type of a function is not part of this signature. In :ref:`Solidity's function overloading <overload-function>` return types are not considered. The reason is to keep function call resolution context-independent.
+ The JSON description of the ABI however contains both inputs and outputs. See (the :ref:`JSON ABI <abi_json>`)
+
Argument Encoding
=================
@@ -290,6 +294,8 @@ In effect, a log entry using this ABI is described as:
For all fixed-length Solidity types, the ``EVENT_INDEXED_ARGS`` array contains the 32-byte encoded value directly. However, for *types of dynamic length*, which include ``string``, ``bytes``, and arrays, ``EVENT_INDEXED_ARGS`` will contain the *Keccak hash* of the encoded value, rather than the encoded value directly. This allows applications to efficiently query for values of dynamic-length types (by setting the hash of the encoded value as the topic), but leaves applications unable to decode indexed values they have not queried for. For dynamic-length types, application developers face a trade-off between fast search for predetermined values (if the argument is indexed) and legibility of arbitrary values (which requires that the arguments not be indexed). Developers may overcome this tradeoff and achieve both efficient search and arbitrary legibility by defining events with two arguments — one indexed, one not — intended to hold the same value.
+.. _abi_json:
+
JSON
====
diff --git a/docs/contracts.rst b/docs/contracts.rst
index 416dc649..67d4a249 100644
--- a/docs/contracts.rst
+++ b/docs/contracts.rst
@@ -402,7 +402,7 @@ State variables can be declared as ``constant``. In this case, they have to be
assigned from an expression which is a constant at compile time. Any expression
that accesses storage, blockchain data (e.g. ``now``, ``this.balance`` or
``block.number``) or
-execution data (``msg.gas``) or make calls to external contracts are disallowed. Expressions
+execution data (``msg.value`` or ``gasleft()``) or make calls to external contracts are disallowed. Expressions
that might have a side-effect on memory allocation are allowed, but those that
might have a side-effect on other memory objects are not. The built-in functions
``keccak256``, ``sha256``, ``ripemd160``, ``ecrecover``, ``addmod`` and ``mulmod``
@@ -473,7 +473,7 @@ The following statements are considered modifying the state:
Getter methods are marked ``view``.
.. warning::
- Before version 0.4.17 the compiler didn't enforce that ``view`` is not modifying the state.
+ The compiler does not enforce yet that a ``view`` method is not modifying state. It raises a warning though.
.. index:: ! pure function, function;pure
@@ -503,7 +503,7 @@ In addition to the list of state modifying statements explained above, the follo
}
.. warning::
- Before version 0.4.17 the compiler didn't enforce that ``view`` is not reading the state.
+ Before version 0.4.17 the compiler didn't enforce that ``pure`` is not reading the state.
.. index:: ! fallback function, function;fallback
@@ -523,16 +523,14 @@ Ether (without data). Additionally, in order to receive Ether, the fallback func
must be marked ``payable``. If no such function exists, the contract cannot receive
Ether through regular transactions.
-In such a context, there is usually very little gas available to the function call (to be precise, 2300 gas), so it is important to make fallback functions as cheap as possible. Note that the gas required by a transaction (as opposed to an internal call) that invokes the fallback function is much higher, because each transaction charges an additional amount of 21000 gas or more for things like signature checking.
-
-In particular, the following operations will consume more gas than the stipend provided to a fallback function:
+In the worst case, the fallback function can only rely on 2300 gas being available (for example when send or transfer is used), leaving not much room to perform other operations except basic logging. The following operations will consume more gas than the 2300 gas stipend:
- Writing to storage
- Creating a contract
- Calling an external function which consumes a large amount of gas
- Sending Ether
-Please ensure you test your fallback function thoroughly to ensure the execution cost is less than 2300 gas before deploying a contract.
+Like any function, the fallback function can execute complex operations as long as there is enough gas passed on to it.
.. note::
Even though the fallback function cannot have arguments, one can still use ``msg.data`` to retrieve
@@ -955,6 +953,31 @@ not known in the context of the class where it is used,
although its type is known. This is similar for ordinary
virtual method lookup.
+.. index:: ! constructor
+
+Constructors
+============
+A constructor is an optional function with the same name as the contract which is executed upon contract creation.
+Constructor functions can be either ``public`` or ``internal``.
+
+::
+
+ pragma solidity ^0.4.11;
+
+ contract A {
+ uint public a;
+
+ function A(uint _a) internal {
+ a = _a;
+ }
+ }
+
+ contract B is A(1) {
+ function B() public {}
+ }
+
+A constructor set as ``internal`` causes the contract to be marked as :ref:`abstract <abstract-contract>`.
+
.. index:: ! base;constructor
Arguments for Base Constructors
@@ -1027,11 +1050,13 @@ As an exception, a state variable getter can override a public function.
.. index:: ! contract;abstract, ! abstract contract
+.. _abstract-contract:
+
******************
Abstract Contracts
******************
-Contract functions can lack an implementation as in the following example (note that the function declaration header is terminated by ``;``)::
+Contracts are marked as abstract when at least one of their functions lacks an implementation as in the following example (note that the function declaration header is terminated by ``;``)::
pragma solidity ^0.4.0;
@@ -1039,9 +1064,7 @@ Contract functions can lack an implementation as in the following example (note
function utterance() public returns (bytes32);
}
-Such contracts cannot be compiled (even if they contain
-implemented functions alongside non-implemented functions),
-but they can be used as base contracts::
+Such contracts cannot be compiled (even if they contain implemented functions alongside non-implemented functions), but they can be used as base contracts::
pragma solidity ^0.4.0;
@@ -1065,6 +1088,8 @@ Example of a Function Type (a variable declaration, where the variable is of typ
function(address) external returns (address) foo;
+Abstract contracts decouple the definition of a contract from its implementation providing better extensibility and self-documentation and
+facilitating patterns like the `Template method <https://en.wikipedia.org/wiki/Template_method_pattern>`_ and removing code duplication.
.. index:: ! contract;interface, ! interface contract
diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst
index b5d605ac..075b6be0 100644
--- a/docs/miscellaneous.rst
+++ b/docs/miscellaneous.rst
@@ -192,6 +192,11 @@ These are regular array indices into a list of source files usually called
``"sourceList"``, which is part of the combined-json and the output of
the json / npm compiler.
+.. note ::
+ In the case of instructions that are not associated with any particular source file,
+ the source mapping assigns an integer identifier of ``-1``. This may happen for
+ bytecode sections stemming from compiler-generated inline assembly statements.
+
The source mappings inside the AST use the following
notation:
@@ -230,7 +235,10 @@ Tips and Tricks
* Make your state variables public - the compiler will create :ref:`getters <visibility-and-getters>` for you automatically.
* If you end up checking conditions on input or state a lot at the beginning of your functions, try using :ref:`modifiers`.
* If your contract has a function called ``send`` but you want to use the built-in send-function, use ``address(contractVariable).send(amount)``.
-* Initialise storage structs with a single assignment: ``x = MyStruct({a: 1, b: 2});``
+* Initialize storage structs with a single assignment: ``x = MyStruct({a: 1, b: 2});``
+
+.. note::
+ If the storage struct has tightly packed properties, initialize it with separate assignments: ``x.a = 1; x.b = 2;``. In this way it will be easier for the optimizer to update storage in one go, thus making assignment cheaper.
**********
Cheatsheet
@@ -312,8 +320,9 @@ Global Variables
- ``block.gaslimit`` (``uint``): current block gaslimit
- ``block.number`` (``uint``): current block number
- ``block.timestamp`` (``uint``): current block timestamp
+- ``gasleft() returns (uint256)``: remaining gas
- ``msg.data`` (``bytes``): complete calldata
-- ``msg.gas`` (``uint``): remaining gas
+- ``msg.gas`` (``uint``): remaining gas - deprecated in version 0.4.21 and to be replaced by ``gasleft()``
- ``msg.sender`` (``address``): sender of the message (current call)
- ``msg.value`` (``uint``): number of wei sent with the message
- ``now`` (``uint``): current block timestamp (alias for ``block.timestamp``)
diff --git a/docs/units-and-global-variables.rst b/docs/units-and-global-variables.rst
index cc4d4446..1b58b1e8 100644
--- a/docs/units-and-global-variables.rst
+++ b/docs/units-and-global-variables.rst
@@ -58,8 +58,9 @@ Block and Transaction Properties
- ``block.gaslimit`` (``uint``): current block gaslimit
- ``block.number`` (``uint``): current block number
- ``block.timestamp`` (``uint``): current block timestamp as seconds since unix epoch
+- ``gasleft() returns (uint256)``: remaining gas
- ``msg.data`` (``bytes``): complete calldata
-- ``msg.gas`` (``uint``): remaining gas
+- ``msg.gas`` (``uint``): remaining gas - deprecated in version 0.4.21 and to be replaced by ``gasleft()``
- ``msg.sender`` (``address``): sender of the message (current call)
- ``msg.sig`` (``bytes4``): first four bytes of the calldata (i.e. function identifier)
- ``msg.value`` (``uint``): number of wei sent with the message
diff --git a/docs/using-the-compiler.rst b/docs/using-the-compiler.rst
index 66e3ac35..df30b6b4 100644
--- a/docs/using-the-compiler.rst
+++ b/docs/using-the-compiler.rst
@@ -101,6 +101,7 @@ Input Description
enabled: true,
runs: 500
},
+ evmVersion: "byzantium", // Version of the EVM to compile for. Affects type checking and code generation. Can be homestead, tangerineWhistle, spuriousDragon, byzantium or constantinople
// Metadata settings (optional)
metadata: {
// Use only literal content and not URLs (false by default)
diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp
index b9fedf26..bd4ebf59 100644
--- a/libevmasm/Assembly.cpp
+++ b/libevmasm/Assembly.cpp
@@ -353,7 +353,7 @@ void Assembly::injectStart(AssemblyItem const& _i)
m_items.insert(m_items.begin(), _i);
}
-Assembly& Assembly::optimise(bool _enable, bool _isCreation, size_t _runs)
+Assembly& Assembly::optimise(bool _enable, EVMVersion _evmVersion, bool _isCreation, size_t _runs)
{
OptimiserSettings settings;
settings.isCreation = _isCreation;
@@ -365,6 +365,7 @@ Assembly& Assembly::optimise(bool _enable, bool _isCreation, size_t _runs)
settings.runCSE = true;
settings.runConstantOptimiser = true;
}
+ settings.evmVersion = _evmVersion;
settings.expectedExecutionsPerDeployment = _runs;
optimise(settings);
return *this;
@@ -482,6 +483,7 @@ map<u256, u256> Assembly::optimiseInternal(
ConstantOptimisationMethod::optimiseConstants(
_settings.isCreation,
_settings.isCreation ? 1 : _settings.expectedExecutionsPerDeployment,
+ _settings.evmVersion,
*this,
m_items
);
diff --git a/libevmasm/Assembly.h b/libevmasm/Assembly.h
index 885192e4..367c6daa 100644
--- a/libevmasm/Assembly.h
+++ b/libevmasm/Assembly.h
@@ -23,6 +23,8 @@
#include <libevmasm/LinkerObject.h>
#include <libevmasm/Exceptions.h>
+#include <libsolidity/interface/EVMVersion.h>
+
#include <libdevcore/Common.h>
#include <libdevcore/Assertions.h>
#include <libdevcore/SHA3.h>
@@ -107,6 +109,7 @@ public:
bool runDeduplicate = false;
bool runCSE = false;
bool runConstantOptimiser = false;
+ solidity::EVMVersion evmVersion;
/// This specifies an estimate on how often each opcode in this assembly will be executed,
/// i.e. use a small value to optimise for size and a large value to optimise for runtime gas usage.
size_t expectedExecutionsPerDeployment = 200;
@@ -120,7 +123,7 @@ public:
/// @a _runs specifes an estimate on how often each opcode in this assembly will be executed,
/// i.e. use a small value to optimise for size and a large value to optimise for runtime.
/// If @a _enable is not set, will perform some simple peephole optimizations.
- Assembly& optimise(bool _enable, bool _isCreation = true, size_t _runs = 200);
+ Assembly& optimise(bool _enable, EVMVersion _evmVersion, bool _isCreation = true, size_t _runs = 200);
/// Create a text representation of the assembly.
std::string assemblyString(
diff --git a/libevmasm/ConstantOptimiser.cpp b/libevmasm/ConstantOptimiser.cpp
index 2efd2dc9..d0b6843c 100644
--- a/libevmasm/ConstantOptimiser.cpp
+++ b/libevmasm/ConstantOptimiser.cpp
@@ -29,6 +29,7 @@ using namespace dev::eth;
unsigned ConstantOptimisationMethod::optimiseConstants(
bool _isCreation,
size_t _runs,
+ solidity::EVMVersion _evmVersion,
Assembly& _assembly,
AssemblyItems& _items
)
@@ -48,6 +49,7 @@ unsigned ConstantOptimisationMethod::optimiseConstants(
params.multiplicity = it.second;
params.isCreation = _isCreation;
params.runs = _runs;
+ params.evmVersion = _evmVersion;
LiteralMethod lit(params, item.data());
bigint literalGas = lit.gasNeeded();
CodeCopyMethod copy(params, item.data());
@@ -80,7 +82,12 @@ bigint ConstantOptimisationMethod::simpleRunGas(AssemblyItems const& _items)
if (item.type() == Push)
gas += GasMeter::runGas(Instruction::PUSH1);
else if (item.type() == Operation)
- gas += GasMeter::runGas(item.instruction());
+ {
+ if (item.instruction() == Instruction::EXP)
+ gas += GasCosts::expGas;
+ else
+ gas += GasMeter::runGas(item.instruction());
+ }
return gas;
}
@@ -286,7 +293,7 @@ bigint ComputeMethod::gasNeeded(AssemblyItems const& _routine) const
{
size_t numExps = count(_routine.begin(), _routine.end(), Instruction::EXP);
return combineGas(
- simpleRunGas(_routine) + numExps * (GasCosts::expGas + GasCosts::expByteGas),
+ simpleRunGas(_routine) + numExps * (GasCosts::expGas + GasCosts::expByteGas(m_params.evmVersion)),
// Data gas for routine: Some bytes are zero, but we ignore them.
bytesRequired(_routine) * (m_params.isCreation ? GasCosts::txDataNonZeroGas : GasCosts::createDataGas),
0
diff --git a/libevmasm/ConstantOptimiser.h b/libevmasm/ConstantOptimiser.h
index c450b0b4..9b60b26b 100644
--- a/libevmasm/ConstantOptimiser.h
+++ b/libevmasm/ConstantOptimiser.h
@@ -23,6 +23,8 @@
#include <libevmasm/Exceptions.h>
+#include <libsolidity/interface/EVMVersion.h>
+
#include <libdevcore/Assertions.h>
#include <libdevcore/CommonData.h>
#include <libdevcore/CommonIO.h>
@@ -50,6 +52,7 @@ public:
static unsigned optimiseConstants(
bool _isCreation,
size_t _runs,
+ solidity::EVMVersion _evmVersion,
Assembly& _assembly,
AssemblyItems& _items
);
@@ -59,6 +62,7 @@ public:
bool isCreation; ///< Whether this is called during contract creation or runtime.
size_t runs; ///< Estimated number of calls per opcode oven the lifetime of the contract.
size_t multiplicity; ///< Number of times the constant appears in the code.
+ solidity::EVMVersion evmVersion; ///< Version of the EVM
};
explicit ConstantOptimisationMethod(Params const& _params, u256 const& _value):
diff --git a/libevmasm/GasMeter.cpp b/libevmasm/GasMeter.cpp
index 543f1cbc..caa06fc0 100644
--- a/libevmasm/GasMeter.cpp
+++ b/libevmasm/GasMeter.cpp
@@ -61,7 +61,6 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _
case Operation:
{
ExpressionClasses& classes = m_state->expressionClasses();
- gas = runGas(_item.instruction());
switch (_item.instruction())
{
case Instruction::SSTORE:
@@ -72,26 +71,29 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _
m_state->storageContent().count(slot) &&
classes.knownNonZero(m_state->storageContent().at(slot))
))
- gas += GasCosts::sstoreResetGas; //@todo take refunds into account
+ gas = GasCosts::sstoreResetGas; //@todo take refunds into account
else
- gas += GasCosts::sstoreSetGas;
+ gas = GasCosts::sstoreSetGas;
break;
}
case Instruction::SLOAD:
- gas += GasCosts::sloadGas;
+ gas = GasCosts::sloadGas(m_evmVersion);
break;
case Instruction::RETURN:
case Instruction::REVERT:
+ gas = runGas(_item.instruction());
gas += memoryGas(0, -1);
break;
case Instruction::MLOAD:
case Instruction::MSTORE:
+ gas = runGas(_item.instruction());
gas += memoryGas(classes.find(Instruction::ADD, {
m_state->relativeStackElement(0),
classes.find(AssemblyItem(32))
}));
break;
case Instruction::MSTORE8:
+ gas = runGas(_item.instruction());
gas += memoryGas(classes.find(Instruction::ADD, {
m_state->relativeStackElement(0),
classes.find(AssemblyItem(1))
@@ -105,10 +107,15 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _
case Instruction::CALLDATACOPY:
case Instruction::CODECOPY:
case Instruction::RETURNDATACOPY:
+ gas = runGas(_item.instruction());
gas += memoryGas(0, -2);
gas += wordGas(GasCosts::copyGas, m_state->relativeStackElement(-2));
break;
+ case Instruction::EXTCODESIZE:
+ gas = GasCosts::extCodeGas(m_evmVersion);
+ break;
case Instruction::EXTCODECOPY:
+ gas = GasCosts::extCodeGas(m_evmVersion);
gas += memoryGas(-1, -3);
gas += wordGas(GasCosts::copyGas, m_state->relativeStackElement(-3));
break;
@@ -137,7 +144,7 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _
gas = GasConsumption::infinite();
else
{
- gas = GasCosts::callGas;
+ gas = GasCosts::callGas(m_evmVersion);
if (u256 const* value = classes.knownConstant(m_state->relativeStackElement(0)))
gas += (*value);
else
@@ -155,7 +162,7 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _
break;
}
case Instruction::SELFDESTRUCT:
- gas = GasCosts::selfdestructGas;
+ gas = GasCosts::selfdestructGas(m_evmVersion);
gas += GasCosts::callNewAccountGas; // We very rarely know whether the address exists.
break;
case Instruction::CREATE:
@@ -172,11 +179,15 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _
case Instruction::EXP:
gas = GasCosts::expGas;
if (u256 const* value = classes.knownConstant(m_state->relativeStackElement(-1)))
- gas += GasCosts::expByteGas * (32 - (h256(*value).firstBitSet() / 8));
+ gas += GasCosts::expByteGas(m_evmVersion) * (32 - (h256(*value).firstBitSet() / 8));
else
- gas += GasCosts::expByteGas * 32;
+ gas += GasCosts::expByteGas(m_evmVersion) * 32;
+ break;
+ case Instruction::BALANCE:
+ gas = GasCosts::balanceGas(m_evmVersion);
break;
default:
+ gas = runGas(_item.instruction());
break;
}
break;
@@ -241,12 +252,9 @@ unsigned GasMeter::runGas(Instruction _instruction)
case Tier::Mid: return GasCosts::tier4Gas;
case Tier::High: return GasCosts::tier5Gas;
case Tier::Ext: return GasCosts::tier6Gas;
- case Tier::Special: return GasCosts::tier7Gas;
- case Tier::ExtCode: return GasCosts::extCodeGas;
- case Tier::Balance: return GasCosts::balanceGas;
default: break;
}
- assertThrow(false, OptimizerException, "Invalid gas tier.");
+ assertThrow(false, OptimizerException, "Invalid gas tier for instruction " + instructionInfo(_instruction).name);
return 0;
}
diff --git a/libevmasm/GasMeter.h b/libevmasm/GasMeter.h
index 2c3ecf5a..b131802f 100644
--- a/libevmasm/GasMeter.h
+++ b/libevmasm/GasMeter.h
@@ -21,11 +21,14 @@
#pragma once
-#include <ostream>
-#include <tuple>
#include <libevmasm/ExpressionClasses.h>
#include <libevmasm/AssemblyItem.h>
+#include <libsolidity/interface/EVMVersion.h>
+
+#include <ostream>
+#include <tuple>
+
namespace dev
{
namespace eth
@@ -44,13 +47,25 @@ namespace GasCosts
static unsigned const tier5Gas = 10;
static unsigned const tier6Gas = 20;
static unsigned const tier7Gas = 0;
- static unsigned const extCodeGas = 700;
- static unsigned const balanceGas = 400;
+ inline unsigned extCodeGas(EVMVersion _evmVersion)
+ {
+ return _evmVersion >= EVMVersion::tangerineWhistle() ? 700 : 20;
+ }
+ inline unsigned balanceGas(EVMVersion _evmVersion)
+ {
+ return _evmVersion >= EVMVersion::tangerineWhistle() ? 400 : 20;
+ }
static unsigned const expGas = 10;
- static unsigned const expByteGas = 50;
+ inline unsigned expByteGas(EVMVersion _evmVersion)
+ {
+ return _evmVersion >= EVMVersion::spuriousDragon() ? 50 : 10;
+ }
static unsigned const keccak256Gas = 30;
static unsigned const keccak256WordGas = 6;
- static unsigned const sloadGas = 200;
+ inline unsigned sloadGas(EVMVersion _evmVersion)
+ {
+ return _evmVersion >= EVMVersion::tangerineWhistle() ? 200 : 50;
+ }
static unsigned const sstoreSetGas = 20000;
static unsigned const sstoreResetGas = 5000;
static unsigned const sstoreRefundGas = 15000;
@@ -59,11 +74,17 @@ namespace GasCosts
static unsigned const logDataGas = 8;
static unsigned const logTopicGas = 375;
static unsigned const createGas = 32000;
- static unsigned const callGas = 700;
+ inline unsigned callGas(EVMVersion _evmVersion)
+ {
+ return _evmVersion >= EVMVersion::tangerineWhistle() ? 700 : 40;
+ }
static unsigned const callStipend = 2300;
static unsigned const callValueTransferGas = 9000;
static unsigned const callNewAccountGas = 25000;
- static unsigned const selfdestructGas = 5000;
+ inline unsigned selfdestructGas(EVMVersion _evmVersion)
+ {
+ return _evmVersion >= EVMVersion::tangerineWhistle() ? 5000 : 0;
+ }
static unsigned const selfdestructRefundGas = 24000;
static unsigned const memoryGas = 3;
static unsigned const quadCoeffDiv = 512;
@@ -100,8 +121,8 @@ public:
};
/// Constructs a new gas meter given the current state.
- explicit GasMeter(std::shared_ptr<KnownState> const& _state, u256 const& _largestMemoryAccess = 0):
- m_state(_state), m_largestMemoryAccess(_largestMemoryAccess) {}
+ GasMeter(std::shared_ptr<KnownState> const& _state, solidity::EVMVersion _evmVersion, u256 const& _largestMemoryAccess = 0):
+ m_state(_state), m_evmVersion(_evmVersion), m_largestMemoryAccess(_largestMemoryAccess) {}
/// @returns an upper bound on the gas consumed by the given instruction and updates
/// the state.
@@ -110,6 +131,8 @@ public:
u256 const& largestMemoryAccess() const { return m_largestMemoryAccess; }
+ /// @returns gas costs for simple instructions with constant gas costs (that do not
+ /// change with EVM versions)
static unsigned runGas(Instruction _instruction);
private:
@@ -123,6 +146,7 @@ private:
GasConsumption memoryGas(int _stackPosOffset, int _stackPosSize);
std::shared_ptr<KnownState> m_state;
+ EVMVersion m_evmVersion;
/// Largest point where memory was accessed since the creation of this object.
u256 m_largestMemoryAccess;
};
diff --git a/libevmasm/PathGasMeter.cpp b/libevmasm/PathGasMeter.cpp
index c56e2f8b..3fe682b7 100644
--- a/libevmasm/PathGasMeter.cpp
+++ b/libevmasm/PathGasMeter.cpp
@@ -27,8 +27,8 @@ using namespace std;
using namespace dev;
using namespace dev::eth;
-PathGasMeter::PathGasMeter(AssemblyItems const& _items):
- m_items(_items)
+PathGasMeter::PathGasMeter(AssemblyItems const& _items, solidity::EVMVersion _evmVersion):
+ m_items(_items), m_evmVersion(_evmVersion)
{
for (size_t i = 0; i < m_items.size(); ++i)
if (m_items[i].type() == Tag)
@@ -59,7 +59,7 @@ GasMeter::GasConsumption PathGasMeter::handleQueueItem()
m_queue.pop_back();
shared_ptr<KnownState> state = path->state;
- GasMeter meter(state, path->largestMemoryAccess);
+ GasMeter meter(state, m_evmVersion, path->largestMemoryAccess);
ExpressionClasses& classes = state->expressionClasses();
GasMeter::GasConsumption gas = path->gas;
size_t index = path->index;
diff --git a/libevmasm/PathGasMeter.h b/libevmasm/PathGasMeter.h
index 4826eac2..2527d7fb 100644
--- a/libevmasm/PathGasMeter.h
+++ b/libevmasm/PathGasMeter.h
@@ -21,10 +21,13 @@
#pragma once
+#include <libevmasm/GasMeter.h>
+
+#include <libsolidity/interface/EVMVersion.h>
+
#include <set>
#include <vector>
#include <memory>
-#include <libevmasm/GasMeter.h>
namespace dev
{
@@ -50,7 +53,7 @@ struct GasPath
class PathGasMeter
{
public:
- explicit PathGasMeter(AssemblyItems const& _items);
+ explicit PathGasMeter(AssemblyItems const& _items, solidity::EVMVersion _evmVersion);
GasMeter::GasConsumption estimateMax(size_t _startIndex, std::shared_ptr<KnownState> const& _state);
@@ -60,6 +63,7 @@ private:
std::vector<std::unique_ptr<GasPath>> m_queue;
std::map<u256, size_t> m_tagPositions;
AssemblyItems const& m_items;
+ solidity::EVMVersion m_evmVersion;
};
}
diff --git a/liblll/Compiler.cpp b/liblll/Compiler.cpp
index 1638f69e..f2c1b0be 100644
--- a/liblll/Compiler.cpp
+++ b/liblll/Compiler.cpp
@@ -19,17 +19,16 @@
* @date 2014
*/
-#include "Compiler.h"
-#include "Parser.h"
-#include "CompilerState.h"
-#include "CodeFragment.h"
+#include <liblll/Compiler.h>
+#include <liblll/Parser.h>
+#include <liblll/CompilerState.h>
+#include <liblll/CodeFragment.h>
using namespace std;
using namespace dev;
using namespace dev::eth;
-
-bytes dev::eth::compileLLL(string const& _src, bool _opt, vector<string>* _errors, ReadCallback const& _readFile)
+bytes dev::eth::compileLLL(string const& _src, dev::solidity::EVMVersion _evmVersion, bool _opt, std::vector<std::string>* _errors, dev::eth::ReadCallback const& _readFile)
{
try
{
@@ -37,7 +36,7 @@ bytes dev::eth::compileLLL(string const& _src, bool _opt, vector<string>* _error
cs.populateStandard();
auto assembly = CodeFragment::compile(_src, cs, _readFile).assembly(cs);
if (_opt)
- assembly = assembly.optimise(true);
+ assembly = assembly.optimise(true, _evmVersion);
bytes ret = assembly.assemble().bytecode;
for (auto i: cs.treesToKill)
killBigints(i);
@@ -67,7 +66,7 @@ bytes dev::eth::compileLLL(string const& _src, bool _opt, vector<string>* _error
return bytes();
}
-std::string dev::eth::compileLLLToAsm(std::string const& _src, bool _opt, std::vector<std::string>* _errors, ReadCallback const& _readFile)
+std::string dev::eth::compileLLLToAsm(std::string const& _src, EVMVersion _evmVersion, bool _opt, std::vector<std::string>* _errors, ReadCallback const& _readFile)
{
try
{
@@ -75,7 +74,7 @@ std::string dev::eth::compileLLLToAsm(std::string const& _src, bool _opt, std::v
cs.populateStandard();
auto assembly = CodeFragment::compile(_src, cs, _readFile).assembly(cs);
if (_opt)
- assembly = assembly.optimise(true);
+ assembly = assembly.optimise(true, _evmVersion);
string ret = assembly.assemblyString();
for (auto i: cs.treesToKill)
killBigints(i);
diff --git a/liblll/Compiler.h b/liblll/Compiler.h
index c3395b66..06440c17 100644
--- a/liblll/Compiler.h
+++ b/liblll/Compiler.h
@@ -21,9 +21,12 @@
#pragma once
+#include <libdevcore/Common.h>
+
+#include <libsolidity/interface/EVMVersion.h>
+
#include <string>
#include <vector>
-#include <libdevcore/Common.h>
namespace dev
{
@@ -33,8 +36,8 @@ namespace eth
using ReadCallback = std::function<std::string(std::string const&)>;
std::string parseLLL(std::string const& _src);
-std::string compileLLLToAsm(std::string const& _src, bool _opt = true, std::vector<std::string>* _errors = nullptr, ReadCallback const& _readFile = ReadCallback());
-bytes compileLLL(std::string const& _src, bool _opt = true, std::vector<std::string>* _errors = nullptr, ReadCallback const& _readFile = ReadCallback());
+std::string compileLLLToAsm(std::string const& _src, solidity::EVMVersion _evmVersion, bool _opt = true, std::vector<std::string>* _errors = nullptr, ReadCallback const& _readFile = ReadCallback());
+bytes compileLLL(std::string const& _src, solidity::EVMVersion _evmVersion, bool _opt = true, std::vector<std::string>* _errors = nullptr, ReadCallback const& _readFile = ReadCallback());
}
}
diff --git a/libsolidity/analysis/GlobalContext.cpp b/libsolidity/analysis/GlobalContext.cpp
index fd39d860..34cb61d8 100644
--- a/libsolidity/analysis/GlobalContext.cpp
+++ b/libsolidity/analysis/GlobalContext.cpp
@@ -39,6 +39,7 @@ m_magicVariables(vector<shared_ptr<MagicVariableDeclaration const>>{
make_shared<MagicVariableDeclaration>("assert", make_shared<FunctionType>(strings{"bool"}, strings{}, FunctionType::Kind::Assert, false, StateMutability::Pure)),
make_shared<MagicVariableDeclaration>("block", make_shared<MagicType>(MagicType::Kind::Block)),
make_shared<MagicVariableDeclaration>("ecrecover", make_shared<FunctionType>(strings{"bytes32", "uint8", "bytes32", "bytes32"}, strings{"address"}, FunctionType::Kind::ECRecover, false, StateMutability::Pure)),
+ make_shared<MagicVariableDeclaration>("gasleft", make_shared<FunctionType>(strings(), strings{"uint256"}, FunctionType::Kind::GasLeft, false, StateMutability::View)),
make_shared<MagicVariableDeclaration>("keccak256", make_shared<FunctionType>(strings(), strings{"bytes32"}, FunctionType::Kind::SHA3, true, StateMutability::Pure)),
make_shared<MagicVariableDeclaration>("log0", make_shared<FunctionType>(strings{"bytes32"}, strings{}, FunctionType::Kind::Log0)),
make_shared<MagicVariableDeclaration>("log1", make_shared<FunctionType>(strings{"bytes32", "bytes32"}, strings{}, FunctionType::Kind::Log1)),
diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp
index 985c44d0..296a39c2 100644
--- a/libsolidity/analysis/ReferencesResolver.cpp
+++ b/libsolidity/analysis/ReferencesResolver.cpp
@@ -278,8 +278,9 @@ bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly)
};
// Will be re-generated later with correct information
+ // We use the latest EVM version because we will re-run it anyway.
assembly::AsmAnalysisInfo analysisInfo;
- assembly::AsmAnalyzer(analysisInfo, errorsIgnored, assembly::AsmFlavour::Loose, resolver).analyze(_inlineAssembly.operations());
+ assembly::AsmAnalyzer(analysisInfo, errorsIgnored, EVMVersion(), assembly::AsmFlavour::Loose, resolver).analyze(_inlineAssembly.operations());
return false;
}
diff --git a/libsolidity/analysis/StaticAnalyzer.cpp b/libsolidity/analysis/StaticAnalyzer.cpp
index bd8ee597..d4de219a 100644
--- a/libsolidity/analysis/StaticAnalyzer.cpp
+++ b/libsolidity/analysis/StaticAnalyzer.cpp
@@ -139,6 +139,23 @@ bool StaticAnalyzer::visit(ExpressionStatement const& _statement)
bool StaticAnalyzer::visit(MemberAccess const& _memberAccess)
{
+ bool const v050 = m_currentContract->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050);
+
+ if (MagicType const* type = dynamic_cast<MagicType const*>(_memberAccess.expression().annotation().type.get()))
+ if (type->kind() == MagicType::Kind::Message && _memberAccess.memberName() == "gas")
+ {
+ if (v050)
+ m_errorReporter.typeError(
+ _memberAccess.location(),
+ "\"msg.gas\" has been deprecated in favor of \"gasleft()\""
+ );
+ else
+ m_errorReporter.warning(
+ _memberAccess.location(),
+ "\"msg.gas\" has been deprecated in favor of \"gasleft()\""
+ );
+ }
+
if (m_nonPayablePublic && !m_library)
if (MagicType const* type = dynamic_cast<MagicType const*>(_memberAccess.expression().annotation().type.get()))
if (type->kind() == MagicType::Kind::Message && _memberAccess.memberName() == "value")
@@ -151,7 +168,7 @@ bool StaticAnalyzer::visit(MemberAccess const& _memberAccess)
if (auto const* type = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type.get()))
if (type->kind() == FunctionType::Kind::BareCallCode)
{
- if (m_currentContract->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050))
+ if (v050)
m_errorReporter.typeError(
_memberAccess.location(),
"\"callcode\" has been deprecated in favour of \"delegatecall\"."
diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp
index a2d94be4..1748b518 100644
--- a/libsolidity/analysis/TypeChecker.cpp
+++ b/libsolidity/analysis/TypeChecker.cpp
@@ -897,6 +897,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
assembly::AsmAnalyzer analyzer(
*_inlineAssembly.annotation().analysisInfo,
m_errorReporter,
+ m_evmVersion,
assembly::AsmFlavour::Loose,
identifierAccess
);
@@ -2043,6 +2044,8 @@ void TypeChecker::endVisit(ElementaryTypeNameExpression const& _expr)
void TypeChecker::endVisit(Literal const& _literal)
{
+ bool const v050 = m_scope->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050);
+
if (_literal.looksLikeAddress())
{
if (_literal.passesAddressChecksum())
@@ -2056,6 +2059,21 @@ void TypeChecker::endVisit(Literal const& _literal)
"For more information please see https://solidity.readthedocs.io/en/develop/types.html#address-literals"
);
}
+ if (_literal.isHexNumber() && _literal.subDenomination() != Literal::SubDenomination::None)
+ {
+ if (v050)
+ m_errorReporter.fatalTypeError(
+ _literal.location(),
+ "Hexadecimal numbers cannot be used with unit denominations. "
+ "You can use an expression of the form '0x1234 * 1 day' instead."
+ );
+ else
+ m_errorReporter.warning(
+ _literal.location(),
+ "Hexadecimal numbers with unit denominations are deprecated. "
+ "You can use an expression of the form '0x1234 * 1 day' instead."
+ );
+ }
if (!_literal.annotation().type)
_literal.annotation().type = Type::forLiteral(_literal);
diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h
index 16796b63..2ba31232 100644
--- a/libsolidity/analysis/TypeChecker.h
+++ b/libsolidity/analysis/TypeChecker.h
@@ -22,6 +22,8 @@
#pragma once
+#include <libsolidity/interface/EVMVersion.h>
+
#include <libsolidity/ast/Types.h>
#include <libsolidity/ast/ASTAnnotations.h>
#include <libsolidity/ast/ASTForward.h>
@@ -43,7 +45,10 @@ class TypeChecker: private ASTConstVisitor
{
public:
/// @param _errorReporter provides the error logging functionality.
- TypeChecker(ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) {}
+ TypeChecker(EVMVersion _evmVersion, ErrorReporter& _errorReporter):
+ m_evmVersion(_evmVersion),
+ m_errorReporter(_errorReporter)
+ {}
/// Performs type checking on the given contract and all of its sub-nodes.
/// @returns true iff all checks passed. Note even if all checks passed, errors() can still contain warnings
@@ -132,6 +137,8 @@ private:
ContractDefinition const* m_scope = nullptr;
+ EVMVersion m_evmVersion;
+
/// Flag indicating whether we are currently inside an EmitStatement.
bool m_insideEmitStatement = false;
diff --git a/libsolidity/ast/ExperimentalFeatures.h b/libsolidity/ast/ExperimentalFeatures.h
index 3ecfac7b..a17778b4 100644
--- a/libsolidity/ast/ExperimentalFeatures.h
+++ b/libsolidity/ast/ExperimentalFeatures.h
@@ -40,6 +40,7 @@ static const std::map<ExperimentalFeature, bool> ExperimentalFeatureOnlyAnalysis
{
{ ExperimentalFeature::SMTChecker, true },
{ ExperimentalFeature::TestOnlyAnalysis, true },
+ { ExperimentalFeature::V050, true }
};
static const std::map<std::string, ExperimentalFeature> ExperimentalFeatureNames =
diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h
index 7985521e..c20a025f 100644
--- a/libsolidity/ast/Types.h
+++ b/libsolidity/ast/Types.h
@@ -902,7 +902,8 @@ public:
ByteArrayPush, ///< .push() to a dynamically sized byte array in storage
ObjectCreation, ///< array creation using new
Assert, ///< assert()
- Require ///< require()
+ Require, ///< require()
+ GasLeft ///< gasleft()
};
virtual Category category() const override { return Category::Function; }
diff --git a/libsolidity/codegen/Compiler.h b/libsolidity/codegen/Compiler.h
index 06654486..f6865d75 100644
--- a/libsolidity/codegen/Compiler.h
+++ b/libsolidity/codegen/Compiler.h
@@ -22,22 +22,25 @@
#pragma once
-#include <ostream>
-#include <functional>
#include <libsolidity/codegen/CompilerContext.h>
+#include <libsolidity/interface/EVMVersion.h>
+
#include <libevmasm/Assembly.h>
+#include <ostream>
+#include <functional>
+
namespace dev {
namespace solidity {
class Compiler
{
public:
- explicit Compiler(bool _optimize = false, unsigned _runs = 200):
+ explicit Compiler(EVMVersion _evmVersion = EVMVersion{}, bool _optimize = false, unsigned _runs = 200):
m_optimize(_optimize),
m_optimizeRuns(_runs),
- m_runtimeContext(),
- m_context(&m_runtimeContext)
+ m_runtimeContext(_evmVersion),
+ m_context(_evmVersion, &m_runtimeContext)
{ }
/// Compiles a contract.
diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp
index 0198a107..ebf0213a 100644
--- a/libsolidity/codegen/CompilerContext.cpp
+++ b/libsolidity/codegen/CompilerContext.cpp
@@ -329,6 +329,7 @@ void CompilerContext::appendInlineAssembly(
analyzerResult = assembly::AsmAnalyzer(
analysisInfo,
errorReporter,
+ m_evmVersion,
assembly::AsmFlavour::Strict,
identifierAccess.resolve
).analyze(*parserResult);
diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h
index a155a3a5..cf626683 100644
--- a/libsolidity/codegen/CompilerContext.h
+++ b/libsolidity/codegen/CompilerContext.h
@@ -24,6 +24,8 @@
#include <libsolidity/codegen/ABIFunctions.h>
+#include <libsolidity/interface/EVMVersion.h>
+
#include <libsolidity/ast/ASTForward.h>
#include <libsolidity/ast/Types.h>
#include <libsolidity/ast/ASTAnnotations.h>
@@ -50,14 +52,17 @@ namespace solidity {
class CompilerContext
{
public:
- explicit CompilerContext(CompilerContext* _runtimeContext = nullptr):
+ explicit CompilerContext(EVMVersion _evmVersion = EVMVersion{}, CompilerContext* _runtimeContext = nullptr):
m_asm(std::make_shared<eth::Assembly>()),
+ m_evmVersion(_evmVersion),
m_runtimeContext(_runtimeContext)
{
if (m_runtimeContext)
m_runtimeSub = size_t(m_asm->newSub(m_runtimeContext->m_asm).data());
}
+ EVMVersion const& evmVersion() const { return m_evmVersion; }
+
/// Update currently enabled set of experimental features.
void setExperimentalFeatures(std::set<ExperimentalFeature> const& _features) { m_experimentalFeatures = _features; }
/// @returns true if the given feature is enabled.
@@ -204,7 +209,7 @@ public:
void appendAuxiliaryData(bytes const& _data) { m_asm->appendAuxiliaryDataToEnd(_data); }
/// Run optimisation step.
- void optimise(bool _fullOptimsation, unsigned _runs = 200) { m_asm->optimise(_fullOptimsation, true, _runs); }
+ void optimise(bool _fullOptimsation, unsigned _runs = 200) { m_asm->optimise(_fullOptimsation, m_evmVersion, true, _runs); }
/// @returns the runtime context if in creation mode and runtime context is set, nullptr otherwise.
CompilerContext* runtimeContext() { return m_runtimeContext; }
@@ -287,6 +292,8 @@ private:
} m_functionCompilationQueue;
eth::AssemblyPointer m_asm;
+ /// Version of the EVM to compile against.
+ EVMVersion m_evmVersion;
/// Activated experimental features.
std::set<ExperimentalFeature> m_experimentalFeatures;
/// Other already compiled contracts to be used in contract creation calls.
diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp
index ebb718a5..5a9498f0 100644
--- a/libsolidity/codegen/ContractCompiler.cpp
+++ b/libsolidity/codegen/ContractCompiler.cpp
@@ -1059,7 +1059,7 @@ void ContractCompiler::compileExpression(Expression const& _expression, TypePoin
CompilerUtils(m_context).convertType(*_expression.annotation().type, *_targetType);
}
-eth::AssemblyPointer ContractCompiler::cloneRuntime()
+eth::AssemblyPointer ContractCompiler::cloneRuntime() const
{
eth::Assembly a;
a << Instruction::CALLDATASIZE;
@@ -1070,7 +1070,7 @@ eth::AssemblyPointer ContractCompiler::cloneRuntime()
// this is the address which has to be substituted by the linker.
//@todo implement as special "marker" AssemblyItem.
a << u256("0xcafecafecafecafecafecafecafecafecafecafe");
- a << u256(eth::GasCosts::callGas + 10) << Instruction::GAS << Instruction::SUB;
+ a << u256(eth::GasCosts::callGas(m_context.evmVersion()) + 10) << Instruction::GAS << Instruction::SUB;
a << Instruction::DELEGATECALL;
//Propagate error condition (if DELEGATECALL pushes 0 on stack).
a << Instruction::ISZERO;
diff --git a/libsolidity/codegen/ContractCompiler.h b/libsolidity/codegen/ContractCompiler.h
index d698dc71..8559ea58 100644
--- a/libsolidity/codegen/ContractCompiler.h
+++ b/libsolidity/codegen/ContractCompiler.h
@@ -43,7 +43,7 @@ public:
m_runtimeCompiler(_runtimeCompiler),
m_context(_context)
{
- m_context = CompilerContext(_runtimeCompiler ? &_runtimeCompiler->m_context : nullptr);
+ m_context = CompilerContext(_context.evmVersion(), _runtimeCompiler ? &_runtimeCompiler->m_context : nullptr);
}
void compileContract(
@@ -125,7 +125,7 @@ private:
void compileExpression(Expression const& _expression, TypePointer const& _targetType = TypePointer());
/// @returns the runtime assembly for clone contracts.
- static eth::AssemblyPointer cloneRuntime();
+ eth::AssemblyPointer cloneRuntime() const;
bool const m_optimise;
/// Pointer to the runtime compiler in case this is a creation compiler.
diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp
index 61920592..d27af7db 100644
--- a/libsolidity/codegen/ExpressionCompiler.cpp
+++ b/libsolidity/codegen/ExpressionCompiler.cpp
@@ -906,6 +906,9 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
m_context << success;
break;
}
+ case FunctionType::Kind::GasLeft:
+ m_context << Instruction::GAS;
+ break;
default:
solAssert(false, "Invalid function type.");
}
@@ -1671,16 +1674,19 @@ void ExpressionCompiler::appendExternalFunctionCall(
utils().storeFreeMemoryPointer();
}
- // Touch the end of the output area so that we do not pay for memory resize during the call
- // (which we would have to subtract from the gas left)
- // We could also just use MLOAD; POP right before the gas calculation, but the optimizer
- // would remove that, so we use MSTORE here.
- if (!_functionType.gasSet() && retSize > 0)
+ if (!m_context.evmVersion().canOverchargeGasForCall())
{
- m_context << u256(0);
- utils().fetchFreeMemoryPointer();
- // This touches too much, but that way we save some rounding arithmetics
- m_context << u256(retSize) << Instruction::ADD << Instruction::MSTORE;
+ // Touch the end of the output area so that we do not pay for memory resize during the call
+ // (which we would have to subtract from the gas left)
+ // We could also just use MLOAD; POP right before the gas calculation, but the optimizer
+ // would remove that, so we use MSTORE here.
+ if (!_functionType.gasSet() && retSize > 0)
+ {
+ m_context << u256(0);
+ utils().fetchFreeMemoryPointer();
+ // This touches too much, but that way we save some rounding arithmetics
+ m_context << u256(retSize) << Instruction::ADD << Instruction::MSTORE;
+ }
}
// Copy function identifier to memory.
@@ -1749,14 +1755,14 @@ void ExpressionCompiler::appendExternalFunctionCall(
if (_functionType.gasSet())
m_context << dupInstruction(m_context.baseToCurrentStackOffset(gasStackPos));
- else if (m_context.experimentalFeatureActive(ExperimentalFeature::V050))
+ else if (m_context.evmVersion().canOverchargeGasForCall())
// Send all gas (requires tangerine whistle EVM)
m_context << Instruction::GAS;
else
{
// send all gas except the amount needed to execute "SUB" and "CALL"
// @todo this retains too much gas for now, needs to be fine-tuned.
- u256 gasNeededByCaller = eth::GasCosts::callGas + 10;
+ u256 gasNeededByCaller = eth::GasCosts::callGas(m_context.evmVersion()) + 10;
if (_functionType.valueSet())
gasNeededByCaller += eth::GasCosts::callValueTransferGas;
if (!existenceChecked)
diff --git a/libsolidity/formal/Z3Interface.cpp b/libsolidity/formal/Z3Interface.cpp
index 769e6edb..125da00d 100644
--- a/libsolidity/formal/Z3Interface.cpp
+++ b/libsolidity/formal/Z3Interface.cpp
@@ -28,6 +28,7 @@ using namespace dev::solidity::smt;
Z3Interface::Z3Interface():
m_solver(m_context)
{
+ z3::set_param("rewriter.pull_cheap_ite", true);
}
void Z3Interface::reset()
diff --git a/libsolidity/inlineasm/AsmAnalysis.cpp b/libsolidity/inlineasm/AsmAnalysis.cpp
index 1030523a..a7f764a5 100644
--- a/libsolidity/inlineasm/AsmAnalysis.cpp
+++ b/libsolidity/inlineasm/AsmAnalysis.cpp
@@ -533,33 +533,47 @@ void AsmAnalyzer::expectValidType(string const& type, SourceLocation const& _loc
void AsmAnalyzer::warnOnInstructions(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))
+ // We assume that returndatacopy, returndatasize and staticcall are either all available
+ // or all not available.
+ solAssert(m_evmVersion.supportsReturndata() == m_evmVersion.hasStaticCall(), "");
+
+ if (_instr == solidity::Instruction::CREATE2)
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."
+ + "\" instruction is not supported by the VM version \"" +
+ "" + m_evmVersion.name() +
+ "\" you are currently compiling for. " +
+ "It will be interpreted as an invalid instruction on this VM."
);
-
- static set<solidity::Instruction> experimentalInstructions{
- solidity::Instruction::SHL,
- solidity::Instruction::SHR,
- solidity::Instruction::SAR
- };
- if (experimentalInstructions.count(_instr))
+ else if ((
+ _instr == solidity::Instruction::RETURNDATACOPY ||
+ _instr == solidity::Instruction::RETURNDATASIZE ||
+ _instr == solidity::Instruction::STATICCALL
+ ) && !m_evmVersion.supportsReturndata())
+ m_errorReporter.warning(
+ _location,
+ "The \"" +
+ boost::to_lower_copy(instructionInfo(_instr).name)
+ + "\" instruction is only available for Byzantium-compatible VMs. " +
+ "You are currently compiling for \"" +
+ m_evmVersion.name() +
+ "\", where it will be interpreted as an invalid instruction."
+ );
+ else if ((
+ _instr == solidity::Instruction::SHL ||
+ _instr == solidity::Instruction::SHR ||
+ _instr == solidity::Instruction::SAR
+ ) && !m_evmVersion.hasBitwiseShifting())
m_errorReporter.warning(
_location,
"The \"" +
boost::to_lower_copy(instructionInfo(_instr).name)
- + "\" instruction is only available after " +
- "the Constantinople hard fork. Before that it acts as an invalid instruction."
+ + "\" instruction is only available for Constantinople-compatible VMs. " +
+ "You are currently compiling for \"" +
+ m_evmVersion.name() +
+ "\", where it will be interpreted as an invalid instruction."
);
if (_instr == solidity::Instruction::JUMP || _instr == solidity::Instruction::JUMPI || _instr == solidity::Instruction::JUMPDEST)
diff --git a/libsolidity/inlineasm/AsmAnalysis.h b/libsolidity/inlineasm/AsmAnalysis.h
index 7a81dbf8..867711c7 100644
--- a/libsolidity/inlineasm/AsmAnalysis.h
+++ b/libsolidity/inlineasm/AsmAnalysis.h
@@ -21,6 +21,7 @@
#pragma once
#include <libsolidity/interface/Exceptions.h>
+#include <libsolidity/interface/EVMVersion.h>
#include <libsolidity/inlineasm/AsmScope.h>
@@ -54,9 +55,10 @@ public:
explicit AsmAnalyzer(
AsmAnalysisInfo& _analysisInfo,
ErrorReporter& _errorReporter,
+ EVMVersion _evmVersion,
AsmFlavour _flavour = AsmFlavour::Loose,
julia::ExternalIdentifierAccess::Resolver const& _resolver = julia::ExternalIdentifierAccess::Resolver()
- ): m_resolver(_resolver), m_info(_analysisInfo), m_errorReporter(_errorReporter), m_flavour(_flavour) {}
+ ): m_resolver(_resolver), m_info(_analysisInfo), m_errorReporter(_errorReporter), m_evmVersion(_evmVersion), m_flavour(_flavour) {}
bool analyze(assembly::Block const& _block);
@@ -97,6 +99,7 @@ private:
std::set<Scope::Variable const*> m_activeVariables;
AsmAnalysisInfo& m_info;
ErrorReporter& m_errorReporter;
+ EVMVersion m_evmVersion;
AsmFlavour m_flavour = AsmFlavour::Loose;
};
diff --git a/libsolidity/interface/AssemblyStack.cpp b/libsolidity/interface/AssemblyStack.cpp
index c9e534c7..7a9fffbf 100644
--- a/libsolidity/interface/AssemblyStack.cpp
+++ b/libsolidity/interface/AssemblyStack.cpp
@@ -91,7 +91,7 @@ bool AssemblyStack::analyze(assembly::Block const& _block, Scanner const* _scann
bool AssemblyStack::analyzeParsed()
{
m_analysisInfo = make_shared<assembly::AsmAnalysisInfo>();
- assembly::AsmAnalyzer analyzer(*m_analysisInfo, m_errorReporter, languageToAsmFlavour(m_language));
+ assembly::AsmAnalyzer analyzer(*m_analysisInfo, m_errorReporter, m_evmVersion, languageToAsmFlavour(m_language));
m_analysisSuccessful = analyzer.analyze(*m_parserResult);
return m_analysisSuccessful;
}
diff --git a/libsolidity/interface/AssemblyStack.h b/libsolidity/interface/AssemblyStack.h
index 6ae7e8d1..720220ab 100644
--- a/libsolidity/interface/AssemblyStack.h
+++ b/libsolidity/interface/AssemblyStack.h
@@ -22,6 +22,8 @@
#pragma once
#include <libsolidity/interface/ErrorReporter.h>
+#include <libsolidity/interface/EVMVersion.h>
+
#include <libevmasm/LinkerObject.h>
#include <string>
@@ -54,8 +56,8 @@ public:
enum class Language { JULIA, Assembly, StrictAssembly };
enum class Machine { EVM, EVM15, eWasm };
- explicit AssemblyStack(Language _language = Language::Assembly):
- m_language(_language), m_errorReporter(m_errors)
+ explicit AssemblyStack(EVMVersion _evmVersion = EVMVersion(), Language _language = Language::Assembly):
+ m_language(_language), m_evmVersion(_evmVersion), m_errorReporter(m_errors)
{}
/// @returns the scanner used during parsing
@@ -82,6 +84,7 @@ private:
bool analyzeParsed();
Language m_language = Language::Assembly;
+ EVMVersion m_evmVersion;
std::shared_ptr<Scanner> m_scanner;
diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp
index 3b5e65e8..eacfca9c 100644
--- a/libsolidity/interface/CompilerStack.cpp
+++ b/libsolidity/interface/CompilerStack.cpp
@@ -74,6 +74,12 @@ void CompilerStack::setRemappings(vector<string> const& _remappings)
swap(m_remappings, remappings);
}
+void CompilerStack::setEVMVersion(EVMVersion _version)
+{
+ solAssert(m_stackState < State::ParsingSuccessful, "Set EVM version after parsing.");
+ m_evmVersion = _version;
+}
+
void CompilerStack::reset(bool _keepSources)
{
if (_keepSources)
@@ -88,6 +94,7 @@ void CompilerStack::reset(bool _keepSources)
m_sources.clear();
}
m_libraries.clear();
+ m_evmVersion = EVMVersion();
m_optimize = false;
m_optimizeRuns = 200;
m_globalContext.reset();
@@ -198,7 +205,7 @@ bool CompilerStack::analyze()
m_contracts[contract->fullyQualifiedName()].contract = contract;
}
- TypeChecker typeChecker(m_errorReporter);
+ TypeChecker typeChecker(m_evmVersion, m_errorReporter);
for (Source const* source: m_sourceOrder)
for (ASTPointer<ASTNode> const& node: source->ast->nodes())
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
@@ -677,7 +684,7 @@ void CompilerStack::compileContract(
for (auto const* dependency: _contract.annotation().contractDependencies)
compileContract(*dependency, _compiledContracts);
- shared_ptr<Compiler> compiler = make_shared<Compiler>(m_optimize, m_optimizeRuns);
+ shared_ptr<Compiler> compiler = make_shared<Compiler>(m_evmVersion, m_optimize, m_optimizeRuns);
Contract& compiledContract = m_contracts.at(_contract.fullyQualifiedName());
string metadata = createMetadata(compiledContract);
bytes cborEncodedHash =
@@ -736,7 +743,7 @@ void CompilerStack::compileContract(
{
if (!_contract.isLibrary())
{
- Compiler cloneCompiler(m_optimize, m_optimizeRuns);
+ Compiler cloneCompiler(m_evmVersion, m_optimize, m_optimizeRuns);
cloneCompiler.compileClone(_contract, _compiledContracts);
compiledContract.cloneObject = cloneCompiler.assembledObject();
}
@@ -838,6 +845,7 @@ string CompilerStack::createMetadata(Contract const& _contract) const
}
meta["settings"]["optimizer"]["enabled"] = m_optimize;
meta["settings"]["optimizer"]["runs"] = m_optimizeRuns;
+ meta["settings"]["evmVersion"] = m_evmVersion.name();
meta["settings"]["compilationTarget"][_contract.contract->sourceUnitName()] =
_contract.contract->annotation().canonicalName;
@@ -951,11 +959,12 @@ Json::Value CompilerStack::gasEstimates(string const& _contractName) const
return Json::Value();
using Gas = GasEstimator::GasConsumption;
+ GasEstimator gasEstimator(m_evmVersion);
Json::Value output(Json::objectValue);
if (eth::AssemblyItems const* items = assemblyItems(_contractName))
{
- Gas executionGas = GasEstimator::functionalEstimation(*items);
+ Gas executionGas = gasEstimator.functionalEstimation(*items);
u256 bytecodeSize(runtimeObject(_contractName).bytecode.size());
Gas codeDepositGas = bytecodeSize * eth::GasCosts::createDataGas;
@@ -976,14 +985,14 @@ Json::Value CompilerStack::gasEstimates(string const& _contractName) const
for (auto it: contract.interfaceFunctions())
{
string sig = it.second->externalSignature();
- externalFunctions[sig] = gasToJson(GasEstimator::functionalEstimation(*items, sig));
+ externalFunctions[sig] = gasToJson(gasEstimator.functionalEstimation(*items, sig));
}
if (contract.fallbackFunction())
/// This needs to be set to an invalid signature in order to trigger the fallback,
/// without the shortcut (of CALLDATSIZE == 0), and therefore to receive the upper bound.
/// An empty string ("") would work to trigger the shortcut only.
- externalFunctions[""] = gasToJson(GasEstimator::functionalEstimation(*items, "INVALID"));
+ externalFunctions[""] = gasToJson(gasEstimator.functionalEstimation(*items, "INVALID"));
if (!externalFunctions.empty())
output["external"] = externalFunctions;
@@ -999,7 +1008,7 @@ Json::Value CompilerStack::gasEstimates(string const& _contractName) const
size_t entry = functionEntryPoint(_contractName, *it);
GasEstimator::GasConsumption gas = GasEstimator::GasConsumption::infinite();
if (entry > 0)
- gas = GasEstimator::functionalEstimation(*items, entry, *it);
+ gas = gasEstimator.functionalEstimation(*items, entry, *it);
/// TODO: This could move into a method shared with externalSignature()
FunctionType type(*it);
diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h
index b377b3aa..13c9cc7a 100644
--- a/libsolidity/interface/CompilerStack.h
+++ b/libsolidity/interface/CompilerStack.h
@@ -23,20 +23,26 @@
#pragma once
+#include <libsolidity/interface/ErrorReporter.h>
+#include <libsolidity/interface/ReadFile.h>
+#include <libsolidity/interface/EVMVersion.h>
+
+#include <libevmasm/SourceLocation.h>
+#include <libevmasm/LinkerObject.h>
+
+#include <libdevcore/Common.h>
+#include <libdevcore/FixedHash.h>
+
+#include <json/json.h>
+
+#include <boost/noncopyable.hpp>
+#include <boost/filesystem.hpp>
+
#include <ostream>
#include <string>
#include <memory>
#include <vector>
#include <functional>
-#include <boost/noncopyable.hpp>
-#include <boost/filesystem.hpp>
-#include <json/json.h>
-#include <libdevcore/Common.h>
-#include <libdevcore/FixedHash.h>
-#include <libevmasm/SourceLocation.h>
-#include <libevmasm/LinkerObject.h>
-#include <libsolidity/interface/ErrorReporter.h>
-#include <libsolidity/interface/ReadFile.h>
namespace dev
{
@@ -116,6 +122,8 @@ public:
m_optimizeRuns = _runs;
}
+ void setEVMVersion(EVMVersion _version = EVMVersion{});
+
/// Sets the list of requested contract names. If empty, no filtering is performed and every contract
/// found in the supplied sources is compiled. Names are cleared iff @a _contractNames is missing.
void setRequestedContractNames(std::set<std::string> const& _contractNames = std::set<std::string>{})
@@ -310,6 +318,7 @@ private:
ReadCallback::Callback m_smtQuery;
bool m_optimize = false;
unsigned m_optimizeRuns = 200;
+ EVMVersion m_evmVersion;
std::set<std::string> m_requestedContractNames;
std::map<std::string, h160> m_libraries;
/// list of path prefix remappings, e.g. mylibrary: github.com/ethereum = /usr/local/ethereum
diff --git a/libsolidity/interface/EVMVersion.h b/libsolidity/interface/EVMVersion.h
new file mode 100644
index 00000000..13c4ec94
--- /dev/null
+++ b/libsolidity/interface/EVMVersion.h
@@ -0,0 +1,93 @@
+/*
+ This file is part of solidity.
+
+ solidity is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ solidity is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with solidity. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * EVM versioning.
+ */
+
+#pragma once
+
+#include <string>
+
+#include <boost/optional.hpp>
+#include <boost/operators.hpp>
+
+namespace dev
+{
+namespace solidity
+{
+
+/**
+ * A version specifier of the EVM we want to compile to.
+ * Defaults to the latest version.
+ */
+class EVMVersion:
+ boost::less_than_comparable<EVMVersion>,
+ boost::equality_comparable<EVMVersion>
+{
+public:
+ EVMVersion() {}
+
+ static EVMVersion homestead() { return {Version::Homestead}; }
+ static EVMVersion tangerineWhistle() { return {Version::TangerineWhistle}; }
+ static EVMVersion spuriousDragon() { return {Version::SpuriousDragon}; }
+ static EVMVersion byzantium() { return {Version::Byzantium}; }
+ static EVMVersion constantinople() { return {Version::Constantinople}; }
+
+ static boost::optional<EVMVersion> fromString(std::string const& _version)
+ {
+ for (auto const& v: {homestead(), tangerineWhistle(), spuriousDragon(), byzantium()})
+ if (_version == v.name())
+ return v;
+ return {};
+ }
+
+ bool operator==(EVMVersion const& _other) const { return m_version == _other.m_version; }
+ bool operator<(EVMVersion const& _other) const { return m_version < _other.m_version; }
+
+ std::string name() const
+ {
+ switch (m_version)
+ {
+ case Version::Homestead: return "homestead";
+ case Version::TangerineWhistle: return "tangerineWhistle";
+ case Version::SpuriousDragon: return "spuriousDragon";
+ case Version::Byzantium: return "byzantium";
+ case Version::Constantinople: return "constantinople";
+ }
+ return "INVALID";
+ }
+
+ /// Has the RETURNDATACOPY and RETURNDATASIZE opcodes.
+ bool supportsReturndata() const { return *this >= byzantium(); }
+ bool hasStaticCall() const { return *this >= byzantium(); }
+ bool hasBitwiseShifting() const { return *this >= constantinople(); }
+
+ /// Whether we have to retain the costs for the call opcode itself (false),
+ /// or whether we can just forward easily all remaining gas (true).
+ bool canOverchargeGasForCall() const { return *this >= tangerineWhistle(); }
+
+private:
+ enum class Version { Homestead, TangerineWhistle, SpuriousDragon, Byzantium, Constantinople };
+
+ EVMVersion(Version _version): m_version(_version) {}
+
+ Version m_version = Version::Byzantium;
+};
+
+
+}
+}
diff --git a/libsolidity/interface/GasEstimator.cpp b/libsolidity/interface/GasEstimator.cpp
index 22cc0266..2139395f 100644
--- a/libsolidity/interface/GasEstimator.cpp
+++ b/libsolidity/interface/GasEstimator.cpp
@@ -40,7 +40,7 @@ using namespace dev::solidity;
GasEstimator::ASTGasConsumptionSelfAccumulated GasEstimator::structuralEstimation(
AssemblyItems const& _items,
vector<ASTNode const*> const& _ast
-)
+) const
{
solAssert(std::count(_ast.begin(), _ast.end(), nullptr) == 0, "");
map<SourceLocation, GasConsumption> particularCosts;
@@ -49,7 +49,7 @@ GasEstimator::ASTGasConsumptionSelfAccumulated GasEstimator::structuralEstimatio
for (BasicBlock const& block: cfg.optimisedBlocks())
{
solAssert(!!block.startState, "");
- GasMeter meter(block.startState->copy());
+ GasMeter meter(block.startState->copy(), m_evmVersion);
auto const end = _items.begin() + block.end;
for (auto iter = _items.begin() + block.begin; iter != end; ++iter)
particularCosts[iter->location()] += meter.estimateMax(*iter);
@@ -127,7 +127,7 @@ map<ASTNode const*, GasMeter::GasConsumption> GasEstimator::breakToStatementLeve
GasEstimator::GasConsumption GasEstimator::functionalEstimation(
AssemblyItems const& _items,
string const& _signature
-)
+) const
{
auto state = make_shared<KnownState>();
@@ -144,7 +144,7 @@ GasEstimator::GasConsumption GasEstimator::functionalEstimation(
});
}
- PathGasMeter meter(_items);
+ PathGasMeter meter(_items, m_evmVersion);
return meter.estimateMax(0, state);
}
@@ -152,7 +152,7 @@ GasEstimator::GasConsumption GasEstimator::functionalEstimation(
AssemblyItems const& _items,
size_t const& _offset,
FunctionDefinition const& _function
-)
+) const
{
auto state = make_shared<KnownState>();
@@ -167,7 +167,7 @@ GasEstimator::GasConsumption GasEstimator::functionalEstimation(
if (parametersSize > 0)
state->feedItem(swapInstruction(parametersSize));
- return PathGasMeter(_items).estimateMax(_offset, state);
+ return PathGasMeter(_items, m_evmVersion).estimateMax(_offset, state);
}
set<ASTNode const*> GasEstimator::finestNodesAtLocation(
diff --git a/libsolidity/interface/GasEstimator.h b/libsolidity/interface/GasEstimator.h
index bf63df96..ea94d988 100644
--- a/libsolidity/interface/GasEstimator.h
+++ b/libsolidity/interface/GasEstimator.h
@@ -22,11 +22,14 @@
#pragma once
+#include <libsolidity/interface/EVMVersion.h>
+
+#include <libevmasm/GasMeter.h>
+#include <libevmasm/Assembly.h>
+
#include <vector>
#include <map>
#include <array>
-#include <libevmasm/GasMeter.h>
-#include <libevmasm/Assembly.h>
namespace dev
{
@@ -44,13 +47,15 @@ public:
using ASTGasConsumptionSelfAccumulated =
std::map<ASTNode const*, std::array<GasConsumption, 2>>;
+ explicit GasEstimator(EVMVersion _evmVersion): m_evmVersion(_evmVersion) {}
+
/// Estimates the gas consumption for every assembly item in the given assembly and stores
/// it by source location.
/// @returns a mapping from each AST node to a pair of its particular and syntactically accumulated gas costs.
- static ASTGasConsumptionSelfAccumulated structuralEstimation(
+ ASTGasConsumptionSelfAccumulated structuralEstimation(
eth::AssemblyItems const& _items,
std::vector<ASTNode const*> const& _ast
- );
+ ) const;
/// @returns a mapping from nodes with non-overlapping source locations to gas consumptions such that
/// the following source locations are part of the mapping:
/// 1. source locations of statements that do not contain other statements
@@ -62,23 +67,24 @@ public:
/// @returns the estimated gas consumption by the (public or external) function with the
/// given signature. If no signature is given, estimates the maximum gas usage.
- static GasConsumption functionalEstimation(
+ GasConsumption functionalEstimation(
eth::AssemblyItems const& _items,
std::string const& _signature = ""
- );
+ ) const;
/// @returns the estimated gas consumption by the given function which starts at the given
/// offset into the list of assembly items.
/// @note this does not work correctly for recursive functions.
- static GasConsumption functionalEstimation(
+ GasConsumption functionalEstimation(
eth::AssemblyItems const& _items,
size_t const& _offset,
FunctionDefinition const& _function
- );
+ ) const;
private:
/// @returns the set of AST nodes which are the finest nodes at their location.
static std::set<ASTNode const*> finestNodesAtLocation(std::vector<ASTNode const*> const& _roots);
+ EVMVersion m_evmVersion;
};
}
diff --git a/libsolidity/interface/SourceReferenceFormatter.cpp b/libsolidity/interface/SourceReferenceFormatter.cpp
index 9d02c498..0f014372 100644
--- a/libsolidity/interface/SourceReferenceFormatter.cpp
+++ b/libsolidity/interface/SourceReferenceFormatter.cpp
@@ -79,8 +79,8 @@ void SourceReferenceFormatter::printSourceLocation(SourceLocation const* _locati
scanner.lineAtPosition(_location->start) <<
endl <<
string(startColumn, ' ') <<
- "^\n" <<
- "Spanning multiple lines.\n";
+ "^ (Relevant source part starts here and spans across multiple lines)." <<
+ endl;
}
void SourceReferenceFormatter::printSourceName(SourceLocation const* _location)
diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp
index 91fe72ae..ee9b1440 100644
--- a/libsolidity/interface/StandardCompiler.cpp
+++ b/libsolidity/interface/StandardCompiler.cpp
@@ -318,6 +318,14 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
Json::Value const& settings = _input.get("settings", Json::Value());
+ if (settings.isMember("evmVersion"))
+ {
+ boost::optional<EVMVersion> version = EVMVersion::fromString(settings.get("evmVersion", {}).asString());
+ if (!version)
+ return formatFatalError("JSONError", "Invalid EVM version requested.");
+ m_compilerStack.setEVMVersion(*version);
+ }
+
vector<string> remappings;
for (auto const& remapping: settings.get("remappings", Json::Value()))
remappings.push_back(remapping.asString());
diff --git a/lllc/main.cpp b/lllc/main.cpp
index 5679bc2b..0ca3ff13 100644
--- a/lllc/main.cpp
+++ b/lllc/main.cpp
@@ -133,7 +133,7 @@ int main(int argc, char** argv)
}
else if (mode == Binary || mode == Hex)
{
- auto bs = compileLLL(src, optimise ? true : false, &errors, readFileAsString);
+ auto bs = compileLLL(src, EVMVersion{}, optimise ? true : false, &errors, readFileAsString);
if (mode == Hex)
cout << toHex(bs) << endl;
else if (mode == Binary)
@@ -145,7 +145,7 @@ int main(int argc, char** argv)
}
else if (mode == Assembly)
{
- cout << compileLLLToAsm(src, optimise ? true : false, &errors, readFileAsString) << endl;
+ cout << compileLLLToAsm(src, EVMVersion{}, optimise ? true : false, &errors, readFileAsString) << endl;
}
for (auto const& i: errors)
diff --git a/scripts/tests.sh b/scripts/tests.sh
index 3c80adc5..bf4ee3d9 100755
--- a/scripts/tests.sh
+++ b/scripts/tests.sh
@@ -37,11 +37,9 @@ then
echo "Usage: $0 [--junit_report <report_directory>]"
exit 1
fi
- testargs_no_opt="--logger=JUNIT,test_suite,$2/no_opt.xml"
- testargs_opt="--logger=JUNIT,test_suite,$2/opt.xml"
+ log_directory="$2"
else
- testargs_no_opt=''
- testargs_opt=''
+ log_directory=""
fi
echo "Running commandline tests..."
@@ -61,12 +59,15 @@ function download_eth()
ETH_PATH="eth"
else
mkdir -p /tmp/test
- ETH_BINARY=eth_byzantium_artful
- ETH_HASH="e527dd3e3dc17b983529dd7dcfb74a0d3a5aed4e"
if grep -i trusty /etc/lsb-release >/dev/null 2>&1
then
- ETH_BINARY=eth_byzantium2
- ETH_HASH="4dc3f208475f622be7c8e53bee720e14cd254c6f"
+ # built from 1ecff3cac12f0fbbeea3e645f331d5ac026b24d3 at 2018-03-06
+ ETH_BINARY=eth_byzantium_trusty
+ ETH_HASH="5432ea81c150e8a3547615bf597cd6dce9e1e27b"
+ else
+ # built from ?? at 2018-02-13 ?
+ ETH_BINARY=eth_byzantium_artful
+ ETH_HASH="e527dd3e3dc17b983529dd7dcfb74a0d3a5aed4e"
fi
wget -q -O /tmp/test/eth https://github.com/ethereum/cpp-ethereum/releases/download/solidityTester/$ETH_BINARY
test "$(shasum /tmp/test/eth)" = "$ETH_HASH /tmp/test/eth"
@@ -98,13 +99,29 @@ then
progress=""
fi
-echo "--> Running tests without optimizer..."
-"$REPO_ROOT"/build/test/soltest $testargs_no_opt $progress -- --ipcpath /tmp/test/geth.ipc
-echo "--> Running tests WITH optimizer..."
-"$REPO_ROOT"/build/test/soltest $testargs_opt $progress -- --optimize --ipcpath /tmp/test/geth.ipc
+# And then run the Solidity unit-tests in the matrix combination of optimizer / no optimizer
+# and homestead / byzantium VM, # pointing to that IPC endpoint.
+for optimize in "" "--optimize"
+do
+ for vm in homestead byzantium
+ do
+ echo "--> Running tests using "$optimize" --evm-version "$vm"..."
+ log=""
+ if [ -n "$log_directory" ]
+ then
+ if [ -n "$optimize" ]
+ then
+ log=--logger=JUNIT,test_suite,$log_directory/opt_$vm.xml $testargs
+ else
+ log=--logger=JUNIT,test_suite,$log_directory/noopt_$vm.xml $testargs_no_opt
+ fi
+ fi
+ "$REPO_ROOT"/build/test/soltest $progress $log -- "$optimize" --evm-version "$vm" --ipcpath /tmp/test/geth.ipc
+ done
+done
wait $CMDLINE_PID
pkill "$ETH_PID" || true
sleep 4
-pgrep "$ETH_PID" && pkill -9 "$ETH_PID" || true \ No newline at end of file
+pgrep "$ETH_PID" && pkill -9 "$ETH_PID" || true
diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp
index 62b24975..d3d234c3 100644
--- a/solc/CommandLineInterface.cpp
+++ b/solc/CommandLineInterface.cpp
@@ -71,7 +71,6 @@ namespace solidity
static string const g_stdinFileNameStr = "<stdin>";
static string const g_strAbi = "abi";
-static string const g_strAddStandard = "add-std";
static string const g_strAllowPaths = "allow-paths";
static string const g_strAsm = "asm";
static string const g_strAsmJson = "asm-json";
@@ -87,6 +86,7 @@ static string const g_strCompactJSON = "compact-format";
static string const g_strContracts = "contracts";
static string const g_strEVM = "evm";
static string const g_strEVM15 = "evm15";
+static string const g_strEVMVersion = "evm-version";
static string const g_streWasm = "ewasm";
static string const g_strFormal = "formal";
static string const g_strGas = "gas";
@@ -118,7 +118,6 @@ static string const g_strPrettyJson = "pretty-json";
static string const g_strVersion = "version";
static string const g_argAbi = g_strAbi;
-static string const g_argAddStandard = g_strAddStandard;
static string const g_argPrettyJson = g_strPrettyJson;
static string const g_argAllowPaths = g_strAllowPaths;
static string const g_argAsm = g_strAsm;
@@ -537,13 +536,17 @@ Allowed 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_strEVMVersion.c_str(),
+ po::value<string>()->value_name("version"),
+ "Select desired EVM version. Either homestead, tangerineWhistle, spuriousDragon, byzantium (default) or constantinople."
+ )
(g_argOptimize.c_str(), "Enable bytecode optimizer.")
(
g_argOptimizeRuns.c_str(),
po::value<unsigned>()->value_name("n")->default_value(200),
"Estimated number of contract runs for optimizer tuning."
)
- (g_argAddStandard.c_str(), "Add standard contracts.")
(g_argPrettyJson.c_str(), "Output JSON in pretty format. Currently it only works with the combined JSON output.")
(
g_argLibraries.c_str(),
@@ -745,6 +748,18 @@ bool CommandLineInterface::processInput()
if (!parseLibraryOption(library))
return false;
+ if (m_args.count(g_strEVMVersion))
+ {
+ string versionOptionStr = m_args[g_strEVMVersion].as<string>();
+ boost::optional<EVMVersion> versionOption = EVMVersion::fromString(versionOptionStr);
+ if (!versionOption)
+ {
+ cerr << "Invalid option for --evm-version: " << versionOptionStr << endl;
+ return false;
+ }
+ m_evmVersion = *versionOption;
+ }
+
if (m_args.count(g_argAssemble) || m_args.count(g_argStrictAssembly) || m_args.count(g_argJulia))
{
// switch to assembly mode
@@ -792,6 +807,7 @@ bool CommandLineInterface::processInput()
m_compiler->addSource(sourceCode.first, sourceCode.second);
if (m_args.count(g_argLibraries))
m_compiler->setLibraries(m_libraries);
+ m_compiler->setEVMVersion(m_evmVersion);
// TODO: Perhaps we should not compile unless requested
bool optimize = m_args.count(g_argOptimize) > 0;
unsigned runs = m_args[g_argOptimizeRuns].as<unsigned>();
@@ -950,7 +966,7 @@ void CommandLineInterface::handleAst(string const& _argStr)
// FIXME: shouldn't this be done for every contract?
if (m_compiler->runtimeAssemblyItems(m_compiler->lastContractName()))
gasCosts = GasEstimator::breakToStatementLevel(
- GasEstimator::structuralEstimation(*m_compiler->runtimeAssemblyItems(m_compiler->lastContractName()), asts),
+ GasEstimator(m_evmVersion).structuralEstimation(*m_compiler->runtimeAssemblyItems(m_compiler->lastContractName()), asts),
asts
);
@@ -1071,7 +1087,7 @@ bool CommandLineInterface::assemble(
map<string, AssemblyStack> assemblyStacks;
for (auto const& src: m_sourceCodes)
{
- auto& stack = assemblyStacks[src.first] = AssemblyStack(_language);
+ auto& stack = assemblyStacks[src.first] = AssemblyStack(m_evmVersion, _language);
try
{
if (!stack.parseAndAnalyze(src.first, src.second))
diff --git a/solc/CommandLineInterface.h b/solc/CommandLineInterface.h
index 4768c9d8..303023fc 100644
--- a/solc/CommandLineInterface.h
+++ b/solc/CommandLineInterface.h
@@ -23,6 +23,7 @@
#include <libsolidity/interface/CompilerStack.h>
#include <libsolidity/interface/AssemblyStack.h>
+#include <libsolidity/interface/EVMVersion.h>
#include <boost/program_options.hpp>
#include <boost/filesystem/path.hpp>
@@ -102,6 +103,8 @@ private:
std::map<std::string, h160> m_libraries;
/// Solidity compiler stack
std::unique_ptr<dev::solidity::CompilerStack> m_compiler;
+ /// EVM version to use
+ EVMVersion m_evmVersion;
};
}
diff --git a/test/ExecutionFramework.cpp b/test/ExecutionFramework.cpp
index 85b5bd3b..a24f78fb 100644
--- a/test/ExecutionFramework.cpp
+++ b/test/ExecutionFramework.cpp
@@ -20,13 +20,15 @@
* Framework for executing contracts and testing them using RPC.
*/
-#include <cstdlib>
-#include <boost/test/framework.hpp>
-#include <libdevcore/CommonIO.h>
#include <test/ExecutionFramework.h>
+#include <libdevcore/CommonIO.h>
+
+#include <boost/test/framework.hpp>
#include <boost/algorithm/string/replace.hpp>
+#include <cstdlib>
+
using namespace std;
using namespace dev;
using namespace dev::test;
@@ -49,6 +51,7 @@ string getIPCSocketPath()
ExecutionFramework::ExecutionFramework() :
m_rpc(RPCSession::instance(getIPCSocketPath())),
+ m_evmVersion(dev::test::Options::get().evmVersion()),
m_optimize(dev::test::Options::get().optimize),
m_showMessages(dev::test::Options::get().showMessages),
m_sender(m_rpc.account(0))
diff --git a/test/ExecutionFramework.h b/test/ExecutionFramework.h
index 8aa99473..a7971b81 100644
--- a/test/ExecutionFramework.h
+++ b/test/ExecutionFramework.h
@@ -25,6 +25,8 @@
#include <test/TestHelper.h>
#include <test/RPCSession.h>
+#include <libsolidity/interface/EVMVersion.h>
+
#include <libdevcore/FixedHash.h>
#include <libdevcore/SHA3.h>
@@ -227,6 +229,7 @@ protected:
bytes data;
};
+ solidity::EVMVersion m_evmVersion;
unsigned m_optimizeRuns = 200;
bool m_optimize = false;
bool m_showMessages = false;
diff --git a/test/Metadata.cpp b/test/Metadata.cpp
index 1ebfd468..c130d346 100644
--- a/test/Metadata.cpp
+++ b/test/Metadata.cpp
@@ -60,7 +60,8 @@ bool isValidMetadata(string const& _metadata)
!metadata.isMember("compiler") ||
!metadata.isMember("settings") ||
!metadata.isMember("sources") ||
- !metadata.isMember("output")
+ !metadata.isMember("output") ||
+ !metadata["settings"].isMember("evmVersion")
)
return false;
diff --git a/test/RPCSession.cpp b/test/RPCSession.cpp
index 69c75cee..54871057 100644
--- a/test/RPCSession.cpp
+++ b/test/RPCSession.cpp
@@ -19,7 +19,11 @@
/// @file RPCSession.cpp
/// Low-level IPC communication between the test framework and the Ethereum node.
-#include "RPCSession.h"
+#include <test/RPCSession.h>
+
+#include <test/TestHelper.h>
+
+#include <libsolidity/interface/EVMVersion.h>
#include <libdevcore/CommonData.h>
@@ -215,6 +219,13 @@ string RPCSession::personal_newAccount(string const& _password)
void RPCSession::test_setChainParams(vector<string> const& _accounts)
{
+ string forks;
+ if (test::Options::get().evmVersion() >= solidity::EVMVersion::tangerineWhistle())
+ forks += "\"EIP150ForkBlock\": \"0x00\",\n";
+ if (test::Options::get().evmVersion() >= solidity::EVMVersion::spuriousDragon())
+ forks += "\"EIP158ForkBlock\": \"0x00\",\n";
+ if (test::Options::get().evmVersion() >= solidity::EVMVersion::byzantium())
+ forks += "\"byzantiumForkBlock\": \"0x00\",\n";
static string const c_configString = R"(
{
"sealEngine": "NoProof",
@@ -223,9 +234,8 @@ void RPCSession::test_setChainParams(vector<string> const& _accounts)
"maximumExtraDataSize": "0x1000000",
"blockReward": "0x",
"allowFutureBlocks": true,
- "homesteadForkBlock": "0x00",
- "EIP150ForkBlock": "0x00",
- "EIP158ForkBlock": "0x00"
+ )" + forks + R"(
+ "homesteadForkBlock": "0x00"
},
"genesis": {
"author": "0000000000000010000000000000000000000000",
diff --git a/test/TestHelper.cpp b/test/TestHelper.cpp
index c8747a06..e0d4423d 100644
--- a/test/TestHelper.cpp
+++ b/test/TestHelper.cpp
@@ -19,8 +19,12 @@
* @date 2014
*/
+#include <test/TestHelper.h>
+
+#include <libsolidity/interface/EVMVersion.h>
+
#include <boost/test/framework.hpp>
-#include "TestHelper.h"
+
using namespace std;
using namespace dev::test;
@@ -41,6 +45,11 @@ Options::Options()
}
else if (string(suite.argv[i]) == "--optimize")
optimize = true;
+ else if (string(suite.argv[i]) == "--evm-version")
+ {
+ evmVersionString = i + 1 < suite.argc ? suite.argv[i + 1] : "INVALID";
+ ++i;
+ }
else if (string(suite.argv[i]) == "--show-messages")
showMessages = true;
else if (string(suite.argv[i]) == "--no-ipc")
@@ -52,3 +61,17 @@ Options::Options()
if (auto path = getenv("ETH_TEST_IPC"))
ipcPath = path;
}
+
+dev::solidity::EVMVersion Options::evmVersion() const
+{
+ if (!evmVersionString.empty())
+ {
+ // We do this check as opposed to in the constructor because the BOOST_REQUIRE
+ // macros cannot yet be used in the constructor.
+ auto version = solidity::EVMVersion::fromString(evmVersionString);
+ BOOST_REQUIRE_MESSAGE(version, "Invalid EVM version: " + evmVersionString);
+ return *version;
+ }
+ else
+ return dev::solidity::EVMVersion();
+}
diff --git a/test/TestHelper.h b/test/TestHelper.h
index d25c5cd8..8c2eec36 100644
--- a/test/TestHelper.h
+++ b/test/TestHelper.h
@@ -19,11 +19,14 @@
#pragma once
-#include <functional>
+#include <libsolidity/interface/EVMVersion.h>
+
#include <boost/test/unit_test.hpp>
#include <boost/filesystem.hpp>
#include <boost/version.hpp>
+#include <functional>
+
namespace dev
{
namespace test
@@ -37,9 +40,13 @@ struct Options: boost::noncopyable
bool disableIPC = false;
bool disableSMT = false;
+ solidity::EVMVersion evmVersion() const;
+
static Options const& get();
private:
+ std::string evmVersionString;
+
Options();
};
diff --git a/test/contracts/LLL_ENS.cpp b/test/contracts/LLL_ENS.cpp
index c5fe8a82..028d58c8 100644
--- a/test/contracts/LLL_ENS.cpp
+++ b/test/contracts/LLL_ENS.cpp
@@ -345,7 +345,7 @@ protected:
if (!s_compiledEns)
{
vector<string> errors;
- s_compiledEns.reset(new bytes(compileLLL(ensCode, dev::test::Options::get().optimize, &errors)));
+ s_compiledEns.reset(new bytes(compileLLL(ensCode, dev::test::Options::get().evmVersion(), dev::test::Options::get().optimize, &errors)));
BOOST_REQUIRE(errors.empty());
}
sendMessage(*s_compiledEns, true);
diff --git a/test/contracts/LLL_ERC20.cpp b/test/contracts/LLL_ERC20.cpp
index 25665d64..60b43e4f 100644
--- a/test/contracts/LLL_ERC20.cpp
+++ b/test/contracts/LLL_ERC20.cpp
@@ -396,7 +396,7 @@ protected:
if (!s_compiledErc20)
{
vector<string> errors;
- s_compiledErc20.reset(new bytes(compileLLL(erc20Code, dev::test::Options::get().optimize, &errors)));
+ s_compiledErc20.reset(new bytes(compileLLL(erc20Code, dev::test::Options::get().evmVersion(), dev::test::Options::get().optimize, &errors)));
BOOST_REQUIRE(errors.empty());
}
sendMessage(*s_compiledErc20, true);
diff --git a/test/fuzzer.cpp b/test/fuzzer.cpp
index 45738baa..71f38b67 100644
--- a/test/fuzzer.cpp
+++ b/test/fuzzer.cpp
@@ -76,6 +76,7 @@ void testConstantOptimizer()
ConstantOptimisationMethod::optimiseConstants(
isCreation,
runs,
+ EVMVersion{},
assembly,
const_cast<AssemblyItems&>(assembly.items())
);
diff --git a/test/libevmasm/Optimiser.cpp b/test/libevmasm/Optimiser.cpp
index 0ab95b08..e6abcb53 100644
--- a/test/libevmasm/Optimiser.cpp
+++ b/test/libevmasm/Optimiser.cpp
@@ -20,6 +20,8 @@
* Tests for the Solidity optimizer.
*/
+#include <test/TestHelper.h>
+
#include <libevmasm/CommonSubexpressionEliminator.h>
#include <libevmasm/PeepholeOptimiser.h>
#include <libevmasm/JumpdestRemover.h>
@@ -916,7 +918,7 @@ BOOST_AUTO_TEST_CASE(jumpdest_removal_subassemblies)
main.append(t1.toSubAssemblyTag(subId));
main.append(u256(8));
- main.optimise(true);
+ main.optimise(true, dev::test::Options::get().evmVersion());
AssemblyItems expectationMain{
AssemblyItem(PushSubSize, 0),
diff --git a/test/libjulia/Common.cpp b/test/libjulia/Common.cpp
index 7053a68d..d8cd20b6 100644
--- a/test/libjulia/Common.cpp
+++ b/test/libjulia/Common.cpp
@@ -21,6 +21,8 @@
#include <test/libjulia/Common.h>
+#include <test/TestHelper.h>
+
#include <libjulia/optimiser/Disambiguator.h>
#include <libsolidity/parsing/Scanner.h>
@@ -61,7 +63,12 @@ pair<shared_ptr<Block>, shared_ptr<assembly::AsmAnalysisInfo>> dev::julia::test:
{
BOOST_REQUIRE(errorReporter.errors().empty());
auto analysisInfo = make_shared<assembly::AsmAnalysisInfo>();
- assembly::AsmAnalyzer analyzer(*analysisInfo, errorReporter, flavour);
+ assembly::AsmAnalyzer analyzer(
+ *analysisInfo,
+ errorReporter,
+ dev::test::Options::get().evmVersion(),
+ flavour
+ );
if (analyzer.analyze(*parserResult))
{
BOOST_REQUIRE(errorReporter.errors().empty());
diff --git a/test/libjulia/Parser.cpp b/test/libjulia/Parser.cpp
index ff9474c1..6476c4d4 100644
--- a/test/libjulia/Parser.cpp
+++ b/test/libjulia/Parser.cpp
@@ -56,7 +56,12 @@ bool parse(string const& _source, ErrorReporter& errorReporter)
if (parserResult)
{
assembly::AsmAnalysisInfo analysisInfo;
- return (assembly::AsmAnalyzer(analysisInfo, errorReporter, assembly::AsmFlavour::IULIA)).analyze(*parserResult);
+ return (assembly::AsmAnalyzer(
+ analysisInfo,
+ errorReporter,
+ dev::test::Options::get().evmVersion(),
+ assembly::AsmFlavour::IULIA
+ )).analyze(*parserResult);
}
}
catch (FatalError const&)
diff --git a/test/liblll/Compiler.cpp b/test/liblll/Compiler.cpp
index ace97e15..6c6eae3f 100644
--- a/test/liblll/Compiler.cpp
+++ b/test/liblll/Compiler.cpp
@@ -20,11 +20,16 @@
* Unit tests for the LLL compiler.
*/
+#include <test/TestHelper.h>
+
+#include <libdevcore/FixedHash.h>
+
+#include <liblll/Compiler.h>
+
+#include <boost/test/unit_test.hpp>
+
#include <string>
#include <memory>
-#include <boost/test/unit_test.hpp>
-#include <liblll/Compiler.h>
-#include <libdevcore/FixedHash.h>
using namespace std;
@@ -41,7 +46,7 @@ namespace
bool successCompile(string const& _sourceCode)
{
vector<string> errors;
- bytes bytecode = eth::compileLLL(_sourceCode, false, &errors);
+ bytes bytecode = eth::compileLLL(_sourceCode, dev::test::Options::get().evmVersion(), false, &errors);
if (!errors.empty())
return false;
if (bytecode.empty())
@@ -353,7 +358,7 @@ BOOST_AUTO_TEST_CASE(valid_opcodes_functional)
for (size_t i = 0; i < opcodes_bytecode.size(); i++) {
vector<string> errors;
- bytes code = eth::compileLLL(opcodes_lll[i], false, &errors);
+ bytes code = eth::compileLLL(opcodes_lll[i], dev::test::Options::get().evmVersion(), false, &errors);
BOOST_REQUIRE_MESSAGE(errors.empty(), opcodes_lll[i]);
@@ -641,7 +646,7 @@ BOOST_AUTO_TEST_CASE(valid_opcodes_asm)
for (size_t i = 0; i < opcodes_bytecode.size(); i++) {
vector<string> errors;
- bytes code = eth::compileLLL(opcodes_lll[i], false, &errors);
+ bytes code = eth::compileLLL(opcodes_lll[i], dev::test::Options::get().evmVersion(), false, &errors);
BOOST_REQUIRE_MESSAGE(errors.empty(), opcodes_lll[i]);
diff --git a/test/liblll/EndToEndTest.cpp b/test/liblll/EndToEndTest.cpp
index 1a5bb490..e5e70cf8 100644
--- a/test/liblll/EndToEndTest.cpp
+++ b/test/liblll/EndToEndTest.cpp
@@ -20,10 +20,13 @@
* End to end tests for LLL.
*/
+#include <test/liblll/ExecutionFramework.h>
+#include <test/TestHelper.h>
+
+#include <boost/test/unit_test.hpp>
+
#include <string>
#include <memory>
-#include <boost/test/unit_test.hpp>
-#include <test/liblll/ExecutionFramework.h>
using namespace std;
@@ -583,24 +586,34 @@ BOOST_AUTO_TEST_CASE(allgas)
BOOST_AUTO_TEST_CASE(send_two_args)
{
- char const* sourceCode = R"(
- (returnlll
- (send 0xdead 42))
- )";
- compileAndRun(sourceCode);
- callFallbackWithValue(42);
- BOOST_CHECK(balanceAt(Address(0xdead)) == 42);
+ // "send" does not retain enough gas to be able to pay for account creation.
+ // Disabling for non-tangerineWhistle VMs.
+ if (dev::test::Options::get().evmVersion().canOverchargeGasForCall())
+ {
+ char const* sourceCode = R"(
+ (returnlll
+ (send 0xdead 42))
+ )";
+ compileAndRun(sourceCode);
+ callFallbackWithValue(42);
+ BOOST_CHECK(balanceAt(Address(0xdead)) == 42);
+ }
}
BOOST_AUTO_TEST_CASE(send_three_args)
{
- char const* sourceCode = R"(
- (returnlll
- (send allgas 0xdead 42))
- )";
- compileAndRun(sourceCode);
- callFallbackWithValue(42);
- BOOST_CHECK(balanceAt(Address(0xdead)) == 42);
+ // "send" does not retain enough gas to be able to pay for account creation.
+ // Disabling for non-tangerineWhistle VMs.
+ if (dev::test::Options::get().evmVersion().canOverchargeGasForCall())
+ {
+ char const* sourceCode = R"(
+ (returnlll
+ (send allgas 0xdead 42))
+ )";
+ compileAndRun(sourceCode);
+ callFallbackWithValue(42);
+ BOOST_CHECK(balanceAt(Address(0xdead)) == 42);
+ }
}
// Regression test for edge case that previously failed
@@ -708,56 +721,76 @@ BOOST_AUTO_TEST_CASE(msg_four_args)
BOOST_AUTO_TEST_CASE(msg_three_args)
{
- char const* sourceCode = R"(
- (returnlll
- (seq
- (when (= 0 (calldatasize))
- (return (msg (address) 42 0xff)))
- (return (callvalue))))
- )";
- compileAndRun(sourceCode);
- BOOST_CHECK(callFallbackWithValue(42) == encodeArgs(u256(42)));
+ // "msg" does not retain enough gas.
+ // Disabling for non-tangerineWhistle VMs.
+ if (dev::test::Options::get().evmVersion().canOverchargeGasForCall())
+ {
+ char const* sourceCode = R"(
+ (returnlll
+ (seq
+ (when (= 0 (calldatasize))
+ (return (msg (address) 42 0xff)))
+ (return (callvalue))))
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callFallbackWithValue(42) == encodeArgs(u256(42)));
+ }
}
BOOST_AUTO_TEST_CASE(msg_two_args)
{
- char const* sourceCode = R"(
- (returnlll
- (seq
- (when (= 0 (calldatasize))
- (return (msg (address) 0xff)))
- (return 42)))
- )";
- compileAndRun(sourceCode);
- BOOST_CHECK(callFallback() == encodeArgs(u256(42)));
+ // "msg" does not retain enough gas.
+ // Disabling for non-tangerineWhistle VMs.
+ if (dev::test::Options::get().evmVersion().canOverchargeGasForCall())
+ {
+ char const* sourceCode = R"(
+ (returnlll
+ (seq
+ (when (= 0 (calldatasize))
+ (return (msg (address) 0xff)))
+ (return 42)))
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callFallback() == encodeArgs(u256(42)));
+ }
}
BOOST_AUTO_TEST_CASE(create_one_arg)
{
- char const* sourceCode = R"(
- (returnlll
- (seq
- (call allgas
- (create (returnlll (return 42)))
- 0 0 0 0x00 0x20)
- (return 0x00 0x20)))
- )";
- compileAndRun(sourceCode);
- BOOST_CHECK(callFallback() == encodeArgs(u256(42)));
+ // "call" does not retain enough gas.
+ // Disabling for non-tangerineWhistle VMs.
+ if (dev::test::Options::get().evmVersion().canOverchargeGasForCall())
+ {
+ char const* sourceCode = R"(
+ (returnlll
+ (seq
+ (call allgas
+ (create (returnlll (return 42)))
+ 0 0 0 0x00 0x20)
+ (return 0x00 0x20)))
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callFallback() == encodeArgs(u256(42)));
+ }
}
BOOST_AUTO_TEST_CASE(create_two_args)
{
- char const* sourceCode = R"(
- (returnlll
- (seq
- (call allgas
- (create 42 (returnlll (return (balance (address)))))
- 0 0 0 0x00 0x20)
- (return 0x00 0x20)))
- )";
- compileAndRun(sourceCode);
- BOOST_CHECK(callFallbackWithValue(42) == encodeArgs(u256(42)));
+ // "call" does not retain enough gas.
+ // Disabling for non-tangerineWhistle VMs.
+ if (dev::test::Options::get().evmVersion().canOverchargeGasForCall())
+ {
+ char const* sourceCode = R"(
+ (returnlll
+ (seq
+ (call allgas
+ (create 42 (returnlll (return (balance (address)))))
+ 0 0 0 0x00 0x20)
+ (return 0x00 0x20)))
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callFallbackWithValue(42) == encodeArgs(u256(42)));
+ }
}
BOOST_AUTO_TEST_CASE(sha3_two_args)
@@ -822,77 +855,102 @@ BOOST_AUTO_TEST_CASE(makeperm) // Covers makeperm (implicit), permcount and perm
BOOST_AUTO_TEST_CASE(ecrecover)
{
- char const* sourceCode = R"(
- (returnlll
- (return
- (ecrecover
- ; Hash of 'hello world'
- 0x47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad
- ; v = 1 + 27
- 0x1c
- ; r
- 0xdebaaa0cddb321b2dcaaf846d39605de7b97e77ba6106587855b9106cb104215
- ; s
- 0x61a22d94fa8b8a687ff9c911c844d1c016d1a685a9166858f9c7c1bc85128aca)))
- )";
- compileAndRun(sourceCode);
- BOOST_CHECK(callFallback() == encodeArgs(fromHex("0x8743523d96a1b2cbe0c6909653a56da18ed484af")));
+ // "ecrecover" does not retain enough gas.
+ // Disabling for non-tangerineWhistle VMs.
+ if (dev::test::Options::get().evmVersion().canOverchargeGasForCall())
+ {
+ char const* sourceCode = R"(
+ (returnlll
+ (return
+ (ecrecover
+ ; Hash of 'hello world'
+ 0x47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad
+ ; v = 1 + 27
+ 0x1c
+ ; r
+ 0xdebaaa0cddb321b2dcaaf846d39605de7b97e77ba6106587855b9106cb104215
+ ; s
+ 0x61a22d94fa8b8a687ff9c911c844d1c016d1a685a9166858f9c7c1bc85128aca)))
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callFallback() == encodeArgs(fromHex("0x8743523d96a1b2cbe0c6909653a56da18ed484af")));
+ }
}
BOOST_AUTO_TEST_CASE(sha256_two_args)
{
- char const* sourceCode = R"(
- (returnlll
- (seq
- (lit 0x20 "abcdefghijklmnopqrstuvwxyzABCDEF")
- (lit 0x40 "GHIJKLMNOPQRSTUVWXYZ0123456789?!")
- (sha256 0x20 0x40)
- (return 0x00 0x20)))
- )";
- compileAndRun(sourceCode);
- BOOST_CHECK(callFallback() == encodeArgs(
- fromHex("0xcf25a9fe3d86ae228c226c81d2d8c64c687cd6dc4586d10d8e7e4e5b6706d429")));
+ // "sha256" does not retain enough gas.
+ // Disabling for non-tangerineWhistle VMs.
+ if (dev::test::Options::get().evmVersion().canOverchargeGasForCall())
+ {
+ char const* sourceCode = R"(
+ (returnlll
+ (seq
+ (lit 0x20 "abcdefghijklmnopqrstuvwxyzABCDEF")
+ (lit 0x40 "GHIJKLMNOPQRSTUVWXYZ0123456789?!")
+ (sha256 0x20 0x40)
+ (return 0x00 0x20)))
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callFallback() == encodeArgs(
+ fromHex("0xcf25a9fe3d86ae228c226c81d2d8c64c687cd6dc4586d10d8e7e4e5b6706d429")));
+ }
}
BOOST_AUTO_TEST_CASE(ripemd160_two_args)
{
- char const* sourceCode = R"(
- (returnlll
- (seq
- (lit 0x20 "abcdefghijklmnopqrstuvwxyzABCDEF")
- (lit 0x40 "GHIJKLMNOPQRSTUVWXYZ0123456789?!")
- (ripemd160 0x20 0x40)
- (return 0x00 0x20)))
- )";
- compileAndRun(sourceCode);
- BOOST_CHECK(callFallback() == encodeArgs(
- fromHex("0x36c6b90a49e17d4c1e1b0e634ec74124d9b207da")));
+ // "ripemd160" does not retain enough gas.
+ // Disabling for non-tangerineWhistle VMs.
+ if (dev::test::Options::get().evmVersion().canOverchargeGasForCall())
+ {
+ char const* sourceCode = R"(
+ (returnlll
+ (seq
+ (lit 0x20 "abcdefghijklmnopqrstuvwxyzABCDEF")
+ (lit 0x40 "GHIJKLMNOPQRSTUVWXYZ0123456789?!")
+ (ripemd160 0x20 0x40)
+ (return 0x00 0x20)))
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callFallback() == encodeArgs(
+ fromHex("0x36c6b90a49e17d4c1e1b0e634ec74124d9b207da")));
+ }
}
BOOST_AUTO_TEST_CASE(sha256_one_arg)
{
- char const* sourceCode = R"(
- (returnlll
- (seq
- (sha256 0x6162636465666768696a6b6c6d6e6f707172737475767778797a414243444546)
- (return 0x00 0x20)))
- )";
- compileAndRun(sourceCode);
- BOOST_CHECK(callFallback() == encodeArgs(
- fromHex("0xcfd2f1fad75a1978da0a444883db7251414b139f31f5a04704c291fdb0e175e6")));
+ // "sha256" does not retain enough gas.
+ // Disabling for non-tangerineWhistle VMs.
+ if (dev::test::Options::get().evmVersion().canOverchargeGasForCall())
+ {
+ char const* sourceCode = R"(
+ (returnlll
+ (seq
+ (sha256 0x6162636465666768696a6b6c6d6e6f707172737475767778797a414243444546)
+ (return 0x00 0x20)))
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callFallback() == encodeArgs(
+ fromHex("0xcfd2f1fad75a1978da0a444883db7251414b139f31f5a04704c291fdb0e175e6")));
+ }
}
BOOST_AUTO_TEST_CASE(ripemd160_one_arg)
{
- char const* sourceCode = R"(
- (returnlll
- (seq
- (ripemd160 0x6162636465666768696a6b6c6d6e6f707172737475767778797a414243444546)
- (return 0x00 0x20)))
- )";
- compileAndRun(sourceCode);
- BOOST_CHECK(callFallback() == encodeArgs(
- fromHex("0xac5ab22e07b0fb80c69b6207902f725e2507e546")));
+ // "ripemd160" does not retain enough gas.
+ // Disabling for non-tangerineWhistle VMs.
+ if (dev::test::Options::get().evmVersion().canOverchargeGasForCall())
+ {
+ char const* sourceCode = R"(
+ (returnlll
+ (seq
+ (ripemd160 0x6162636465666768696a6b6c6d6e6f707172737475767778797a414243444546)
+ (return 0x00 0x20)))
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callFallback() == encodeArgs(
+ fromHex("0xac5ab22e07b0fb80c69b6207902f725e2507e546")));
+ }
}
BOOST_AUTO_TEST_CASE(wei_szabo_finney_ether)
diff --git a/test/liblll/ExecutionFramework.h b/test/liblll/ExecutionFramework.h
index 58e1f0ad..ae5cd988 100644
--- a/test/liblll/ExecutionFramework.h
+++ b/test/liblll/ExecutionFramework.h
@@ -56,7 +56,7 @@ public:
BOOST_REQUIRE(_libraryAddresses.empty());
std::vector<std::string> errors;
- bytes bytecode = eth::compileLLL(_sourceCode, m_optimize, &errors);
+ bytes bytecode = eth::compileLLL(_sourceCode, dev::test::Options::get().evmVersion(), m_optimize, &errors);
if (!errors.empty())
{
for (auto const& error: errors)
diff --git a/test/libsolidity/ASTJSON.cpp b/test/libsolidity/ASTJSON.cpp
index a165f7a9..9bf60b64 100644
--- a/test/libsolidity/ASTJSON.cpp
+++ b/test/libsolidity/ASTJSON.cpp
@@ -20,12 +20,16 @@
* Tests for the json ast output.
*/
-#include <string>
-#include <boost/test/unit_test.hpp>
+#include <test/TestHelper.h>
+
#include <libsolidity/interface/Exceptions.h>
#include <libsolidity/interface/CompilerStack.h>
#include <libsolidity/ast/ASTJsonConverter.h>
+#include <boost/test/unit_test.hpp>
+
+#include <string>
+
using namespace std;
namespace dev
@@ -41,6 +45,7 @@ BOOST_AUTO_TEST_CASE(smoke_test)
{
CompilerStack c;
c.addSource("a", "contract C {}");
+ c.setEVMVersion(dev::test::Options::get().evmVersion());
c.parseAndAnalyze();
map<string, unsigned> sourceIndices;
sourceIndices["a"] = 1;
@@ -52,6 +57,7 @@ BOOST_AUTO_TEST_CASE(source_location)
{
CompilerStack c;
c.addSource("a", "contract C { function f() { var x = 2; x++; } }");
+ c.setEVMVersion(dev::test::Options::get().evmVersion());
c.parseAndAnalyze();
map<string, unsigned> sourceIndices;
sourceIndices["a"] = 1;
@@ -66,6 +72,7 @@ BOOST_AUTO_TEST_CASE(inheritance_specifier)
{
CompilerStack c;
c.addSource("a", "contract C1 {} contract C2 is C1 {}");
+ c.setEVMVersion(dev::test::Options::get().evmVersion());
c.parseAndAnalyze();
map<string, unsigned> sourceIndices;
sourceIndices["a"] = 1;
@@ -81,6 +88,7 @@ BOOST_AUTO_TEST_CASE(using_for_directive)
{
CompilerStack c;
c.addSource("a", "library L {} contract C { using L for uint; }");
+ c.setEVMVersion(dev::test::Options::get().evmVersion());
c.parseAndAnalyze();
map<string, unsigned> sourceIndices;
sourceIndices["a"] = 1;
@@ -98,6 +106,7 @@ BOOST_AUTO_TEST_CASE(enum_value)
{
CompilerStack c;
c.addSource("a", "contract C { enum E { A, B } }");
+ c.setEVMVersion(dev::test::Options::get().evmVersion());
c.parseAndAnalyze();
map<string, unsigned> sourceIndices;
sourceIndices["a"] = 1;
@@ -115,6 +124,7 @@ BOOST_AUTO_TEST_CASE(modifier_definition)
{
CompilerStack c;
c.addSource("a", "contract C { modifier M(uint i) { _; } function F() M(1) {} }");
+ c.setEVMVersion(dev::test::Options::get().evmVersion());
c.parseAndAnalyze();
map<string, unsigned> sourceIndices;
sourceIndices["a"] = 1;
@@ -129,6 +139,7 @@ BOOST_AUTO_TEST_CASE(modifier_invocation)
{
CompilerStack c;
c.addSource("a", "contract C { modifier M(uint i) { _; } function F() M(1) {} }");
+ c.setEVMVersion(dev::test::Options::get().evmVersion());
c.parseAndAnalyze();
map<string, unsigned> sourceIndices;
sourceIndices["a"] = 1;
@@ -145,6 +156,7 @@ BOOST_AUTO_TEST_CASE(event_definition)
{
CompilerStack c;
c.addSource("a", "contract C { event E(); }");
+ c.setEVMVersion(dev::test::Options::get().evmVersion());
c.parseAndAnalyze();
map<string, unsigned> sourceIndices;
sourceIndices["a"] = 1;
@@ -159,6 +171,7 @@ BOOST_AUTO_TEST_CASE(array_type_name)
{
CompilerStack c;
c.addSource("a", "contract C { uint[] i; }");
+ c.setEVMVersion(dev::test::Options::get().evmVersion());
c.parseAndAnalyze();
map<string, unsigned> sourceIndices;
sourceIndices["a"] = 1;
@@ -172,6 +185,7 @@ BOOST_AUTO_TEST_CASE(placeholder_statement)
{
CompilerStack c;
c.addSource("a", "contract C { modifier M { _; } }");
+ c.setEVMVersion(dev::test::Options::get().evmVersion());
c.parseAndAnalyze();
map<string, unsigned> sourceIndices;
sourceIndices["a"] = 1;
@@ -185,6 +199,7 @@ BOOST_AUTO_TEST_CASE(non_utf8)
{
CompilerStack c;
c.addSource("a", "contract C { function f() { var x = hex\"ff\"; } }");
+ c.setEVMVersion(dev::test::Options::get().evmVersion());
c.parseAndAnalyze();
map<string, unsigned> sourceIndices;
sourceIndices["a"] = 1;
@@ -204,6 +219,7 @@ BOOST_AUTO_TEST_CASE(function_type)
"contract C { function f(function() external payable returns (uint) x) "
"returns (function() external constant returns (uint)) {} }"
);
+ c.setEVMVersion(dev::test::Options::get().evmVersion());
c.parseAndAnalyze();
map<string, unsigned> sourceIndices;
sourceIndices["a"] = 1;
@@ -244,6 +260,7 @@ BOOST_AUTO_TEST_CASE(documentation)
" /** Some comment on fn.*/ function fn() public {}"
"}"
);
+ c.setEVMVersion(dev::test::Options::get().evmVersion());
c.parseAndAnalyze();
map<string, unsigned> sourceIndices;
sourceIndices["a"] = 0;
diff --git a/test/libsolidity/AnalysisFramework.cpp b/test/libsolidity/AnalysisFramework.cpp
index a27e3222..7c335a48 100644
--- a/test/libsolidity/AnalysisFramework.cpp
+++ b/test/libsolidity/AnalysisFramework.cpp
@@ -20,6 +20,8 @@
#include <test/libsolidity/AnalysisFramework.h>
+#include <test/TestHelper.h>
+
#include <libsolidity/interface/CompilerStack.h>
#include <libsolidity/interface/SourceReferenceFormatter.h>
@@ -46,6 +48,7 @@ AnalysisFramework::parseAnalyseAndReturnError(
{
m_compiler.reset();
m_compiler.addSource("", _insertVersionPragma ? "pragma solidity >=0.0;\n" + _source : _source);
+ m_compiler.setEVMVersion(dev::test::Options::get().evmVersion());
if (!m_compiler.parse())
{
BOOST_ERROR("Parsing contract failed in analysis test suite:" + formatErrors());
diff --git a/test/libsolidity/Assembly.cpp b/test/libsolidity/Assembly.cpp
index 59af6d41..aff610a4 100644
--- a/test/libsolidity/Assembly.cpp
+++ b/test/libsolidity/Assembly.cpp
@@ -20,11 +20,11 @@
* Unit tests for Assembly Items from evmasm/Assembly.h
*/
-#include <string>
-#include <iostream>
-#include <boost/test/unit_test.hpp>
+#include <test/TestHelper.h>
+
#include <libevmasm/SourceLocation.h>
#include <libevmasm/Assembly.h>
+
#include <libsolidity/parsing/Scanner.h>
#include <libsolidity/parsing/Parser.h>
#include <libsolidity/analysis/NameAndTypeResolver.h>
@@ -33,6 +33,11 @@
#include <libsolidity/analysis/TypeChecker.h>
#include <libsolidity/interface/ErrorReporter.h>
+#include <boost/test/unit_test.hpp>
+
+#include <string>
+#include <iostream>
+
using namespace std;
using namespace dev::eth;
@@ -46,7 +51,7 @@ namespace test
namespace
{
-eth::AssemblyItems compileContract(const string& _sourceCode)
+eth::AssemblyItems compileContract(string const& _sourceCode)
{
ErrorList errors;
ErrorReporter errorReporter(errors);
@@ -69,7 +74,7 @@ eth::AssemblyItems compileContract(const string& _sourceCode)
for (ASTPointer<ASTNode> const& node: sourceUnit->nodes())
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
{
- TypeChecker checker(errorReporter);
+ TypeChecker checker(dev::test::Options::get().evmVersion(), errorReporter);
BOOST_REQUIRE_NO_THROW(checker.checkTypeRequirements(*contract));
if (!Error::containsOnlyWarnings(errorReporter.errors()))
return AssemblyItems();
@@ -77,7 +82,7 @@ eth::AssemblyItems compileContract(const string& _sourceCode)
for (ASTPointer<ASTNode> const& node: sourceUnit->nodes())
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
{
- Compiler compiler;
+ Compiler compiler(dev::test::Options::get().evmVersion());
compiler.compileContract(*contract, map<ContractDefinition const*, Assembly const*>{}, bytes());
return compiler.runtimeAssemblyItems();
diff --git a/test/libsolidity/GasMeter.cpp b/test/libsolidity/GasMeter.cpp
index 9d3409dd..fd2017f9 100644
--- a/test/libsolidity/GasMeter.cpp
+++ b/test/libsolidity/GasMeter.cpp
@@ -49,13 +49,14 @@ public:
m_compiler.reset(false);
m_compiler.addSource("", "pragma solidity >=0.0;\n" + _sourceCode);
m_compiler.setOptimiserSettings(dev::test::Options::get().optimize);
+ m_compiler.setEVMVersion(m_evmVersion);
BOOST_REQUIRE_MESSAGE(m_compiler.compile(), "Compiling contract failed");
AssemblyItems const* items = m_compiler.runtimeAssemblyItems(m_compiler.lastContractName());
ASTNode const& sourceUnit = m_compiler.ast("");
BOOST_REQUIRE(items != nullptr);
m_gasCosts = GasEstimator::breakToStatementLevel(
- GasEstimator::structuralEstimation(*items, vector<ASTNode const*>({&sourceUnit})),
+ GasEstimator(dev::test::Options::get().evmVersion()).structuralEstimation(*items, vector<ASTNode const*>({&sourceUnit})),
{&sourceUnit}
);
}
@@ -64,7 +65,7 @@ public:
{
compileAndRun(_sourceCode);
auto state = make_shared<KnownState>();
- PathGasMeter meter(*m_compiler.assemblyItems(m_compiler.lastContractName()));
+ PathGasMeter meter(*m_compiler.assemblyItems(m_compiler.lastContractName()), dev::test::Options::get().evmVersion());
GasMeter::GasConsumption gas = meter.estimateMax(0, state);
u256 bytecodeSize(m_compiler.runtimeObject(m_compiler.lastContractName()).bytecode.size());
// costs for deployment
@@ -73,7 +74,7 @@ public:
gas += gasForTransaction(m_compiler.object(m_compiler.lastContractName()).bytecode, true);
BOOST_REQUIRE(!gas.isInfinite);
- BOOST_CHECK(gas.value == m_gasUsed);
+ BOOST_CHECK_EQUAL(gas.value, m_gasUsed);
}
/// Compares the gas computed by PathGasMeter for the given signature (but unknown arguments)
@@ -90,12 +91,12 @@ public:
gas = max(gas, gasForTransaction(hash.asBytes() + arguments, false));
}
- gas += GasEstimator::functionalEstimation(
+ gas += GasEstimator(dev::test::Options::get().evmVersion()).functionalEstimation(
*m_compiler.runtimeAssemblyItems(m_compiler.lastContractName()),
_sig
);
BOOST_REQUIRE(!gas.isInfinite);
- BOOST_CHECK(gas.value == m_gasUsed);
+ BOOST_CHECK_EQUAL(gas.value, m_gasUsed);
}
static GasMeter::GasConsumption gasForTransaction(bytes const& _data, bool _isCreation)
diff --git a/test/libsolidity/Imports.cpp b/test/libsolidity/Imports.cpp
index dc1174f4..bc81b3b1 100644
--- a/test/libsolidity/Imports.cpp
+++ b/test/libsolidity/Imports.cpp
@@ -21,6 +21,7 @@
*/
#include <test/libsolidity/ErrorCheck.h>
+#include <test/TestHelper.h>
#include <libsolidity/interface/Exceptions.h>
#include <libsolidity/interface/CompilerStack.h>
@@ -44,6 +45,7 @@ BOOST_AUTO_TEST_CASE(smoke_test)
{
CompilerStack c;
c.addSource("a", "contract C {} pragma solidity >=0.0;");
+ c.setEVMVersion(dev::test::Options::get().evmVersion());
BOOST_CHECK(c.compile());
}
@@ -52,6 +54,7 @@ BOOST_AUTO_TEST_CASE(regular_import)
CompilerStack c;
c.addSource("a", "contract C {} pragma solidity >=0.0;");
c.addSource("b", "import \"a\"; contract D is C {} pragma solidity >=0.0;");
+ c.setEVMVersion(dev::test::Options::get().evmVersion());
BOOST_CHECK(c.compile());
}
@@ -60,6 +63,7 @@ BOOST_AUTO_TEST_CASE(import_does_not_clutter_importee)
CompilerStack c;
c.addSource("a", "contract C { D d; } pragma solidity >=0.0;");
c.addSource("b", "import \"a\"; contract D is C {} pragma solidity >=0.0;");
+ c.setEVMVersion(dev::test::Options::get().evmVersion());
BOOST_CHECK(!c.compile());
}
@@ -69,6 +73,7 @@ BOOST_AUTO_TEST_CASE(import_is_transitive)
c.addSource("a", "contract C { } pragma solidity >=0.0;");
c.addSource("b", "import \"a\"; pragma solidity >=0.0;");
c.addSource("c", "import \"b\"; contract D is C {} pragma solidity >=0.0;");
+ c.setEVMVersion(dev::test::Options::get().evmVersion());
BOOST_CHECK(c.compile());
}
@@ -77,6 +82,7 @@ BOOST_AUTO_TEST_CASE(circular_import)
CompilerStack c;
c.addSource("a", "import \"b\"; contract C { D d; } pragma solidity >=0.0;");
c.addSource("b", "import \"a\"; contract D { C c; } pragma solidity >=0.0;");
+ c.setEVMVersion(dev::test::Options::get().evmVersion());
BOOST_CHECK(c.compile());
}
@@ -86,6 +92,7 @@ BOOST_AUTO_TEST_CASE(relative_import)
c.addSource("a", "import \"./dir/b\"; contract A is B {} pragma solidity >=0.0;");
c.addSource("dir/b", "contract B {} pragma solidity >=0.0;");
c.addSource("dir/c", "import \"../a\"; contract C is A {} pragma solidity >=0.0;");
+ c.setEVMVersion(dev::test::Options::get().evmVersion());
BOOST_CHECK(c.compile());
}
@@ -94,6 +101,7 @@ BOOST_AUTO_TEST_CASE(relative_import_multiplex)
CompilerStack c;
c.addSource("a", "contract A {} pragma solidity >=0.0;");
c.addSource("dir/a/b/c", "import \"../../.././a\"; contract B is A {} pragma solidity >=0.0;");
+ c.setEVMVersion(dev::test::Options::get().evmVersion());
BOOST_CHECK(c.compile());
}
@@ -102,6 +110,7 @@ BOOST_AUTO_TEST_CASE(simple_alias)
CompilerStack c;
c.addSource("a", "contract A {} pragma solidity >=0.0;");
c.addSource("dir/a/b/c", "import \"../../.././a\" as x; contract B is x.A { function() { x.A r = x.A(20); } } pragma solidity >=0.0;");
+ c.setEVMVersion(dev::test::Options::get().evmVersion());
BOOST_CHECK(c.compile());
}
@@ -111,6 +120,7 @@ BOOST_AUTO_TEST_CASE(library_name_clash)
c.addSource("a", "library A {} pragma solidity >=0.0;");
c.addSource("b", "library A {} pragma solidity >=0.0;");
c.addSource("c", "import {A} from \"./a\"; import {A} from \"./b\";");
+ c.setEVMVersion(dev::test::Options::get().evmVersion());
BOOST_CHECK(!c.compile());
}
@@ -119,6 +129,7 @@ BOOST_AUTO_TEST_CASE(library_name_clash_with_contract)
CompilerStack c;
c.addSource("a", "contract A {} pragma solidity >=0.0;");
c.addSource("b", "library A {} pragma solidity >=0.0;");
+ c.setEVMVersion(dev::test::Options::get().evmVersion());
BOOST_CHECK(c.compile());
}
@@ -128,6 +139,7 @@ BOOST_AUTO_TEST_CASE(complex_import)
c.addSource("a", "contract A {} contract B {} contract C { struct S { uint a; } } pragma solidity >=0.0;");
c.addSource("b", "import \"a\" as x; import {B as b, C as c, C} from \"a\"; "
"contract D is b { function f(c.S var1, x.C.S var2, C.S var3) internal {} } pragma solidity >=0.0;");
+ c.setEVMVersion(dev::test::Options::get().evmVersion());
BOOST_CHECK(c.compile());
}
@@ -136,14 +148,19 @@ BOOST_AUTO_TEST_CASE(name_clash_in_import)
CompilerStack c;
c.addSource("a", "contract A {} pragma solidity >=0.0;");
c.addSource("b", "import \"a\"; contract A {} pragma solidity >=0.0;");
+ c.setEVMVersion(dev::test::Options::get().evmVersion());
BOOST_CHECK(!c.compile());
c.addSource("b", "import \"a\" as A; contract A {} pragma solidity >=0.0;");
+ c.setEVMVersion(dev::test::Options::get().evmVersion());
BOOST_CHECK(!c.compile());
c.addSource("b", "import {A as b} from \"a\"; contract b {} pragma solidity >=0.0;");
+ c.setEVMVersion(dev::test::Options::get().evmVersion());
BOOST_CHECK(!c.compile());
c.addSource("b", "import {A} from \"a\"; contract A {} pragma solidity >=0.0;");
+ c.setEVMVersion(dev::test::Options::get().evmVersion());
BOOST_CHECK(!c.compile());
c.addSource("b", "import {A} from \"a\"; contract B {} pragma solidity >=0.0;");
+ c.setEVMVersion(dev::test::Options::get().evmVersion());
BOOST_CHECK(c.compile());
}
@@ -155,6 +172,7 @@ BOOST_AUTO_TEST_CASE(remappings)
c.addSource("b", "import \"t/tee.sol\"; contract A is Tee {} pragma solidity >=0.0;");
c.addSource("s_1.4.6/s.sol", "contract S {} pragma solidity >=0.0;");
c.addSource("Tee/tee.sol", "contract Tee {} pragma solidity >=0.0;");
+ c.setEVMVersion(dev::test::Options::get().evmVersion());
BOOST_CHECK(c.compile());
}
@@ -166,6 +184,7 @@ BOOST_AUTO_TEST_CASE(context_dependent_remappings)
c.addSource("b/b.sol", "import \"s/s.sol\"; contract B is SSeven {} pragma solidity >=0.0;");
c.addSource("s_1.4.6/s.sol", "contract SSix {} pragma solidity >=0.0;");
c.addSource("s_1.4.7/s.sol", "contract SSeven {} pragma solidity >=0.0;");
+ c.setEVMVersion(dev::test::Options::get().evmVersion());
BOOST_CHECK(c.compile());
}
@@ -174,6 +193,7 @@ BOOST_AUTO_TEST_CASE(filename_with_period)
CompilerStack c;
c.addSource("a/a.sol", "import \".b.sol\"; contract A is B {} pragma solidity >=0.0;");
c.addSource("a/.b.sol", "contract B {} pragma solidity >=0.0;");
+ c.setEVMVersion(dev::test::Options::get().evmVersion());
BOOST_CHECK(!c.compile());
}
@@ -185,6 +205,7 @@ BOOST_AUTO_TEST_CASE(context_dependent_remappings_ensure_default_and_module_pres
c.addSource("vendor/bar/bar.sol", "import \"foo/foo.sol\"; contract Bar {Foo1 foo;} pragma solidity >=0.0;");
c.addSource("vendor/foo_1.0.0/foo.sol", "contract Foo1 {} pragma solidity >=0.0;");
c.addSource("vendor/foo_2.0.0/foo.sol", "contract Foo2 {} pragma solidity >=0.0;");
+ c.setEVMVersion(dev::test::Options::get().evmVersion());
BOOST_CHECK(c.compile());
}
@@ -196,6 +217,7 @@ BOOST_AUTO_TEST_CASE(context_dependent_remappings_order_independent)
c.addSource("a/b/main.sol", "import \"x/y/z/z.sol\"; contract Main is E {} pragma solidity >=0.0;");
c.addSource("d/z.sol", "contract D {} pragma solidity >=0.0;");
c.addSource("e/y/z/z.sol", "contract E {} pragma solidity >=0.0;");
+ c.setEVMVersion(dev::test::Options::get().evmVersion());
BOOST_CHECK(c.compile());
CompilerStack d;
d.setRemappings(vector<string>{"a/b:x=e", "a:x/y/z=d"});
@@ -203,6 +225,7 @@ BOOST_AUTO_TEST_CASE(context_dependent_remappings_order_independent)
d.addSource("a/b/main.sol", "import \"x/y/z/z.sol\"; contract Main is E {} pragma solidity >=0.0;");
d.addSource("d/z.sol", "contract D {} pragma solidity >=0.0;");
d.addSource("e/y/z/z.sol", "contract E {} pragma solidity >=0.0;");
+ d.setEVMVersion(dev::test::Options::get().evmVersion());
BOOST_CHECK(d.compile());
}
@@ -212,6 +235,7 @@ BOOST_AUTO_TEST_CASE(shadowing_via_import)
c.addSource("a", "library A {} pragma solidity >=0.0;");
c.addSource("b", "library A {} pragma solidity >=0.0;");
c.addSource("c", "import {A} from \"./a\"; import {A} from \"./b\";");
+ c.setEVMVersion(dev::test::Options::get().evmVersion());
BOOST_CHECK(!c.compile());
}
@@ -225,6 +249,7 @@ BOOST_AUTO_TEST_CASE(shadowing_builtins_with_imports)
contract C {
}
)");
+ c.setEVMVersion(dev::test::Options::get().evmVersion());
BOOST_CHECK(c.compile());
size_t errorCount = 0;
for (auto const& e: c.errors())
@@ -251,6 +276,7 @@ BOOST_AUTO_TEST_CASE(shadowing_builtins_with_multiple_imports)
contract C {
}
)");
+ c.setEVMVersion(dev::test::Options::get().evmVersion());
BOOST_CHECK(c.compile());
auto numErrors = c.errors().size();
// Sometimes we get the prerelease warning, sometimes not.
@@ -274,6 +300,7 @@ BOOST_AUTO_TEST_CASE(shadowing_builtins_with_alias)
pragma solidity >=0.0;
import {C as msg} from "B.sol";
)");
+ c.setEVMVersion(dev::test::Options::get().evmVersion());
BOOST_CHECK(c.compile());
auto numErrors = c.errors().size();
// Sometimes we get the prerelease warning, sometimes not.
diff --git a/test/libsolidity/InlineAssembly.cpp b/test/libsolidity/InlineAssembly.cpp
index ea120657..a4dcc4d5 100644
--- a/test/libsolidity/InlineAssembly.cpp
+++ b/test/libsolidity/InlineAssembly.cpp
@@ -55,7 +55,7 @@ boost::optional<Error> parseAndReturnFirstError(
AssemblyStack::Machine _machine = AssemblyStack::Machine::EVM
)
{
- AssemblyStack stack(_language);
+ AssemblyStack stack(dev::test::Options::get().evmVersion(), _language);
bool success = false;
try
{
@@ -117,7 +117,7 @@ Error expectError(
void parsePrintCompare(string const& _source, bool _canWarn = false)
{
- AssemblyStack stack;
+ AssemblyStack stack(dev::test::Options::get().evmVersion());
BOOST_REQUIRE(stack.parseAndAnalyze("", _source));
if (_canWarn)
BOOST_REQUIRE(Error::containsOnlyWarnings(stack.errors()));
@@ -567,7 +567,7 @@ BOOST_AUTO_TEST_CASE(print_string_literal_unicode)
{
string source = "{ let x := \"\\u1bac\" }";
string parsed = "{\n let x := \"\\xe1\\xae\\xac\"\n}";
- AssemblyStack stack;
+ AssemblyStack stack(dev::test::Options::get().evmVersion());
BOOST_REQUIRE(stack.parseAndAnalyze("", source));
BOOST_REQUIRE(stack.errors().empty());
BOOST_CHECK_EQUAL(stack.print(), parsed);
@@ -783,9 +783,9 @@ BOOST_AUTO_TEST_CASE(shift)
BOOST_AUTO_TEST_CASE(shift_constantinople_warning)
{
- CHECK_PARSE_WARNING("{ pop(shl(10, 32)) }", Warning, "The \"shl\" instruction is only available after the Constantinople hard fork");
- CHECK_PARSE_WARNING("{ pop(shr(10, 32)) }", Warning, "The \"shr\" instruction is only available after the Constantinople hard fork");
- CHECK_PARSE_WARNING("{ pop(sar(10, 32)) }", Warning, "The \"sar\" instruction is only available after the Constantinople hard fork");
+ CHECK_PARSE_WARNING("{ pop(shl(10, 32)) }", Warning, "The \"shl\" instruction is only available for Constantinople-compatible VMs.");
+ CHECK_PARSE_WARNING("{ pop(shr(10, 32)) }", Warning, "The \"shr\" instruction is only available for Constantinople-compatible VMs.");
+ CHECK_PARSE_WARNING("{ pop(sar(10, 32)) }", Warning, "The \"sar\" instruction is only available for Constantinople-compatible VMs.");
}
BOOST_AUTO_TEST_CASE(jump_warning)
diff --git a/test/libsolidity/Metadata.cpp b/test/libsolidity/Metadata.cpp
index 47cf1d3d..f1edeeb7 100644
--- a/test/libsolidity/Metadata.cpp
+++ b/test/libsolidity/Metadata.cpp
@@ -46,6 +46,7 @@ BOOST_AUTO_TEST_CASE(metadata_stamp)
)";
CompilerStack compilerStack;
compilerStack.addSource("", std::string(sourceCode));
+ compilerStack.setEVMVersion(dev::test::Options::get().evmVersion());
compilerStack.setOptimiserSettings(dev::test::Options::get().optimize);
BOOST_REQUIRE_MESSAGE(compilerStack.compile(), "Compiling contract failed");
bytes const& bytecode = compilerStack.runtimeObject("test").bytecode;
@@ -72,6 +73,7 @@ BOOST_AUTO_TEST_CASE(metadata_stamp_experimental)
)";
CompilerStack compilerStack;
compilerStack.addSource("", std::string(sourceCode));
+ compilerStack.setEVMVersion(dev::test::Options::get().evmVersion());
compilerStack.setOptimiserSettings(dev::test::Options::get().optimize);
BOOST_REQUIRE_MESSAGE(compilerStack.compile(), "Compiling contract failed");
bytes const& bytecode = compilerStack.runtimeObject("test").bytecode;
@@ -106,6 +108,7 @@ BOOST_AUTO_TEST_CASE(metadata_relevant_sources)
}
)";
compilerStack.addSource("B", std::string(sourceCode));
+ compilerStack.setEVMVersion(dev::test::Options::get().evmVersion());
compilerStack.setOptimiserSettings(dev::test::Options::get().optimize);
BOOST_REQUIRE_MESSAGE(compilerStack.compile(), "Compiling contract failed");
@@ -144,6 +147,7 @@ BOOST_AUTO_TEST_CASE(metadata_relevant_sources_imports)
}
)";
compilerStack.addSource("C", std::string(sourceCode));
+ compilerStack.setEVMVersion(dev::test::Options::get().evmVersion());
compilerStack.setOptimiserSettings(dev::test::Options::get().optimize);
BOOST_REQUIRE_MESSAGE(compilerStack.compile(), "Compiling contract failed");
diff --git a/test/libsolidity/SolidityABIJSON.cpp b/test/libsolidity/SolidityABIJSON.cpp
index e242508a..0d471b32 100644
--- a/test/libsolidity/SolidityABIJSON.cpp
+++ b/test/libsolidity/SolidityABIJSON.cpp
@@ -44,6 +44,8 @@ public:
{
m_compilerStack.reset(false);
m_compilerStack.addSource("", "pragma solidity >=0.0;\n" + _code);
+ m_compilerStack.setEVMVersion(dev::test::Options::get().evmVersion());
+ m_compilerStack.setOptimiserSettings(dev::test::Options::get().optimize);
BOOST_REQUIRE_MESSAGE(m_compilerStack.parseAndAnalyze(), "Parsing contract failed");
Json::Value generatedInterface = m_compilerStack.contractABI(m_compilerStack.lastContractName());
diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp
index c352a2c2..ebb2f3ff 100644
--- a/test/libsolidity/SolidityEndToEndTest.cpp
+++ b/test/libsolidity/SolidityEndToEndTest.cpp
@@ -5352,6 +5352,18 @@ BOOST_AUTO_TEST_CASE(super_overload)
ABI_CHECK(callContractFunction("h()"), encodeArgs(2));
}
+BOOST_AUTO_TEST_CASE(gasleft_shadow_resolution)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function gasleft() returns(uint256) { return 0; }
+ function f() returns(uint256) { return gasleft(); }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "C");
+ ABI_CHECK(callContractFunction("f()"), encodeArgs(0));
+}
+
BOOST_AUTO_TEST_CASE(bool_conversion)
{
char const* sourceCode = R"(
diff --git a/test/libsolidity/SolidityExecutionFramework.h b/test/libsolidity/SolidityExecutionFramework.h
index f562721d..12687dd1 100644
--- a/test/libsolidity/SolidityExecutionFramework.h
+++ b/test/libsolidity/SolidityExecutionFramework.h
@@ -68,6 +68,7 @@ public:
m_compiler.reset(false);
m_compiler.addSource("", sourceCode);
m_compiler.setLibraries(_libraryAddresses);
+ m_compiler.setEVMVersion(m_evmVersion);
m_compiler.setOptimiserSettings(m_optimize, m_optimizeRuns);
if (!m_compiler.compile())
{
diff --git a/test/libsolidity/SolidityExpressionCompiler.cpp b/test/libsolidity/SolidityExpressionCompiler.cpp
index e2a0c3cd..5f044b44 100644
--- a/test/libsolidity/SolidityExpressionCompiler.cpp
+++ b/test/libsolidity/SolidityExpressionCompiler.cpp
@@ -132,7 +132,7 @@ bytes compileFirstExpression(
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
{
ErrorReporter errorReporter(errors);
- TypeChecker typeChecker(errorReporter);
+ TypeChecker typeChecker(dev::test::Options::get().evmVersion(), errorReporter);
BOOST_REQUIRE(typeChecker.checkTypeRequirements(*contract));
}
for (ASTPointer<ASTNode> const& node: sourceUnit->nodes())
@@ -141,7 +141,7 @@ bytes compileFirstExpression(
FirstExpressionExtractor extractor(*contract);
BOOST_REQUIRE(extractor.expression() != nullptr);
- CompilerContext context;
+ CompilerContext context(dev::test::Options::get().evmVersion());
context.resetVisitedNodes(contract);
context.setInheritanceHierarchy(inheritanceHierarchy);
unsigned parametersSize = _localVariables.size(); // assume they are all one slot on the stack
@@ -515,6 +515,39 @@ BOOST_AUTO_TEST_CASE(blockhash)
BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
}
+BOOST_AUTO_TEST_CASE(gas_left)
+{
+ char const* sourceCode = R"(
+ contract test {
+ function f() returns (uint256 val) {
+ return msg.gas;
+ }
+ }
+ )";
+ bytes code = compileFirstExpression(
+ sourceCode, {}, {},
+ {make_shared<MagicVariableDeclaration>("msg", make_shared<MagicType>(MagicType::Kind::Message))}
+ );
+
+ bytes expectation({byte(Instruction::GAS)});
+ BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
+
+ sourceCode = R"(
+ contract test {
+ function f() returns (uint256 val) {
+ return gasleft();
+ }
+ }
+ )";
+ code = compileFirstExpression(
+ sourceCode, {}, {},
+ {make_shared<MagicVariableDeclaration>("gasleft", make_shared<FunctionType>(strings(), strings{"uint256"}, FunctionType::Kind::GasLeft))}
+ );
+
+ expectation = bytes({byte(Instruction::GAS)});
+ BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
+}
+
BOOST_AUTO_TEST_SUITE_END()
}
diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp
index 1fadcbde..87d62973 100644
--- a/test/libsolidity/SolidityNameAndTypeResolution.cpp
+++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp
@@ -22,6 +22,8 @@
#include <test/libsolidity/AnalysisFramework.h>
+#include <test/TestHelper.h>
+
#include <libsolidity/ast/AST.h>
#include <libdevcore/SHA3.h>
@@ -100,7 +102,6 @@ BOOST_AUTO_TEST_CASE(double_variable_declaration_050)
)";
CHECK_WARNING_ALLOW_MULTI(text, (vector<string>{
"This declaration shadows an existing declaration.",
- "Experimental features",
"Unused local variable",
"Unused local variable"
}));
@@ -131,7 +132,6 @@ BOOST_AUTO_TEST_CASE(double_variable_declaration_disjoint_scope_050)
}
)";
CHECK_WARNING_ALLOW_MULTI(text, (vector<string>{
- "Experimental features",
"Unused local variable",
"Unused local variable"
}));
@@ -162,7 +162,6 @@ BOOST_AUTO_TEST_CASE(double_variable_declaration_disjoint_scope_activation_050)
}
)";
CHECK_WARNING_ALLOW_MULTI(text, (vector<string>{
- "Experimental features",
"Unused local variable",
"Unused local variable"
}));
@@ -260,7 +259,7 @@ BOOST_AUTO_TEST_CASE(scoping_for)
}
}
)";
- CHECK_WARNING(text, "Experimental features");
+ CHECK_SUCCESS_NO_WARNINGS(text);
}
BOOST_AUTO_TEST_CASE(scoping_for2)
@@ -274,7 +273,7 @@ BOOST_AUTO_TEST_CASE(scoping_for2)
}
}
)";
- CHECK_WARNING(text, "Experimental features");
+ CHECK_SUCCESS_NO_WARNINGS(text);
}
BOOST_AUTO_TEST_CASE(scoping_for3)
@@ -2773,6 +2772,25 @@ BOOST_AUTO_TEST_CASE(explicit_conversion_from_decimal_to_bytesxx)
CHECK_SUCCESS_NO_WARNINGS(text);
}
+BOOST_AUTO_TEST_CASE(combining_hex_and_denomination)
+{
+ char const* text = R"(
+ contract Foo {
+ uint constant x = 0x01 wei;
+ }
+ )";
+ CHECK_WARNING(text, "Hexadecimal numbers with unit denominations are deprecated.");
+
+ char const* textV050 = R"(
+ pragma experimental "v0.5.0";
+
+ contract Foo {
+ uint constant x = 0x01 wei;
+ }
+ )";
+ CHECK_ERROR(textV050, TypeError, "Hexadecimal numbers cannot be used with unit denominations.");
+}
+
BOOST_AUTO_TEST_CASE(assigning_value_to_const_variable)
{
char const* text = R"(
@@ -7137,11 +7155,13 @@ BOOST_AUTO_TEST_CASE(returndatacopy_as_variable)
char const* text = R"(
contract c { function f() public { uint returndatasize; assembly { returndatasize }}}
)";
- CHECK_ALLOW_MULTI(text, (std::vector<std::pair<Error::Type, std::string>>{
+ vector<pair<Error::Type, std::string>> expectations(vector<pair<Error::Type, std::string>>{
{Error::Type::Warning, "Variable is shadowed in inline assembly by an instruction of the same name"},
- {Error::Type::DeclarationError, "Unbalanced stack"},
- {Error::Type::Warning, "only available after the Metropolis"}
- }));
+ {Error::Type::DeclarationError, "Unbalanced stack"}
+ });
+ if (!dev::test::Options::get().evmVersion().supportsReturndata())
+ expectations.emplace_back(make_pair(Error::Type::Warning, std::string("\"returndatasize\" instruction is only available for Byzantium-compatible")));
+ CHECK_ALLOW_MULTI(text, expectations);
}
BOOST_AUTO_TEST_CASE(create2_as_variable)
@@ -7151,7 +7171,7 @@ BOOST_AUTO_TEST_CASE(create2_as_variable)
)";
CHECK_ALLOW_MULTI(text, (std::vector<std::pair<Error::Type, std::string>>{
{Error::Type::Warning, "Variable is shadowed in inline assembly by an instruction of the same name"},
- {Error::Type::Warning, "only available after the Metropolis"},
+ {Error::Type::Warning, "The \"create2\" instruction is not supported by the VM version"},
{Error::Type::DeclarationError, "Unbalanced stack"}
}));
}
@@ -7446,6 +7466,50 @@ BOOST_AUTO_TEST_CASE(builtin_reject_gas)
CHECK_ERROR(text, TypeError, "Member \"gas\" not found or not visible after argument-dependent lookup");
}
+BOOST_AUTO_TEST_CASE(gasleft)
+{
+ char const* text = R"(
+ contract C {
+ function f() public view returns (uint256 val) { return msg.gas; }
+ }
+ )";
+ CHECK_WARNING(text, "\"msg.gas\" has been deprecated in favor of \"gasleft()\"");
+
+ text = R"(
+ contract C {
+ function f() public view returns (uint256 val) { return gasleft(); }
+ }
+ )";
+ CHECK_SUCCESS_NO_WARNINGS(text);
+
+ text = R"(
+ pragma experimental "v0.5.0";
+ contract C {
+ function f() public returns (uint256 val) { return msg.gas; }
+ }
+ )";
+ CHECK_ERROR(text, TypeError, "\"msg.gas\" has been deprecated in favor of \"gasleft()\"");
+}
+
+BOOST_AUTO_TEST_CASE(gasleft_shadowing)
+{
+ char const* text = R"(
+ contract C {
+ function gasleft() public pure returns (bytes32 val) { return "abc"; }
+ function f() public pure returns (bytes32 val) { return gasleft(); }
+ }
+ )";
+ CHECK_WARNING(text, "This declaration shadows a builtin symbol.");
+
+ text = R"(
+ contract C {
+ uint gasleft;
+ function f() public { gasleft = 42; }
+ }
+ )";
+ CHECK_WARNING(text, "This declaration shadows a builtin symbol.");
+}
+
BOOST_AUTO_TEST_CASE(builtin_reject_value)
{
char const* text = R"(
@@ -7707,7 +7771,7 @@ BOOST_AUTO_TEST_CASE(non_external_fallback)
function () external { }
}
)";
- CHECK_WARNING(text, "Experimental features are turned on.");
+ CHECK_SUCCESS_NO_WARNINGS(text);
text = R"(
pragma experimental "v0.5.0";
contract C {
diff --git a/test/libsolidity/SolidityNatspecJSON.cpp b/test/libsolidity/SolidityNatspecJSON.cpp
index e8906bb9..49a725e0 100644
--- a/test/libsolidity/SolidityNatspecJSON.cpp
+++ b/test/libsolidity/SolidityNatspecJSON.cpp
@@ -47,6 +47,7 @@ public:
{
m_compilerStack.reset(false);
m_compilerStack.addSource("", "pragma solidity >=0.0;\n" + _code);
+ m_compilerStack.setEVMVersion(dev::test::Options::get().evmVersion());
BOOST_REQUIRE_MESSAGE(m_compilerStack.parseAndAnalyze(), "Parsing contract failed");
Json::Value generatedDocumentation;
@@ -67,6 +68,7 @@ public:
{
m_compilerStack.reset(false);
m_compilerStack.addSource("", "pragma solidity >=0.0;\n" + _code);
+ m_compilerStack.setEVMVersion(dev::test::Options::get().evmVersion());
BOOST_CHECK(!m_compilerStack.parseAndAnalyze());
BOOST_REQUIRE(Error::containsErrorOfType(m_compilerStack.errors(), Error::Type::DocstringParsingError));
}
diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp
index b7097d0f..f03b30e1 100644
--- a/test/libsolidity/SolidityParser.cpp
+++ b/test/libsolidity/SolidityParser.cpp
@@ -1164,6 +1164,36 @@ BOOST_AUTO_TEST_CASE(constant_is_keyword)
CHECK_PARSE_ERROR(text, "Expected identifier");
}
+BOOST_AUTO_TEST_CASE(keyword_is_reserved)
+{
+ auto keywords = {
+ "abstract",
+ "after",
+ "case",
+ "catch",
+ "default",
+ "final",
+ "in",
+ "inline",
+ "let",
+ "match",
+ "null",
+ "of",
+ "relocatable",
+ "static",
+ "switch",
+ "try",
+ "type",
+ "typeof"
+ };
+
+ for (const auto& keyword: keywords)
+ {
+ auto text = std::string("contract ") + keyword + " {}";
+ CHECK_PARSE_ERROR(text.c_str(), "Expected identifier");
+ }
+}
+
BOOST_AUTO_TEST_CASE(var_array)
{
char const* text = R"(
diff --git a/test/libsolidity/StandardCompiler.cpp b/test/libsolidity/StandardCompiler.cpp
index eb2773ba..4c8918be 100644
--- a/test/libsolidity/StandardCompiler.cpp
+++ b/test/libsolidity/StandardCompiler.cpp
@@ -724,6 +724,43 @@ BOOST_AUTO_TEST_CASE(library_linking)
BOOST_CHECK(contract["evm"]["bytecode"]["linkReferences"]["library2.sol"]["L2"][0].isObject());
}
+BOOST_AUTO_TEST_CASE(evm_version)
+{
+ auto inputForVersion = [](string const& _version)
+ {
+ return R"(
+ {
+ "language": "Solidity",
+ "sources": { "fileA": { "content": "contract A { }" } },
+ "settings": {
+ )" + _version + R"(
+ "outputSelection": {
+ "fileA": {
+ "A": [ "metadata" ]
+ }
+ }
+ }
+ }
+ )";
+ };
+ Json::Value result;
+ result = compile(inputForVersion("\"evmVersion\": \"homestead\","));
+ BOOST_CHECK(result["contracts"]["fileA"]["A"]["metadata"].asString().find("\"evmVersion\":\"homestead\"") != string::npos);
+ result = compile(inputForVersion("\"evmVersion\": \"tangerineWhistle\","));
+ BOOST_CHECK(result["contracts"]["fileA"]["A"]["metadata"].asString().find("\"evmVersion\":\"tangerineWhistle\"") != string::npos);
+ result = compile(inputForVersion("\"evmVersion\": \"spuriousDragon\","));
+ BOOST_CHECK(result["contracts"]["fileA"]["A"]["metadata"].asString().find("\"evmVersion\":\"spuriousDragon\"") != string::npos);
+ result = compile(inputForVersion("\"evmVersion\": \"byzantium\","));
+ BOOST_CHECK(result["contracts"]["fileA"]["A"]["metadata"].asString().find("\"evmVersion\":\"byzantium\"") != string::npos);
+ // test default
+ result = compile(inputForVersion(""));
+ BOOST_CHECK(result["contracts"]["fileA"]["A"]["metadata"].asString().find("\"evmVersion\":\"byzantium\"") != string::npos);
+ // test invalid
+ result = compile(inputForVersion("\"evmVersion\": \"invalid\","));
+ BOOST_CHECK(result["errors"][0]["message"].asString() == "Invalid EVM version requested.");
+}
+
+
BOOST_AUTO_TEST_SUITE_END()
}
diff --git a/test/libsolidity/ViewPureChecker.cpp b/test/libsolidity/ViewPureChecker.cpp
index 2599ca28..ed4cf792 100644
--- a/test/libsolidity/ViewPureChecker.cpp
+++ b/test/libsolidity/ViewPureChecker.cpp
@@ -20,6 +20,8 @@
#include <test/libsolidity/AnalysisFramework.h>
+#include <test/TestHelper.h>
+
#include <boost/test/unit_test.hpp>
#include <string>
@@ -109,6 +111,7 @@ BOOST_AUTO_TEST_CASE(environment_access)
"block.difficulty",
"block.number",
"block.gaslimit",
+ "gasleft()",
"msg.gas",
"msg.value",
"msg.sender",
@@ -423,7 +426,10 @@ BOOST_AUTO_TEST_CASE(assembly_staticcall)
}
}
)";
- CHECK_WARNING(text, "only available after the Metropolis");
+ if (!dev::test::Options::get().evmVersion().hasStaticCall())
+ CHECK_WARNING(text, "\"staticcall\" instruction is only available for Byzantium-compatible");
+ else
+ CHECK_SUCCESS_NO_WARNINGS(text);
}
BOOST_AUTO_TEST_CASE(assembly_jump)