aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Changelog.md6
-rw-r--r--cmake/UseDev.cmake1
m---------deps0
-rw-r--r--docs/assembly.rst2
-rw-r--r--docs/bugs.json7
-rw-r--r--docs/bugs_by_version.json33
-rw-r--r--docs/contributing.rst19
-rw-r--r--docs/control-structures.rst65
-rw-r--r--docs/grammar.txt25
-rw-r--r--docs/installing-solidity.rst2
-rw-r--r--docs/miscellaneous.rst6
-rw-r--r--docs/solidity-by-example.rst8
-rw-r--r--docs/units-and-global-variables.rst19
-rw-r--r--libdevcore/Assertions.h34
-rw-r--r--libdevcore/Common.h2
-rw-r--r--libdevcore/CommonIO.cpp23
-rw-r--r--libdevcore/Exceptions.h4
-rw-r--r--libdevcore/Whiskers.cpp127
-rw-r--r--libdevcore/Whiskers.h87
-rw-r--r--libdevcore/debugbreak.h128
-rw-r--r--libevmasm/Assembly.cpp16
-rw-r--r--libevmasm/Assembly.h13
-rw-r--r--libjulia/backends/evm/AbstractAssembly.h3
-rw-r--r--libjulia/backends/evm/EVMAssembly.cpp29
-rw-r--r--libjulia/backends/evm/EVMAssembly.h4
-rw-r--r--libjulia/backends/evm/EVMCodeTransform.cpp2
-rw-r--r--liblll/All.h6
-rw-r--r--liblll/CodeFragment.cpp45
-rw-r--r--liblll/CompilerState.cpp10
-rw-r--r--libsolidity/analysis/TypeChecker.cpp19
-rw-r--r--libsolidity/ast/AST.cpp2
-rw-r--r--libsolidity/ast/AST.h1
-rw-r--r--libsolidity/ast/ASTJsonConverter.cpp1
-rw-r--r--libsolidity/ast/ASTJsonConverter.h1
-rw-r--r--libsolidity/ast/Types.cpp1
-rw-r--r--libsolidity/codegen/ArrayUtils.cpp2
-rw-r--r--libsolidity/codegen/CompilerContext.cpp15
-rw-r--r--libsolidity/codegen/CompilerContext.h10
-rw-r--r--libsolidity/codegen/CompilerUtils.cpp17
-rw-r--r--libsolidity/codegen/CompilerUtils.h10
-rw-r--r--libsolidity/codegen/ContractCompiler.cpp9
-rw-r--r--libsolidity/codegen/ExpressionCompiler.cpp17
-rw-r--r--libsolidity/codegen/ExpressionCompiler.h2
-rw-r--r--libsolidity/inlineasm/AsmAnalysis.cpp1
-rw-r--r--libsolidity/inlineasm/AsmCodeGen.cpp5
-rw-r--r--libsolidity/inlineasm/AsmPrinter.cpp2
-rw-r--r--libsolidity/inlineasm/AsmScopeFiller.cpp2
-rw-r--r--libsolidity/interface/CompilerStack.cpp10
-rw-r--r--libsolidity/interface/CompilerStack.h10
-rw-r--r--libsolidity/interface/Exceptions.cpp1
-rw-r--r--libsolidity/interface/Exceptions.h11
-rw-r--r--libsolidity/interface/StandardCompiler.cpp10
-rw-r--r--libsolidity/interface/Utils.h45
-rw-r--r--libsolidity/interface/Version.cpp2
-rw-r--r--libsolidity/parsing/DocStringParser.cpp2
-rw-r--r--libsolidity/parsing/ParserBase.cpp20
-rw-r--r--libsolidity/parsing/ParserBase.h9
-rw-r--r--libsolidity/parsing/Scanner.cpp2
-rw-r--r--libsolidity/parsing/Token.h1
-rwxr-xr-xscripts/bytecodecompare/prepare_report.py2
-rw-r--r--scripts/bytecodecompare/storebytecode.bat7
-rw-r--r--solc/CommandLineInterface.cpp7
-rw-r--r--solc/jsonCompiler.cpp209
-rw-r--r--test/libdevcore/MiniMoustache.cpp127
-rw-r--r--test/liblll/EndToEndTest.cpp146
-rw-r--r--test/libsolidity/Assembly.cpp4
-rw-r--r--test/libsolidity/JSONCompiler.cpp26
-rw-r--r--test/libsolidity/SolidityEndToEndTest.cpp23
-rw-r--r--test/libsolidity/SolidityNameAndTypeResolution.cpp30
-rw-r--r--test/libsolidity/StandardCompiler.cpp30
70 files changed, 1047 insertions, 530 deletions
diff --git a/Changelog.md b/Changelog.md
index 0fb2fe5c..6e15c9db 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -5,17 +5,23 @@ Features:
* Assembly: Display auxiliary data in the assembly output.
* Assembly: Add ``CREATE2`` (EIP86), ``STATICCALL`` (EIP214), ``RETURNDATASIZE`` and ``RETURNDATACOPY`` (EIP211) instructions.
* AST: export all attributes to JSON format.
+ * C API (``jsonCompiler``): Use the Standard JSON I/O internally.
* Inline Assembly: Present proper error message when not supplying enough arguments to a functional
instruction.
* Inline Assembly: introduce ``keccak256`` as an opcode. ``sha3`` is still a valid alias.
* Inline Assembly: ``for`` and ``switch`` statements.
* Inline Assembly: function definitions and function calls.
+ * Code Generator: Added the Whiskers template system.
Bugfixes:
+ * Code generator: Use ``REVERT`` instead of ``INVALID`` for generated input validation routines.
+ * Type Checker: Fix address literals not being treated as compile-time constants.
* Type Checker: Make UTF8-validation a bit more sloppy to include more valid sequences.
* Fixed crash concerning non-callable types.
* Unused variable warnings no longer issued for variables used inside inline assembly.
+ * Code Generator: Fix ABI encoding of empty literal string.
* Inline Assembly: Enforce function arguments when parsing functional instructions.
+ * Fixed segfault with constant function parameters
### 0.4.11 (2017-05-03)
diff --git a/cmake/UseDev.cmake b/cmake/UseDev.cmake
index 4461a8a0..68df691a 100644
--- a/cmake/UseDev.cmake
+++ b/cmake/UseDev.cmake
@@ -10,6 +10,7 @@ function(eth_apply TARGET REQUIRED SUBMODULE)
target_link_libraries(${TARGET} ${Boost_RANDOM_LIBRARIES})
target_link_libraries(${TARGET} ${Boost_FILESYSTEM_LIBRARIES})
target_link_libraries(${TARGET} ${Boost_SYSTEM_LIBRARIES})
+ target_link_libraries(${TARGET} ${Boost_REGEX_LIBRARIES})
if (DEFINED MSVC)
target_link_libraries(${TARGET} ${Boost_CHRONO_LIBRARIES})
diff --git a/deps b/deps
-Subproject b3db8905894eafb74a436b702de78ba235f3a3b
+Subproject e5c8316db8d3daa0abc3b5af8545ce330057608
diff --git a/docs/assembly.rst b/docs/assembly.rst
index 7ef41483..83643634 100644
--- a/docs/assembly.rst
+++ b/docs/assembly.rst
@@ -13,6 +13,8 @@ TODO: Write about how scoping rules of inline assembly are a bit different
and the complications that arise when for example using internal functions
of libraries. Furthermore, write about the symbols defined by the compiler.
+.. _inline-assembly:
+
Inline Assembly
===============
diff --git a/docs/bugs.json b/docs/bugs.json
index 1a67d626..a0c0e7c4 100644
--- a/docs/bugs.json
+++ b/docs/bugs.json
@@ -1,5 +1,12 @@
[
{
+ "name": "SkipEmptyStringLiteral",
+ "summary": "If \"\" is used in a function call, the following function arguments will not be correctly passed to the function.",
+ "description": "If the empty string literal \"\" is used as an argument in a function call, it is skipped by the encoder. This has the effect that the encoding of all arguments following this is shifted left by 32 bytes and thus the function call data is corrupted.",
+ "fixed": "0.4.12",
+ "severity": "low"
+ },
+ {
"name": "ConstantOptimizerSubtraction",
"summary": "In some situations, the optimizer replaces certain numbers in the code with routines that compute different numbers.",
"description": "The optimizer tries to represent any number in the bytecode by routines that compute them with less gas. For some special numbers, an incorrect routine is generated. This could allow an attacker to e.g. trick victims about a specific amount of ether, or function calls to call different functions (or none at all).",
diff --git a/docs/bugs_by_version.json b/docs/bugs_by_version.json
index 0f7346b4..1be05f3c 100644
--- a/docs/bugs_by_version.json
+++ b/docs/bugs_by_version.json
@@ -1,6 +1,7 @@
{
"0.1.0": {
"bugs": [
+ "SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored",
"OptimizerStaleKnowledgeAboutSHA3",
@@ -15,6 +16,7 @@
},
"0.1.1": {
"bugs": [
+ "SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored",
"OptimizerStaleKnowledgeAboutSHA3",
@@ -29,6 +31,7 @@
},
"0.1.2": {
"bugs": [
+ "SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored",
"OptimizerStaleKnowledgeAboutSHA3",
@@ -43,6 +46,7 @@
},
"0.1.3": {
"bugs": [
+ "SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored",
"OptimizerStaleKnowledgeAboutSHA3",
@@ -57,6 +61,7 @@
},
"0.1.4": {
"bugs": [
+ "SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored",
"OptimizerStaleKnowledgeAboutSHA3",
@@ -71,6 +76,7 @@
},
"0.1.5": {
"bugs": [
+ "SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored",
"OptimizerStaleKnowledgeAboutSHA3",
@@ -85,6 +91,7 @@
},
"0.1.6": {
"bugs": [
+ "SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored",
"HighOrderByteCleanStorage",
@@ -100,6 +107,7 @@
},
"0.1.7": {
"bugs": [
+ "SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored",
"HighOrderByteCleanStorage",
@@ -115,6 +123,7 @@
},
"0.2.0": {
"bugs": [
+ "SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored",
"HighOrderByteCleanStorage",
@@ -130,6 +139,7 @@
},
"0.2.1": {
"bugs": [
+ "SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored",
"HighOrderByteCleanStorage",
@@ -145,6 +155,7 @@
},
"0.2.2": {
"bugs": [
+ "SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored",
"HighOrderByteCleanStorage",
@@ -160,6 +171,7 @@
},
"0.3.0": {
"bugs": [
+ "SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored",
"HighOrderByteCleanStorage",
@@ -174,6 +186,7 @@
},
"0.3.1": {
"bugs": [
+ "SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored",
"HighOrderByteCleanStorage",
@@ -187,6 +200,7 @@
},
"0.3.2": {
"bugs": [
+ "SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored",
"HighOrderByteCleanStorage",
@@ -200,6 +214,7 @@
},
"0.3.3": {
"bugs": [
+ "SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored",
"HighOrderByteCleanStorage",
@@ -212,6 +227,7 @@
},
"0.3.4": {
"bugs": [
+ "SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored",
"HighOrderByteCleanStorage",
@@ -224,6 +240,7 @@
},
"0.3.5": {
"bugs": [
+ "SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored",
"HighOrderByteCleanStorage",
@@ -236,6 +253,7 @@
},
"0.3.6": {
"bugs": [
+ "SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored",
"HighOrderByteCleanStorage",
@@ -246,6 +264,7 @@
},
"0.4.0": {
"bugs": [
+ "SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored",
"HighOrderByteCleanStorage",
@@ -256,6 +275,7 @@
},
"0.4.1": {
"bugs": [
+ "SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored",
"HighOrderByteCleanStorage",
@@ -266,16 +286,20 @@
},
"0.4.10": {
"bugs": [
+ "SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction"
],
"released": "2017-03-15"
},
"0.4.11": {
- "bugs": [],
+ "bugs": [
+ "SkipEmptyStringLiteral"
+ ],
"released": "2017-05-03"
},
"0.4.2": {
"bugs": [
+ "SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored",
"HighOrderByteCleanStorage",
@@ -285,6 +309,7 @@
},
"0.4.3": {
"bugs": [
+ "SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored",
"HighOrderByteCleanStorage"
@@ -293,6 +318,7 @@
},
"0.4.4": {
"bugs": [
+ "SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored"
],
@@ -300,6 +326,7 @@
},
"0.4.5": {
"bugs": [
+ "SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored",
"OptimizerStateKnowledgeNotResetForJumpdest"
@@ -308,6 +335,7 @@
},
"0.4.6": {
"bugs": [
+ "SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored"
],
@@ -315,18 +343,21 @@
},
"0.4.7": {
"bugs": [
+ "SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction"
],
"released": "2016-12-15"
},
"0.4.8": {
"bugs": [
+ "SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction"
],
"released": "2017-01-13"
},
"0.4.9": {
"bugs": [
+ "SkipEmptyStringLiteral",
"ConstantOptimizerSubtraction"
],
"released": "2017-01-31"
diff --git a/docs/contributing.rst b/docs/contributing.rst
index 1f869dbb..559f9f6a 100644
--- a/docs/contributing.rst
+++ b/docs/contributing.rst
@@ -74,3 +74,22 @@ To run a subset of tests, filters can be used:
``soltest -t TestSuite/TestName -- --ipcpath /tmp/testeth/geth.ipc``, where ``TestName`` can be a wildcard ``*``.
Alternatively, there is a testing script at ``scripts/test.sh`` which executes all tests.
+
+Whiskers
+========
+
+*Whiskers* is a templating system similar to `Moustache <https://mustache.github.io>`_. It is used by the
+compiler in various places to aid readability, and thus maintainability and verifiability, of the code.
+
+The syntax comes with a substantial difference to Moustache: the template markers ``{{`` and ``}}`` are
+replaced by ``<`` and ``>`` in order to aid parsing and avoid conflicts with :ref:`inline-assembly`
+(The symbols ``<`` and ``>`` are invalid in inline assembly, while ``{`` and ``}`` are used to delimit blocks).
+Another limitation is that lists are only resolved one depth and they will not recurse. This may change in the future.
+
+A rough specification is the following:
+
+Any occurrence of ``<name>`` is replaced by the string-value of the supplied variable ``name`` without any
+escaping and without iterated replacements. An area can be delimited by ``<#name>...</name>``. It is replaced
+by as many concatenations of its contents as there were sets of variables supplied to the template system,
+each time replacing any ``<inner>`` items by their respective value. Top-level variales can also be used
+inside such areas. \ No newline at end of file
diff --git a/docs/control-structures.rst b/docs/control-structures.rst
index a2d34274..03787c20 100644
--- a/docs/control-structures.rst
+++ b/docs/control-structures.rst
@@ -361,55 +361,72 @@ As a result, the following code is legal, despite being poorly written::
return bar;// returns 5
}
-.. index:: ! exception, ! throw
+.. index:: ! exception, ! throw, ! assert, ! require, ! revert
-Exceptions
-==========
+Error handling: Assert, Require, Revert and Exceptions
+======================================================
+
+Solidity uses state-reverting exceptions to handle errors. Such an exception will undo all changes made to the
+state in the current call (and all its sub-calls) and also flag an error to the caller.
+The convenience functions ``assert`` and ``require`` can be used to check for conditions and throw an exception
+if the condition is not met. The difference between the two is that ``assert`` should only be used for internal errors
+and ``require`` should be used to check external conditions (invalid inputs or errors in external components).
+The idea behind that is that analysis tools can check your contract and try to come up with situations and
+series of function calls that will reach a failing assertion. If this is possible, this means there is a bug
+in your contract you should fix.
-There are some cases where exceptions are thrown automatically (see below). You can use the ``throw`` instruction to throw an exception manually. The effect of an exception is that the currently executing call is stopped and reverted (i.e. all changes to the state and balances are undone) and the exception is also "bubbled up" through Solidity function calls (exceptions are ``send`` and the low-level functions ``call``, ``delegatecall`` and ``callcode``, those return ``false`` in case of an exception).
+There are two other ways to trigger execptions: The ``revert`` function can be used to flag an error and
+revert the current call. In the future it might be possible to also include details about the error
+in a call to ``revert``. The ``throw`` keyword can also be used as an alternative to ``revert()``.
+
+When exceptions happen in a sub-call, they "bubble up" (i.e. exceptions are rethrown) automatically. Exceptions to this rule are ``send``
+and the low-level functions ``call``, ``delegatecall`` and ``callcode`` -- those return ``false`` in case
+of an exception instead of "bubbling up".
Catching exceptions is not yet possible.
-In the following example, we show how ``throw`` can be used to easily revert an Ether transfer and also how to check the return value of ``send``::
+In the following example, you can see how ``require`` can be used to easily check conditions on inputs
+and how ``assert`` can be used for internal error checking::
pragma solidity ^0.4.0;
contract Sharer {
function sendHalf(address addr) payable returns (uint balance) {
- if (!addr.send(msg.value / 2))
- throw; // also reverts the transfer to Sharer
+ require(msg.value % 2 == 0); // Only allow even numbers
+ uint balanceBeforeTransfer = this.balance;
+ addr.transfer(msg.value / 2);
+ // Since transfer throws an exception on failure and
+ // cannot call back here, there should be no way for us to
+ // still have half of the money.
+ assert(this.balance == balanceBeforeTransfer - msg.value / 2);
return this.balance;
}
}
-Currently, Solidity automatically generates a runtime exception in the following situations:
+An ``assert``-style exception is generated in the following situations:
#. If you access an array at a too large or negative index (i.e. ``x[i]`` where ``i >= x.length`` or ``i < 0``).
#. If you access a fixed-length ``bytesN`` at a too large or negative index.
-#. If you call a function via a message call but it does not finish properly (i.e. it runs out of gas, has no matching function, or throws an exception itself), except when a low level operation ``call``, ``send``, ``delegatecall`` or ``callcode`` is used. The low level operations never throw exceptions but indicate failures by returning ``false``.
-#. If you create a contract using the ``new`` keyword but the contract creation does not finish properly (see above for the definition of "not finish properly").
#. If you divide or modulo by zero (e.g. ``5 / 0`` or ``23 % 0``).
#. If you shift by a negative amount.
#. If you convert a value too big or negative into an enum type.
-#. If you perform an external function call targeting a contract that contains no code.
-#. If your contract receives Ether via a public function without ``payable`` modifier (including the constructor and the fallback function).
-#. If your contract receives Ether via a public getter function.
#. If you call a zero-initialized variable of internal function type.
-#. If a ``.transfer()`` fails.
#. If you call ``assert`` with an argument that evaluates to false.
-While a user-provided exception is generated in the following situations:
+A ``require``-style exception is generated in the following situations:
#. Calling ``throw``.
#. Calling ``require`` with an argument that evaluates to ``false``.
+#. If you call a function via a message call but it does not finish properly (i.e. it runs out of gas, has no matching function, or throws an exception itself), except when a low level operation ``call``, ``send``, ``delegatecall`` or ``callcode`` is used. The low level operations never throw exceptions but indicate failures by returning ``false``.
+#. If you create a contract using the ``new`` keyword but the contract creation does not finish properly (see above for the definition of "not finish properly").
+#. If you perform an external function call targeting a contract that contains no code.
+#. If your contract receives Ether via a public function without ``payable`` modifier (including the constructor and the fallback function).
+#. If your contract receives Ether via a public getter function.
+#. If a ``.transfer()`` fails.
-Internally, Solidity performs a revert operation (instruction ``0xfd``) when a user-provided exception is thrown or the condition of
-a ``require`` call is not met. In contrast, it performs an invalid operation
-(instruction ``0xfe``) if a runtime exception is encountered or the condition of an ``assert`` call is not met. In both cases, this causes
-the EVM to revert all changes made to the state. The reason for this is that there is no safe way to continue execution, because an expected effect
+Internally, Solidity performs a revert operation (instruction ``0xfd``) for a ``require``-style exception and executes an invalid operation
+(instruction ``0xfe``) to throw an ``assert``-style exception. In both cases, this causes
+the EVM to revert all changes made to the state. The reason for reverting is that there is no safe way to continue execution, because an expected effect
did not occur. Because we want to retain the atomicity of transactions, the safest thing to do is to revert all changes and make the whole transaction
-(or at least call) without effect.
-
-If contracts are written so that ``assert`` is only used to test internal conditions and ``require``
-is used in case of malformed input, a formal analysis tool that verifies that the invalid
-opcode can never be reached can be used to check for the absence of errors assuming valid inputs.
+(or at least call) without effect. Note that ``assert``-style exceptions consume all gas available to the call, while
+``revert``-style exceptions will not consume any gas starting from the Metropolis release. \ No newline at end of file
diff --git a/docs/grammar.txt b/docs/grammar.txt
index 09e492f3..6c041460 100644
--- a/docs/grammar.txt
+++ b/docs/grammar.txt
@@ -20,9 +20,12 @@ StateVariableDeclaration = TypeName ( 'public' | 'internal' | 'private' )? Ident
UsingForDeclaration = 'using' Identifier 'for' ('*' | TypeName) ';'
StructDefinition = 'struct' Identifier '{'
( VariableDeclaration ';' (VariableDeclaration ';')* )? '}'
+
ModifierDefinition = 'modifier' Identifier ParameterList? Block
+ModifierInvocation = Identifier ( '(' ExpressionList? ')' )?
+
FunctionDefinition = 'function' Identifier? ParameterList
- ( FunctionCall | Identifier | 'constant' | 'payable' | 'external' | 'public' | 'internal' | 'private' )*
+ ( ModifierInvocation | 'constant' | 'payable' | 'external' | 'public' | 'internal' | 'private' )*
( 'returns' ParameterList )? ( ';' | Block )
EventDefinition = 'event' Identifier IndexedParameterList 'anonymous'? ';'
@@ -72,8 +75,13 @@ VariableDefinition = ('var' IdentifierList | VariableDeclaration) ( '=' Expressi
IdentifierList = '(' ( Identifier? ',' )* Identifier? ')'
// Precedence by order (see github.com/ethereum/solidity/pull/732)
-Expression =
- ( Expression ('++' | '--') | FunctionCall | IndexAccess | MemberAccess | '(' Expression ')' )
+Expression
+ = Expression ('++' | '--')
+ | NewExpression
+ | IndexAccess
+ | MemberAccess
+ | FunctionCall
+ | '(' Expression ')'
| ('!' | '~' | 'delete' | '++' | '--' | '+' | '-') Expression
| Expression '**' Expression
| Expression ('*' | '/' | '%') Expression
@@ -88,20 +96,20 @@ Expression =
| Expression '||' Expression
| Expression '?' Expression ':' Expression
| Expression ('=' | '|=' | '^=' | '&=' | '<<=' | '>>=' | '+=' | '-=' | '*=' | '/=' | '%=') Expression
- | Expression? (',' Expression)
| PrimaryExpression
-PrimaryExpression = Identifier
- | BooleanLiteral
+PrimaryExpression = BooleanLiteral
| NumberLiteral
| HexLiteral
| StringLiteral
+ | TupleExpression
+ | Identifier
| ElementaryTypeNameExpression
ExpressionList = Expression ( ',' Expression )*
NameValueList = Identifier ':' Expression ( ',' Identifier ':' Expression )*
-FunctionCall = ( PrimaryExpression | NewExpression | TypeName ) ( ( '.' Identifier ) | ( '[' Expression ']' ) )* '(' FunctionCallArguments ')'
+FunctionCall = Expression '(' FunctionCallArguments ')'
FunctionCallArguments = '{' NameValueList? '}'
| ExpressionList?
@@ -120,6 +128,9 @@ Identifier = [a-zA-Z_$] [a-zA-Z_$0-9]*
HexNumber = '0x' [0-9a-fA-F]+
DecimalNumber = [0-9]+
+TupleExpression = '(' ( Expression ( ',' Expression )* )? ')'
+ | '[' ( Expression ( ',' Expression )* )? ']'
+
ElementaryTypeNameExpression = ElementaryTypeName
ElementaryTypeName = 'address' | 'bool' | 'string' | 'var'
diff --git a/docs/installing-solidity.rst b/docs/installing-solidity.rst
index 08160dad..9b5ba9f2 100644
--- a/docs/installing-solidity.rst
+++ b/docs/installing-solidity.rst
@@ -267,7 +267,7 @@ If there are local modifications, the commit will be postfixed with ``.mod``.
These parts are combined as required by Semver, where the Solidity pre-release tag equals to the Semver pre-release
and the Solidity commit and platform combined make up the Semver build metadata.
-A relase example: ``0.4.8+commit.60cc1668.Emscripten.clang``.
+A release example: ``0.4.8+commit.60cc1668.Emscripten.clang``.
A pre-release example: ``0.4.9-nightly.2017.1.17+commit.6ecb4aa3.Emscripten.clang``
diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst
index 2e0ccf45..182de33a 100644
--- a/docs/miscellaneous.rst
+++ b/docs/miscellaneous.rst
@@ -394,12 +394,14 @@ The following is the order of precedence for operators, listed in order of evalu
+============+=====================================+============================================+
| *1* | Postfix increment and decrement | ``++``, ``--`` |
+ +-------------------------------------+--------------------------------------------+
-| | Function-like call | ``<func>(<args...>)`` |
+| | New expression | ``new <typename>`` |
+ +-------------------------------------+--------------------------------------------+
| | Array subscripting | ``<array>[<index>]`` |
+ +-------------------------------------+--------------------------------------------+
| | Member access | ``<object>.<member>`` |
+ +-------------------------------------+--------------------------------------------+
+| | Function-like call | ``<func>(<args...>)`` |
++ +-------------------------------------+--------------------------------------------+
| | Parentheses | ``(<statement>)`` |
+------------+-------------------------------------+--------------------------------------------+
| *2* | Prefix increment and decrement | ``++``, ``--`` |
@@ -462,7 +464,7 @@ Global Variables
- ``tx.gasprice`` (``uint``): gas price of the transaction
- ``tx.origin`` (``address``): sender of the transaction (full call chain)
- ``assert(bool condition)``: abort execution and revert state changes if condition is ``false`` (use for internal error)
-- ``require(bool condition)``: abort execution and revert state changes if condition is ``false`` (use for malformed input)
+- ``require(bool condition)``: abort execution and revert state changes if condition is ``false`` (use for malformed input or error in external component)
- ``revert()``: abort execution and revert state changes
- ``keccak256(...) returns (bytes32)``: compute the Ethereum-SHA-3 (Keccak-256) hash of the (tightly packed) arguments
- ``sha3(...) returns (bytes32)``: an alias to `keccak256()`
diff --git a/docs/solidity-by-example.rst b/docs/solidity-by-example.rst
index 993e2c18..450b0286 100644
--- a/docs/solidity-by-example.rst
+++ b/docs/solidity-by-example.rst
@@ -94,7 +94,7 @@ of votes.
// called incorrectly. But watch out, this
// will currently also consume all provided gas
// (this is planned to change in the future).
- require((msg.sender == chairperson) && !voters[voter].voted);
+ require((msg.sender == chairperson) && !voters[voter].voted && (voters[voter].weight == 0));
voters[voter].weight = 1;
}
@@ -165,7 +165,7 @@ of votes.
}
}
}
-
+
// Calls winningProposal() function to get the index
// of the winner contained in the proposals array and then
// returns the name of the winner
@@ -273,7 +273,7 @@ activate themselves.
// If the bid is not higher, send the
// money back.
require(msg.value > highestBid);
-
+
if (highestBidder != 0) {
// Sending back the money by simply using
// highestBidder.send(highestBid) is a security risk
@@ -296,7 +296,7 @@ activate themselves.
// before `send` returns.
pendingReturns[msg.sender] = 0;
- if (!msg.sender.send(amount)) {
+ if (!msg.sender.send(amount)) {
// No need to call throw here, just reset the amount owing
pendingReturns[msg.sender] = amount;
return false;
diff --git a/docs/units-and-global-variables.rst b/docs/units-and-global-variables.rst
index 779e3819..7d21f065 100644
--- a/docs/units-and-global-variables.rst
+++ b/docs/units-and-global-variables.rst
@@ -79,13 +79,23 @@ Block and Transaction Properties
You can only access the hashes of the most recent 256 blocks, all other
values will be zero.
-.. index:: assert, revert, keccak256, ripemd160, sha256, ecrecover, addmod, mulmod, cryptography, this, super, selfdestruct, balance, send
+.. index:: assert, revert, require
+
+Error Handling
+--------------
+
+``assert(bool condition)``:
+ throws if the condition is not met - to be used for internal errors.
+``require(bool condition)``:
+ throws if the condition is not met - to be used for errors in inputs or external components.
+``revert()``:
+ abort execution and revert state changes
+
+.. index:: keccak256, ripemd160, sha256, ecrecover, addmod, mulmod, cryptography,
Mathematical and Cryptographic Functions
----------------------------------------
-``assert(bool condition)``:
- throws if the condition is not met.
``addmod(uint x, uint y, uint k) returns (uint)``:
compute ``(x + y) % k`` where the addition is performed with arbitrary precision and does not wrap around at ``2**256``.
``mulmod(uint x, uint y, uint k) returns (uint)``:
@@ -101,8 +111,6 @@ Mathematical and Cryptographic Functions
``ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address)``:
recover the address associated with the public key from elliptic curve signature or return zero on error
(`example usage <https://ethereum.stackexchange.com/q/1777/222>`_)
-``revert()``:
- abort execution and revert state changes
In the above, "tightly packed" means that the arguments are concatenated without padding.
This means that the following are all identical::
@@ -122,6 +130,7 @@ This means that, for example, ``keccak256(0) == keccak256(uint8(0))`` and
It might be that you run into Out-of-Gas for ``sha256``, ``ripemd160`` or ``ecrecover`` on a *private blockchain*. The reason for this is that those are implemented as so-called precompiled contracts and these contracts only really exist after they received the first message (although their contract code is hardcoded). Messages to non-existing contracts are more expensive and thus the execution runs into an Out-of-Gas error. A workaround for this problem is to first send e.g. 1 Wei to each of the contracts before you use them in your actual contracts. This is not an issue on the official or test net.
+.. index:: balance, send, transfer, call, callcode, delegatecall
.. _address_related:
Address Related
diff --git a/libdevcore/Assertions.h b/libdevcore/Assertions.h
index e54b9d55..729ffb05 100644
--- a/libdevcore/Assertions.h
+++ b/libdevcore/Assertions.h
@@ -25,7 +25,6 @@
#pragma once
#include "Exceptions.h"
-#include "debugbreak.h"
namespace dev
{
@@ -38,37 +37,6 @@ namespace dev
#define ETH_FUNC __func__
#endif
-#define asserts(A) ::dev::assertAux(A, #A, __LINE__, __FILE__, ETH_FUNC)
-#define assertsEqual(A, B) ::dev::assertEqualAux(A, B, #A, #B, __LINE__, __FILE__, ETH_FUNC)
-
-inline bool assertAux(bool _a, char const* _aStr, unsigned _line, char const* _file, char const* _func)
-{
- bool ret = _a;
- if (!ret)
- {
- std::cerr << "Assertion failed:" << _aStr << " [func=" << _func << ", line=" << _line << ", file=" << _file << "]" << std::endl;
-#if ETH_DEBUG
- debug_break();
-#endif
- }
- return !ret;
-}
-
-template<class A, class B>
-inline bool assertEqualAux(A const& _a, B const& _b, char const* _aStr, char const* _bStr, unsigned _line, char const* _file, char const* _func)
-{
- bool ret = _a == _b;
- if (!ret)
- {
- std::cerr << "Assertion failed: " << _aStr << " == " << _bStr << " [func=" << _func << ", line=" << _line << ", file=" << _file << "]" << std::endl;
- std::cerr << " Fail equality: " << _a << "==" << _b << std::endl;
-#if ETH_DEBUG
- debug_break();
-#endif
- }
- return !ret;
-}
-
/// Assertion that throws an exception containing the given description if it is not met.
/// Use it as assertThrow(1 == 1, ExceptionType, "Mathematics is wrong.");
/// Do NOT supply an exception object as the second parameter.
@@ -86,6 +54,4 @@ inline bool assertEqualAux(A const& _a, B const& _b, char const* _aStr, char con
} \
while (false)
-using errinfo_comment = boost::error_info<struct tag_comment, std::string>;
-
}
diff --git a/libdevcore/Common.h b/libdevcore/Common.h
index dc981ff6..c5b09a80 100644
--- a/libdevcore/Common.h
+++ b/libdevcore/Common.h
@@ -76,8 +76,6 @@ using byte = uint8_t;
#define DEV_QUOTED_HELPER(s) #s
#define DEV_QUOTED(s) DEV_QUOTED_HELPER(s)
-#define DEV_IGNORE_EXCEPTIONS(X) try { X; } catch (...) {}
-
namespace dev
{
diff --git a/libdevcore/CommonIO.cpp b/libdevcore/CommonIO.cpp
index 8dbcb00a..52829455 100644
--- a/libdevcore/CommonIO.cpp
+++ b/libdevcore/CommonIO.cpp
@@ -30,11 +30,11 @@
#include <termios.h>
#endif
#include <boost/filesystem.hpp>
-#include "Exceptions.h"
+#include "Assertions.h"
+
using namespace std;
using namespace dev;
-
template <typename _T>
inline _T contentsGeneric(std::string const& _file)
{
@@ -78,13 +78,24 @@ void dev::writeFile(std::string const& _file, bytesConstRef _data, bool _writeDe
if (!fs::exists(p.parent_path()))
{
fs::create_directories(p.parent_path());
- DEV_IGNORE_EXCEPTIONS(fs::permissions(p.parent_path(), fs::owner_all));
+ try
+ {
+ fs::permissions(p.parent_path(), fs::owner_all);
+ }
+ catch (...)
+ {
+ }
}
ofstream s(_file, ios::trunc | ios::binary);
s.write(reinterpret_cast<char const*>(_data.data()), _data.size());
- if (!s)
- BOOST_THROW_EXCEPTION(FileError() << errinfo_comment("Could not write to file: " + _file));
- DEV_IGNORE_EXCEPTIONS(fs::permissions(_file, fs::owner_read|fs::owner_write));
+ assertThrow(s, FileError, "Could not write to file: " + _file);
+ try
+ {
+ fs::permissions(_file, fs::owner_read|fs::owner_write);
+ }
+ catch (...)
+ {
+ }
}
}
diff --git a/libdevcore/Exceptions.h b/libdevcore/Exceptions.h
index 37cdbed9..4817e9e3 100644
--- a/libdevcore/Exceptions.h
+++ b/libdevcore/Exceptions.h
@@ -56,9 +56,5 @@ DEV_SIMPLE_EXCEPTION(FileError);
// error information to be added to exceptions
using errinfo_invalidSymbol = boost::error_info<struct tag_invalidSymbol, char>;
using errinfo_comment = boost::error_info<struct tag_comment, std::string>;
-using errinfo_required = boost::error_info<struct tag_required, bigint>;
-using errinfo_got = boost::error_info<struct tag_got, bigint>;
-using errinfo_required_h256 = boost::error_info<struct tag_required_h256, h256>;
-using errinfo_got_h256 = boost::error_info<struct tag_get_h256, h256>;
}
diff --git a/libdevcore/Whiskers.cpp b/libdevcore/Whiskers.cpp
new file mode 100644
index 00000000..4bad8476
--- /dev/null
+++ b/libdevcore/Whiskers.cpp
@@ -0,0 +1,127 @@
+/*
+ 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/>.
+*/
+/** @file Whiskers.cpp
+ * @author Chris <chis@ethereum.org>
+ * @date 2017
+ *
+ * Moustache-like templates.
+ */
+
+#include <libdevcore/Whiskers.h>
+
+#include <libdevcore/Assertions.h>
+
+#include <boost/regex.hpp>
+
+using namespace std;
+using namespace dev;
+
+Whiskers::Whiskers(string const& _template):
+m_template(_template)
+{
+}
+
+Whiskers& Whiskers::operator ()(string const& _parameter, string const& _value)
+{
+ assertThrow(
+ m_parameters.count(_parameter) == 0,
+ WhiskersError,
+ _parameter + " already set."
+ );
+ assertThrow(
+ m_listParameters.count(_parameter) == 0,
+ WhiskersError,
+ _parameter + " already set as list parameter."
+ );
+ m_parameters[_parameter] = _value;
+
+ return *this;
+}
+
+Whiskers& Whiskers::operator ()(
+ string const& _listParameter,
+ vector<map<string, string>> const& _values
+)
+{
+ assertThrow(
+ m_listParameters.count(_listParameter) == 0,
+ WhiskersError,
+ _listParameter + " already set."
+ );
+ assertThrow(
+ m_parameters.count(_listParameter) == 0,
+ WhiskersError,
+ _listParameter + " already set as value parameter."
+ );
+ m_listParameters[_listParameter] = _values;
+
+ return *this;
+}
+
+string Whiskers::render() const
+{
+ return replace(m_template, m_parameters, m_listParameters);
+}
+
+string Whiskers::replace(
+ string const& _template,
+ StringMap const& _parameters,
+ map<string, vector<StringMap>> const& _listParameters
+)
+{
+ using namespace boost;
+ static regex listOrTag("<([^#/>]+)>|<#([^>]+)>(.*?)</\\2>");
+ return regex_replace(_template, listOrTag, [&](match_results<string::const_iterator> _match) -> string
+ {
+ string tagName(_match[1]);
+ if (!tagName.empty())
+ {
+ assertThrow(_parameters.count(tagName), WhiskersError, "Tag " + tagName + " not found.");
+ return _parameters.at(tagName);
+ }
+ else
+ {
+ string listName(_match[2]);
+ string templ(_match[3]);
+ assertThrow(!listName.empty(), WhiskersError, "");
+ assertThrow(
+ _listParameters.count(listName),
+ WhiskersError, "List parameter " + listName + " not set."
+ );
+ string replacement;
+ for (auto const& parameters: _listParameters.at(listName))
+ replacement += replace(templ, joinMaps(_parameters, parameters));
+ return replacement;
+ }
+ });
+}
+
+Whiskers::StringMap Whiskers::joinMaps(
+ Whiskers::StringMap const& _a,
+ Whiskers::StringMap const& _b
+)
+{
+ Whiskers::StringMap ret = _a;
+ for (auto const& x: _b)
+ assertThrow(
+ ret.insert(x).second,
+ WhiskersError,
+ "Parameter collision"
+ );
+ return ret;
+}
+
diff --git a/libdevcore/Whiskers.h b/libdevcore/Whiskers.h
new file mode 100644
index 00000000..21d46af4
--- /dev/null
+++ b/libdevcore/Whiskers.h
@@ -0,0 +1,87 @@
+/*
+ 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/>.
+*/
+/** @file Whiskers.h
+ * @author Chris <chis@ethereum.org>
+ * @date 2017
+ *
+ * Moustache-like templates.
+ */
+
+#pragma once
+
+#include <libdevcore/Exceptions.h>
+
+#include <string>
+#include <map>
+#include <vector>
+
+namespace dev
+{
+
+DEV_SIMPLE_EXCEPTION(WhiskersError);
+
+///
+/// Moustache-like templates.
+///
+/// Usage:
+/// std::vector<std::map<std::string, std::string>> listValues(2);
+/// listValues[0]["k"] = "key1";
+/// listValues[0]["v"] = "value1";
+/// listValues[1]["k"] = "key2";
+/// listValues[1]["v"] = "value2";
+/// auto s = Whiskers("<p1>\n<#list><k> -> <v>\n</list>")
+/// ("p1", "HEAD")
+/// ("list", listValues)
+/// .render();
+///
+/// results in s == "HEAD\nkey1 -> value1\nkey2 -> value2\n"
+///
+/// Note that lists cannot themselves contain lists - this would be a future feature.
+class Whiskers
+{
+public:
+ using StringMap = std::map<std::string, std::string>;
+ using StringListMap = std::map<std::string, std::vector<StringMap>>;
+
+ explicit Whiskers(std::string const& _template);
+
+ /// Sets a single parameter, <paramName>.
+ Whiskers& operator()(std::string const& _parameter, std::string const& _value);
+ /// Sets a list parameter, <#listName> </listName>.
+ Whiskers& operator()(
+ std::string const& _listParameter,
+ std::vector<StringMap> const& _values
+ );
+
+ std::string render() const;
+
+private:
+ static std::string replace(
+ std::string const& _template,
+ StringMap const& _parameters,
+ StringListMap const& _listParameters = StringListMap()
+ );
+
+ /// Joins the two maps throwing an exception if two keys are equal.
+ static StringMap joinMaps(StringMap const& _a, StringMap const& _b);
+
+ std::string m_template;
+ StringMap m_parameters;
+ StringListMap m_listParameters;
+};
+
+}
diff --git a/libdevcore/debugbreak.h b/libdevcore/debugbreak.h
deleted file mode 100644
index f8838a5f..00000000
--- a/libdevcore/debugbreak.h
+++ /dev/null
@@ -1,128 +0,0 @@
-/* Copyright (c) 2013, Scott Tsai
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef DEBUG_BREAK_H
-#define DEBUG_BREAK_H
-
-#if defined(_MSC_VER) || defined(__MINGW32__)
-
-#define debug_break __debugbreak
-
-#else
-
-#include <signal.h>
-#include <unistd.h>
-#include <sys/syscall.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-enum {
- /* gcc optimizers consider code after __builtin_trap() dead.
- * Making __builtin_trap() unsuitable for breaking into the debugger */
- DEBUG_BREAK_PREFER_BUILTIN_TRAP_TO_SIGTRAP = 0,
-};
-
-#if defined(__i386__) || defined(__x86_64__)
-enum { HAVE_TRAP_INSTRUCTION = 1, };
-__attribute__((gnu_inline, always_inline))
-static void __inline__ trap_instruction(void)
-{
- __asm__ volatile("int $0x03");
-}
-#elif defined(__thumb__)
-enum { HAVE_TRAP_INSTRUCTION = 1, };
-/* FIXME: handle __THUMB_INTERWORK__ */
-__attribute__((gnu_inline, always_inline))
-static void __inline__ trap_instruction(void)
-{
- /* See 'arm-linux-tdep.c' in GDB source.
- * Both instruction sequences below works. */
-#if 1
- /* 'eabi_linux_thumb_le_breakpoint' */
- __asm__ volatile(".inst 0xde01");
-#else
- /* 'eabi_linux_thumb2_le_breakpoint' */
- __asm__ volatile(".inst.w 0xf7f0a000");
-#endif
-
- /* Known problem:
- * After a breakpoint hit, can't stepi, step, or continue in GDB.
- * 'step' stuck on the same instruction.
- *
- * Workaround: a new GDB command,
- * 'debugbreak-step' is defined in debugbreak-gdb.py
- * that does:
- * (gdb) set $instruction_len = 2
- * (gdb) tbreak *($pc + $instruction_len)
- * (gdb) jump *($pc + $instruction_len)
- */
-}
-#elif defined(__arm__) && !defined(__thumb__)
-enum { HAVE_TRAP_INSTRUCTION = 1, };
-__attribute__((gnu_inline, always_inline))
-static void __inline__ trap_instruction(void)
-{
- /* See 'arm-linux-tdep.c' in GDB source,
- * 'eabi_linux_arm_le_breakpoint' */
- __asm__ volatile(".inst 0xe7f001f0");
- /* Has same known problem and workaround
- * as Thumb mode */
-}
-#elif defined(ETH_EMSCRIPTEN)
-enum { HAVE_TRAP_INSTRUCTION = 1, };
-__attribute__((gnu_inline, always_inline))
-static void __inline__ trap_instruction(void)
-{
- asm("debugger");
-}
-#else
-enum { HAVE_TRAP_INSTRUCTION = 0, };
-#endif
-
-__attribute__((gnu_inline, always_inline))
-static void __inline__ debug_break(void)
-{
- if (HAVE_TRAP_INSTRUCTION) {
- trap_instruction();
- } else if (DEBUG_BREAK_PREFER_BUILTIN_TRAP_TO_SIGTRAP) {
- /* raises SIGILL on Linux x86{,-64}, to continue in gdb:
- * (gdb) handle SIGILL stop nopass
- * */
- __builtin_trap();
- } else {
- raise(SIGTRAP);
- }
-}
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
-
-#endif
diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp
index 92a4c2a4..27199b7b 100644
--- a/libevmasm/Assembly.cpp
+++ b/libevmasm/Assembly.cpp
@@ -55,21 +55,15 @@ void Assembly::append(Assembly const& _a)
m_subs += _a.m_subs;
for (auto const& lib: _a.m_libraries)
m_libraries.insert(lib);
-
- assert(!_a.m_baseDeposit);
- assert(!_a.m_totalDeposit);
}
void Assembly::append(Assembly const& _a, int _deposit)
{
- if (_deposit > _a.m_deposit)
- BOOST_THROW_EXCEPTION(InvalidDeposit());
- else
- {
- append(_a);
- while (_deposit++ < _a.m_deposit)
- append(Instruction::POP);
- }
+ assertThrow(_deposit <= _a.m_deposit, InvalidDeposit, "");
+
+ append(_a);
+ while (_deposit++ < _a.m_deposit)
+ append(Instruction::POP);
}
unsigned Assembly::bytesRequired(unsigned subTagSize) const
diff --git a/libevmasm/Assembly.h b/libevmasm/Assembly.h
index 13d82e1a..0d40abcf 100644
--- a/libevmasm/Assembly.h
+++ b/libevmasm/Assembly.h
@@ -85,16 +85,10 @@ public:
AssemblyItem const& back() const { return m_items.back(); }
std::string backString() const { return m_items.size() && m_items.back().type() == PushString ? m_strings.at((h256)m_items.back().data()) : std::string(); }
- void onePath() { if (asserts(!m_totalDeposit && !m_baseDeposit)) BOOST_THROW_EXCEPTION(InvalidDeposit()); m_baseDeposit = m_deposit; m_totalDeposit = INT_MAX; }
- void otherPath() { donePath(); m_totalDeposit = m_deposit; m_deposit = m_baseDeposit; }
- void donePaths() { donePath(); m_totalDeposit = m_baseDeposit = 0; }
- void ignored() { m_baseDeposit = m_deposit; }
- void endIgnored() { m_deposit = m_baseDeposit; m_baseDeposit = 0; }
-
void injectStart(AssemblyItem const& _i);
int deposit() const { return m_deposit; }
- void adjustDeposit(int _adjustment) { m_deposit += _adjustment; if (asserts(m_deposit >= 0)) BOOST_THROW_EXCEPTION(InvalidDeposit()); }
- void setDeposit(int _deposit) { m_deposit = _deposit; if (asserts(m_deposit >= 0)) BOOST_THROW_EXCEPTION(InvalidDeposit()); }
+ void adjustDeposit(int _adjustment) { m_deposit += _adjustment; assertThrow(m_deposit >= 0, InvalidDeposit, ""); }
+ void setDeposit(int _deposit) { m_deposit = _deposit; assertThrow(m_deposit >= 0, InvalidDeposit, ""); }
/// Changes the source location used for each appended item.
void setSourceLocation(SourceLocation const& _location) { m_currentSourceLocation = _location; }
@@ -121,7 +115,6 @@ protected:
/// returns the replaced tags.
std::map<u256, u256> optimiseInternal(bool _enable, bool _isCreation, size_t _runs);
- void donePath() { if (m_totalDeposit != INT_MAX && m_totalDeposit != m_deposit) BOOST_THROW_EXCEPTION(InvalidDeposit()); }
unsigned bytesRequired(unsigned subTagSize) const;
private:
@@ -144,8 +137,6 @@ protected:
mutable std::vector<size_t> m_tagPositionsInBytecode;
int m_deposit = 0;
- int m_baseDeposit = 0;
- int m_totalDeposit = 0;
SourceLocation m_currentSourceLocation;
};
diff --git a/libjulia/backends/evm/AbstractAssembly.h b/libjulia/backends/evm/AbstractAssembly.h
index f667c1a7..cfc9b8a5 100644
--- a/libjulia/backends/evm/AbstractAssembly.h
+++ b/libjulia/backends/evm/AbstractAssembly.h
@@ -89,6 +89,9 @@ public:
/// Return from a subroutine.
/// @param _stackDiffAfter the stack adjustment after this instruction.
virtual void appendReturnsub(int _returns, int _stackDiffAfter = 0) = 0;
+
+ /// Append the assembled size as a constant.
+ virtual void appendAssemblySize() = 0;
};
enum class IdentifierContext { LValue, RValue };
diff --git a/libjulia/backends/evm/EVMAssembly.cpp b/libjulia/backends/evm/EVMAssembly.cpp
index daca2393..173d5e93 100644
--- a/libjulia/backends/evm/EVMAssembly.cpp
+++ b/libjulia/backends/evm/EVMAssembly.cpp
@@ -22,7 +22,7 @@
#include <libevmasm/Instruction.h>
-#include <libsolidity/interface/Utils.h>
+#include <libsolidity/interface/Exceptions.h>
using namespace std;
using namespace dev;
@@ -32,6 +32,8 @@ namespace
{
/// Size of labels in bytes. Four-byte labels are required by some EVM1.5 instructions.
size_t constexpr labelReferenceSize = 4;
+
+size_t constexpr assemblySizeReferenceSize = 4;
}
@@ -145,17 +147,19 @@ void EVMAssembly::appendReturnsub(int _returns, int _stackDiffAfter)
eth::LinkerObject EVMAssembly::finalize()
{
+ size_t bytecodeSize = m_bytecode.size();
+ for (auto const& ref: m_assemblySizePositions)
+ updateReference(ref, assemblySizeReferenceSize, u256(bytecodeSize));
+
for (auto const& ref: m_labelReferences)
{
size_t referencePos = ref.first;
solAssert(m_labelPositions.count(ref.second), "");
size_t labelPos = m_labelPositions.at(ref.second);
solAssert(labelPos != size_t(-1), "Undefined but allocated label used.");
- solAssert(m_bytecode.size() >= 4 && referencePos <= m_bytecode.size() - 4, "");
- solAssert(uint64_t(labelPos) < (uint64_t(1) << (8 * labelReferenceSize)), "");
- for (size_t i = 0; i < labelReferenceSize; i++)
- m_bytecode[referencePos + i] = byte((labelPos >> (8 * (labelReferenceSize - i - 1))) & 0xff);
+ updateReference(referencePos, labelReferenceSize, u256(labelPos));
}
+
eth::LinkerObject obj;
obj.bytecode = m_bytecode;
return obj;
@@ -173,3 +177,18 @@ void EVMAssembly::appendLabelReferenceInternal(LabelID _labelId)
m_labelReferences[m_bytecode.size()] = _labelId;
m_bytecode += bytes(labelReferenceSize);
}
+
+void EVMAssembly::appendAssemblySize()
+{
+ appendInstruction(solidity::pushInstruction(assemblySizeReferenceSize));
+ m_assemblySizePositions.push_back(m_bytecode.size());
+ m_bytecode += bytes(assemblySizeReferenceSize);
+}
+
+void EVMAssembly::updateReference(size_t pos, size_t size, u256 value)
+{
+ solAssert(m_bytecode.size() >= size && pos <= m_bytecode.size() - size, "");
+ solAssert(value < (u256(1) << (8 * size)), "");
+ for (size_t i = 0; i < size; i++)
+ m_bytecode[pos + i] = byte((value >> (8 * (size - i - 1))) & 0xff);
+}
diff --git a/libjulia/backends/evm/EVMAssembly.h b/libjulia/backends/evm/EVMAssembly.h
index a2df0cdc..69585822 100644
--- a/libjulia/backends/evm/EVMAssembly.h
+++ b/libjulia/backends/evm/EVMAssembly.h
@@ -70,6 +70,8 @@ public:
/// Return from a subroutine.
virtual void appendReturnsub(int _returns, int _stackDiffAfter) override;
+ /// Append the assembled size as a constant.
+ virtual void appendAssemblySize() override;
/// Resolves references inside the bytecode and returns the linker object.
eth::LinkerObject finalize();
@@ -77,6 +79,7 @@ public:
private:
void setLabelToCurrentPosition(AbstractAssembly::LabelID _labelId);
void appendLabelReferenceInternal(AbstractAssembly::LabelID _labelId);
+ void updateReference(size_t pos, size_t size, u256 value);
bool m_evm15 = false; ///< if true, switch to evm1.5 mode
LabelID m_nextLabelId = 0;
@@ -84,6 +87,7 @@ private:
bytes m_bytecode;
std::map<LabelID, size_t> m_labelPositions;
std::map<size_t, LabelID> m_labelReferences;
+ std::vector<size_t> m_assemblySizePositions;
};
}
diff --git a/libjulia/backends/evm/EVMCodeTransform.cpp b/libjulia/backends/evm/EVMCodeTransform.cpp
index b231ecec..8f12bc25 100644
--- a/libjulia/backends/evm/EVMCodeTransform.cpp
+++ b/libjulia/backends/evm/EVMCodeTransform.cpp
@@ -23,7 +23,7 @@
#include <libsolidity/inlineasm/AsmAnalysisInfo.h>
#include <libsolidity/inlineasm/AsmData.h>
-#include <libsolidity/interface/Utils.h>
+#include <libsolidity/interface/Exceptions.h>
#include <boost/range/adaptor/reversed.hpp>
diff --git a/liblll/All.h b/liblll/All.h
deleted file mode 100644
index 7c4192f6..00000000
--- a/liblll/All.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#pragma once
-
-#include "CodeFragment.h"
-#include "Compiler.h"
-#include "CompilerState.h"
-#include "Parser.h"
diff --git a/liblll/CodeFragment.cpp b/liblll/CodeFragment.cpp
index 9f37bc65..7496fe83 100644
--- a/liblll/CodeFragment.cpp
+++ b/liblll/CodeFragment.cpp
@@ -171,11 +171,23 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s)
return string();
};
- auto varAddress = [&](string const& n)
+ auto varAddress = [&](string const& n, bool createMissing = false)
{
+ if (n.empty())
+ error<InvalidName>("Empty variable name not allowed");
auto it = _s.vars.find(n);
if (it == _s.vars.end())
- error<InvalidName>(std::string("Symbol not found: ") + s);
+ {
+ if (createMissing)
+ {
+ // Create new variable
+ bool ok;
+ tie(it, ok) = _s.vars.insert(make_pair(n, make_pair(_s.stackSize, 32)));
+ _s.stackSize += 32;
+ }
+ else
+ error<InvalidName>(std::string("Symbol not found: ") + n);
+ }
return it->second.first;
};
@@ -192,7 +204,13 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s)
{
if (_t.size() != 2)
error<IncorrectParameterCount>();
- m_asm.append(CodeFragment::compile(contentsString(firstAsString()), _s).m_asm);
+ string fileName = firstAsString();
+ if (fileName.empty())
+ error<InvalidName>("Empty file name provided");
+ string contents = contentsString(fileName);
+ if (contents.empty())
+ error<InvalidName>(std::string("File not found (or empty): ") + fileName);
+ m_asm.append(CodeFragment::compile(contents, _s).m_asm);
}
else if (us == "SET")
{
@@ -202,7 +220,7 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s)
for (auto const& i: _t)
if (c++ == 2)
m_asm.append(CodeFragment(i, _s, false).m_asm);
- m_asm.append((u256)varAddress(firstAsString()));
+ m_asm.append((u256)varAddress(firstAsString(), true));
m_asm.append(Instruction::MSTORE);
}
else if (us == "GET")
@@ -439,15 +457,21 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s)
int minDep = min(code[1].m_asm.deposit(), code[2].m_asm.deposit());
m_asm.append(code[0].m_asm);
- auto pos = m_asm.appendJumpI();
- m_asm.onePath();
+ auto mainBranch = m_asm.appendJumpI();
+
+ /// The else branch.
+ int startDeposit = m_asm.deposit();
m_asm.append(code[2].m_asm, minDep);
auto end = m_asm.appendJump();
- m_asm.otherPath();
- m_asm << pos.tag();
+ int deposit = m_asm.deposit();
+ m_asm.setDeposit(startDeposit);
+
+ /// The main branch.
+ m_asm << mainBranch.tag();
m_asm.append(code[1].m_asm, minDep);
m_asm << end.tag();
- m_asm.donePaths();
+ if (m_asm.deposit() != deposit)
+ error<InvalidDeposit>();
}
else if (us == "WHEN" || us == "UNLESS")
{
@@ -458,11 +482,8 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s)
if (us == "WHEN")
m_asm.append(Instruction::ISZERO);
auto end = m_asm.appendJumpI();
- m_asm.onePath();
- m_asm.otherPath();
m_asm.append(code[1].m_asm, 0);
m_asm << end.tag();
- m_asm.donePaths();
}
else if (us == "WHILE" || us == "UNTIL")
{
diff --git a/liblll/CompilerState.cpp b/liblll/CompilerState.cpp
index c22242a3..5d38bb8c 100644
--- a/liblll/CompilerState.cpp
+++ b/liblll/CompilerState.cpp
@@ -49,13 +49,15 @@ void CompilerState::populateStandard()
"(def 'allgas (- (gas) 21))"
"(def 'send (to value) (call allgas to value 0 0 0 0))"
"(def 'send (gaslimit to value) (call gaslimit to value 0 0 0 0))"
- "(def 'msg (gaslimit to value data datasize outsize) { (set x outsize) (set y (alloc @32)) (call gaslimit to value data datasize @0 @32) @0 })"
+ // NOTE: in this macro, memory location 0 is set in order to force msize to be at least 32 bytes.
+ "(def 'msg (gaslimit to value data datasize outsize) { [0]:0 [0]:(msize) (call gaslimit to value data datasize @0 outsize) @0 })"
"(def 'msg (gaslimit to value data datasize) { (call gaslimit to value data datasize 0 32) @0 })"
"(def 'msg (gaslimit to value data) { [0]:data (msg gaslimit to value 0 32) })"
"(def 'msg (to value data) { [0]:data (msg allgas to value 0 32) })"
"(def 'msg (to data) { [0]:data (msg allgas to 0 0 32) })"
- "(def 'create (value code) { [0]:(msize) (create value @0 (lll code @0)) })"
- "(def 'create (code) { [0]:(msize) (create 0 @0 (lll code @0)) })"
+ // NOTE: in the create macros, memory location 0 is set in order to force msize to be at least 32 bytes.
+ "(def 'create (value code) { [0]:0 [0]:(msize) (create value @0 (lll code @0)) })"
+ "(def 'create (code) { [0]:0 [0]:(msize) (create 0 @0 (lll code @0)) })"
"(def 'sha3 (loc len) (keccak256 loc len))"
"(def 'sha3 (val) { [0]:val (sha3 0 32) })"
"(def 'sha3pair (a b) { [0]:a [32]:b (sha3 0 64) })"
@@ -65,7 +67,7 @@ void CompilerState::populateStandard()
"(def 'makeperm (name pos) { (def name (sload pos)) (def name (v) (sstore pos v)) } )"
"(def 'permcount 0)"
"(def 'perm (name) { (makeperm name permcount) (def 'permcount (+ permcount 1)) } )"
- "(def 'ecrecover (r s v hash) { [0] r [32] s [64] v [96] hash (msg allgas 1 0 0 128) })"
+ "(def 'ecrecover (hash v r s) { [0] hash [32] v [64] r [96] s (msg allgas 1 0 0 128) })"
"(def 'sha256 (data datasize) (msg allgas 2 0 data datasize))"
"(def 'ripemd160 (data datasize) (msg allgas 3 0 data datasize))"
"(def 'sha256 (val) { [0]:val (sha256 0 32) })"
diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp
index cb9435e8..615bc402 100644
--- a/libsolidity/analysis/TypeChecker.cpp
+++ b/libsolidity/analysis/TypeChecker.cpp
@@ -464,6 +464,8 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
m_errorReporter.typeError(var->location(), "Type is required to live outside storage.");
if (_function.visibility() >= FunctionDefinition::Visibility::Public && !(type(*var)->interfaceType(isLibraryFunction)))
m_errorReporter.fatalTypeError(var->location(), "Internal type is not allowed for public or external functions.");
+
+ var->accept(*this);
}
for (ASTPointer<ModifierInvocation> const& modifier: _function.modifiers())
visitManually(
@@ -488,7 +490,12 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
bool TypeChecker::visit(VariableDeclaration const& _variable)
{
- if (m_scope->contractKind() == ContractDefinition::ContractKind::Interface)
+ // Forbid any variable declarations inside interfaces unless they are part of
+ // a function's input/output parameters.
+ if (
+ m_scope->contractKind() == ContractDefinition::ContractKind::Interface
+ && !_variable.isCallableParameter()
+ )
m_errorReporter.typeError(_variable.location(), "Variables cannot be declared in interfaces.");
// Variables can be declared without type (with "var"), in which case the first assignment
@@ -1720,10 +1727,7 @@ void TypeChecker::endVisit(Literal const& _literal)
if (_literal.looksLikeAddress())
{
if (_literal.passesAddressChecksum())
- {
_literal.annotation().type = make_shared<IntegerType>(0, IntegerType::Modifier::Address);
- return;
- }
else
m_errorReporter.warning(
_literal.location(),
@@ -1731,10 +1735,13 @@ void TypeChecker::endVisit(Literal const& _literal)
"If this is not used as an address, please prepend '00'."
);
}
- _literal.annotation().type = Type::forLiteral(_literal);
- _literal.annotation().isPure = true;
+ if (!_literal.annotation().type)
+ _literal.annotation().type = Type::forLiteral(_literal);
+
if (!_literal.annotation().type)
m_errorReporter.fatalTypeError(_literal.location(), "Invalid literal value.");
+
+ _literal.annotation().isPure = true;
}
bool TypeChecker::contractDependenciesAreCyclic(
diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp
index 03112d2d..40dfa348 100644
--- a/libsolidity/ast/AST.cpp
+++ b/libsolidity/ast/AST.cpp
@@ -20,10 +20,8 @@
* Solidity abstract syntax tree.
*/
-#include <libsolidity/interface/Utils.h>
#include <libsolidity/ast/AST.h>
#include <libsolidity/ast/ASTVisitor.h>
-#include <libsolidity/interface/Exceptions.h>
#include <libsolidity/ast/AST_accept.h>
#include <libdevcore/SHA3.h>
diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h
index ba1d0589..83572692 100644
--- a/libsolidity/ast/AST.h
+++ b/libsolidity/ast/AST.h
@@ -29,7 +29,6 @@
#include <boost/noncopyable.hpp>
#include <libevmasm/SourceLocation.h>
#include <libevmasm/Instruction.h>
-#include <libsolidity/interface/Utils.h>
#include <libsolidity/ast/ASTForward.h>
#include <libsolidity/parsing/Token.h>
#include <libsolidity/ast/Types.h>
diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp
index 4ad1f962..a90debb2 100644
--- a/libsolidity/ast/ASTJsonConverter.cpp
+++ b/libsolidity/ast/ASTJsonConverter.cpp
@@ -24,7 +24,6 @@
#include <boost/algorithm/string/join.hpp>
#include <libdevcore/UTF8.h>
#include <libsolidity/ast/AST.h>
-#include <libsolidity/interface/Exceptions.h>
#include <libsolidity/inlineasm/AsmData.h>
#include <libsolidity/inlineasm/AsmPrinter.h>
diff --git a/libsolidity/ast/ASTJsonConverter.h b/libsolidity/ast/ASTJsonConverter.h
index c2de5c48..27114c2a 100644
--- a/libsolidity/ast/ASTJsonConverter.h
+++ b/libsolidity/ast/ASTJsonConverter.h
@@ -26,7 +26,6 @@
#include <stack>
#include <libsolidity/ast/ASTVisitor.h>
#include <libsolidity/interface/Exceptions.h>
-#include <libsolidity/interface/Utils.h>
#include <libsolidity/ast/ASTAnnotations.h>
#include <json/json.h>
diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp
index cfee041e..bd3346f9 100644
--- a/libsolidity/ast/Types.cpp
+++ b/libsolidity/ast/Types.cpp
@@ -22,7 +22,6 @@
#include <libsolidity/ast/Types.h>
-#include <libsolidity/interface/Utils.h>
#include <libsolidity/ast/AST.h>
#include <libdevcore/CommonIO.h>
diff --git a/libsolidity/codegen/ArrayUtils.cpp b/libsolidity/codegen/ArrayUtils.cpp
index 6a641b02..67ca22f1 100644
--- a/libsolidity/codegen/ArrayUtils.cpp
+++ b/libsolidity/codegen/ArrayUtils.cpp
@@ -25,7 +25,7 @@
#include <libsolidity/codegen/CompilerContext.h>
#include <libsolidity/codegen/CompilerUtils.h>
#include <libsolidity/ast/Types.h>
-#include <libsolidity/interface/Utils.h>
+#include <libsolidity/interface/Exceptions.h>
#include <libsolidity/codegen/LValue.h>
using namespace std;
diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp
index d98efcad..1937b529 100644
--- a/libsolidity/codegen/CompilerContext.cpp
+++ b/libsolidity/codegen/CompilerContext.cpp
@@ -26,6 +26,7 @@
#include <libsolidity/codegen/Compiler.h>
#include <libsolidity/interface/Version.h>
#include <libsolidity/interface/ErrorReporter.h>
+#include <libsolidity/parsing/Scanner.h>
#include <libsolidity/inlineasm/AsmParser.h>
#include <libsolidity/inlineasm/AsmCodeGen.h>
#include <libsolidity/inlineasm/AsmAnalysis.h>
@@ -243,6 +244,20 @@ CompilerContext& CompilerContext::appendConditionalInvalid()
return *this;
}
+CompilerContext& CompilerContext::appendRevert()
+{
+ return *this << u256(0) << u256(0) << Instruction::REVERT;
+}
+
+CompilerContext& CompilerContext::appendConditionalRevert()
+{
+ *this << Instruction::ISZERO;
+ eth::AssemblyItem afterTag = appendConditionalJump();
+ appendRevert();
+ *this << afterTag;
+ return *this;
+}
+
void CompilerContext::resetVisitedNodes(ASTNode const* _node)
{
stack<ASTNode const*> newStack;
diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h
index 030b35a6..1968c1e1 100644
--- a/libsolidity/codegen/CompilerContext.h
+++ b/libsolidity/codegen/CompilerContext.h
@@ -136,11 +136,15 @@ public:
/// Appends a JUMP to a new tag and @returns the tag
eth::AssemblyItem appendJumpToNew() { return m_asm->appendJump().tag(); }
/// Appends a JUMP to a tag already on the stack
- CompilerContext& appendJump(eth::AssemblyItem::JumpType _jumpType = eth::AssemblyItem::JumpType::Ordinary);
+ CompilerContext& appendJump(eth::AssemblyItem::JumpType _jumpType = eth::AssemblyItem::JumpType::Ordinary);
/// Appends an INVALID instruction
- CompilerContext& appendInvalid();
+ CompilerContext& appendInvalid();
/// Appends a conditional INVALID instruction
- CompilerContext& appendConditionalInvalid();
+ CompilerContext& appendConditionalInvalid();
+ /// Appends a REVERT(0, 0) call
+ CompilerContext& appendRevert();
+ /// Appends a conditional REVERT(0, 0) call
+ CompilerContext& appendConditionalRevert();
/// Appends a JUMP to a specific tag
CompilerContext& appendJumpTo(eth::AssemblyItem const& _tag) { m_asm->appendJump(_tag); return *this; }
/// Appends pushing of a new tag and @returns the new tag.
diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp
index 3baaaddf..7fed1975 100644
--- a/libsolidity/codegen/CompilerUtils.cpp
+++ b/libsolidity/codegen/CompilerUtils.cpp
@@ -128,7 +128,7 @@ void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBound
m_context << Instruction::DUP1;
storeStringData(bytesConstRef(str->value()));
if (_padToWordBoundaries)
- m_context << u256(((str->value().size() + 31) / 32) * 32);
+ m_context << u256(max<size_t>(32, ((str->value().size() + 31) / 32) * 32));
else
m_context << u256(str->value().size());
m_context << Instruction::ADD;
@@ -392,7 +392,13 @@ void CompilerUtils::pushCombinedFunctionEntryLabel(Declaration const& _function)
Instruction::OR;
}
-void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetType, bool _cleanupNeeded, bool _chopSignBits)
+void CompilerUtils::convertType(
+ Type const& _typeOnStack,
+ Type const& _targetType,
+ bool _cleanupNeeded,
+ bool _chopSignBits,
+ bool _asPartOfArgumentDecoding
+)
{
// For a type extension, we need to remove all higher-order bits that we might have ignored in
// previous operations.
@@ -450,7 +456,10 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp
EnumType const& enumType = dynamic_cast<decltype(enumType)>(_typeOnStack);
solAssert(enumType.numberOfMembers() > 0, "empty enum should have caused a parser error.");
m_context << u256(enumType.numberOfMembers() - 1) << Instruction::DUP2 << Instruction::GT;
- m_context.appendConditionalInvalid();
+ if (_asPartOfArgumentDecoding)
+ m_context.appendConditionalRevert();
+ else
+ m_context.appendConditionalInvalid();
enumOverflowCheckPending = false;
}
break;
@@ -985,7 +994,7 @@ unsigned CompilerUtils::loadFromMemoryHelper(Type const& _type, bool _fromCallda
m_context << shiftFactor << Instruction::MUL;
}
if (_fromCalldata)
- convertType(_type, _type, true);
+ convertType(_type, _type, true, false, true);
return numBytes;
}
diff --git a/libsolidity/codegen/CompilerUtils.h b/libsolidity/codegen/CompilerUtils.h
index a88951bc..caf2cdc2 100644
--- a/libsolidity/codegen/CompilerUtils.h
+++ b/libsolidity/codegen/CompilerUtils.h
@@ -135,7 +135,15 @@ public:
/// If @a _cleanupNeeded, high order bits cleanup is also done if no type conversion would be
/// necessary.
/// If @a _chopSignBits, the function resets the signed bits out of the width of the signed integer.
- void convertType(Type const& _typeOnStack, Type const& _targetType, bool _cleanupNeeded = false, bool _chopSignBits = false);
+ /// If @a _asPartOfArgumentDecoding is true, failed conversions are flagged via REVERT,
+ /// otherwise they are flagged with INVALID.
+ void convertType(
+ Type const& _typeOnStack,
+ Type const& _targetType,
+ bool _cleanupNeeded = false,
+ bool _chopSignBits = false,
+ bool _asPartOfArgumentDecoding = false
+ );
/// Creates a zero-value for the given type and puts it onto the stack. This might allocate
/// memory for memory references.
diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp
index dc090634..ace82ad4 100644
--- a/libsolidity/codegen/ContractCompiler.cpp
+++ b/libsolidity/codegen/ContractCompiler.cpp
@@ -111,7 +111,7 @@ void ContractCompiler::appendCallValueCheck()
{
// Throw if function is not payable but call contained ether.
m_context << Instruction::CALLVALUE;
- m_context.appendConditionalInvalid();
+ m_context.appendConditionalRevert();
}
void ContractCompiler::appendInitAndConstructorCode(ContractDefinition const& _contract)
@@ -276,7 +276,7 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac
appendReturnValuePacker(FunctionType(*fallback).returnParameterTypes(), _contract.isLibrary());
}
else
- m_context.appendInvalid();
+ m_context.appendRevert();
for (auto const& it: interfaceFunctions)
{
@@ -368,7 +368,7 @@ void ContractCompiler::appendCalldataUnpacker(TypePointers const& _typeParameter
// copy to memory
// move calldata type up again
CompilerUtils(m_context).moveIntoStack(calldataType->sizeOnStack());
- CompilerUtils(m_context).convertType(*calldataType, arrayType);
+ CompilerUtils(m_context).convertType(*calldataType, arrayType, false, false, true);
// fetch next pointer again
CompilerUtils(m_context).moveToStackTop(arrayType.sizeOnStack());
}
@@ -805,8 +805,7 @@ bool ContractCompiler::visit(Throw const& _throw)
{
CompilerContext::LocationSetter locationSetter(m_context, _throw);
// Do not send back an error detail.
- m_context << u256(0) << u256(0);
- m_context << Instruction::REVERT;
+ m_context.appendRevert();
return false;
}
diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp
index 25154bc0..a7cfe4dc 100644
--- a/libsolidity/codegen/ExpressionCompiler.cpp
+++ b/libsolidity/codegen/ExpressionCompiler.cpp
@@ -32,6 +32,7 @@
#include <libsolidity/codegen/CompilerUtils.h>
#include <libsolidity/codegen/LValue.h>
#include <libevmasm/GasMeter.h>
+
using namespace std;
namespace dev
@@ -586,7 +587,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
m_context << Instruction::CREATE;
// Check if zero (out of stack or not enough balance).
m_context << Instruction::DUP1 << Instruction::ISZERO;
- m_context.appendConditionalInvalid();
+ m_context.appendConditionalRevert();
if (function.valueSet())
m_context << swapInstruction(1) << Instruction::POP;
break;
@@ -650,7 +651,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
{
// Check if zero (out of stack or not enough balance).
m_context << Instruction::ISZERO;
- m_context.appendConditionalInvalid();
+ m_context.appendConditionalRevert();
}
break;
case FunctionType::Kind::Selfdestruct:
@@ -659,9 +660,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
m_context << Instruction::SELFDESTRUCT;
break;
case FunctionType::Kind::Revert:
- // memory offset returned - zero length
- m_context << u256(0) << u256(0);
- m_context << Instruction::REVERT;
+ m_context.appendRevert();
break;
case FunctionType::Kind::SHA3:
{
@@ -887,9 +886,9 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
auto success = m_context.appendConditionalJump();
if (function.kind() == FunctionType::Kind::Assert)
// condition was not met, flag an error
- m_context << Instruction::INVALID;
+ m_context.appendInvalid();
else
- m_context << u256(0) << u256(0) << Instruction::REVERT;
+ m_context.appendRevert();
// the success branch
m_context << success;
break;
@@ -1694,7 +1693,7 @@ void ExpressionCompiler::appendExternalFunctionCall(
if (funKind == FunctionType::Kind::External || funKind == FunctionType::Kind::CallCode || funKind == FunctionType::Kind::DelegateCall)
{
m_context << Instruction::DUP1 << Instruction::EXTCODESIZE << Instruction::ISZERO;
- m_context.appendConditionalInvalid();
+ m_context.appendConditionalRevert();
existenceChecked = true;
}
@@ -1730,7 +1729,7 @@ void ExpressionCompiler::appendExternalFunctionCall(
{
//Propagate error condition (if CALL pushes 0 on stack).
m_context << Instruction::ISZERO;
- m_context.appendConditionalInvalid();
+ m_context.appendConditionalRevert();
}
utils().popStackSlots(remainsSize);
diff --git a/libsolidity/codegen/ExpressionCompiler.h b/libsolidity/codegen/ExpressionCompiler.h
index d0a8ac15..3b8cf1c6 100644
--- a/libsolidity/codegen/ExpressionCompiler.h
+++ b/libsolidity/codegen/ExpressionCompiler.h
@@ -28,7 +28,7 @@
#include <libevmasm/SourceLocation.h>
#include <libsolidity/ast/ASTVisitor.h>
#include <libsolidity/codegen/LValue.h>
-#include <libsolidity/interface/Utils.h>
+#include <libsolidity/interface/Exceptions.h>
namespace dev {
namespace eth
diff --git a/libsolidity/inlineasm/AsmAnalysis.cpp b/libsolidity/inlineasm/AsmAnalysis.cpp
index 2891ec95..b0d044ae 100644
--- a/libsolidity/inlineasm/AsmAnalysis.cpp
+++ b/libsolidity/inlineasm/AsmAnalysis.cpp
@@ -26,7 +26,6 @@
#include <libsolidity/inlineasm/AsmAnalysisInfo.h>
#include <libsolidity/interface/ErrorReporter.h>
-#include <libsolidity/interface/Utils.h>
#include <boost/range/adaptor/reversed.hpp>
#include <boost/algorithm/string.hpp>
diff --git a/libsolidity/inlineasm/AsmCodeGen.cpp b/libsolidity/inlineasm/AsmCodeGen.cpp
index 3c7c62c6..2bbd1b70 100644
--- a/libsolidity/inlineasm/AsmCodeGen.cpp
+++ b/libsolidity/inlineasm/AsmCodeGen.cpp
@@ -121,6 +121,11 @@ public:
solAssert(false, "RETURNSUB not implemented for EVM 1.0");
}
+ virtual void appendAssemblySize() override
+ {
+ m_assembly.appendProgramSize();
+ }
+
private:
LabelID assemblyTagToIdentifier(eth::AssemblyItem const& _tag) const
{
diff --git a/libsolidity/inlineasm/AsmPrinter.cpp b/libsolidity/inlineasm/AsmPrinter.cpp
index 0d06fedd..062ff453 100644
--- a/libsolidity/inlineasm/AsmPrinter.cpp
+++ b/libsolidity/inlineasm/AsmPrinter.cpp
@@ -22,7 +22,7 @@
#include <libsolidity/inlineasm/AsmPrinter.h>
#include <libsolidity/inlineasm/AsmData.h>
-#include <libsolidity/interface/Utils.h>
+#include <libsolidity/interface/Exceptions.h>
#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/replace.hpp>
diff --git a/libsolidity/inlineasm/AsmScopeFiller.cpp b/libsolidity/inlineasm/AsmScopeFiller.cpp
index 3bef9cec..5b3174b8 100644
--- a/libsolidity/inlineasm/AsmScopeFiller.cpp
+++ b/libsolidity/inlineasm/AsmScopeFiller.cpp
@@ -25,7 +25,7 @@
#include <libsolidity/inlineasm/AsmAnalysisInfo.h>
#include <libsolidity/interface/ErrorReporter.h>
-#include <libsolidity/interface/Utils.h>
+#include <libsolidity/interface/Exceptions.h>
#include <boost/range/adaptor/reversed.hpp>
diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp
index b09108b0..8be2c8dd 100644
--- a/libsolidity/interface/CompilerStack.cpp
+++ b/libsolidity/interface/CompilerStack.cpp
@@ -481,12 +481,12 @@ Json::Value const& CompilerStack::natspec(Contract const& _contract, Documentati
return *(*doc);
}
-Json::Value CompilerStack::functionHashes(ContractDefinition const& _contract)
+Json::Value CompilerStack::methodIdentifiers(string const& _contractName) const
{
- Json::Value functionHashes(Json::objectValue);
- for (auto const& it: _contract.interfaceFunctions())
- functionHashes[it.second->externalSignature()] = toHex(it.first.ref());
- return functionHashes;
+ Json::Value methodIdentifiers(Json::objectValue);
+ for (auto const& it: contractDefinition(_contractName).interfaceFunctions())
+ methodIdentifiers[it.second->externalSignature()] = toHex(it.first.ref());
+ return methodIdentifiers;
}
string const& CompilerStack::onChainMetadata(string const& _contractName) const
diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h
index 3250429b..f7435f0e 100644
--- a/libsolidity/interface/CompilerStack.h
+++ b/libsolidity/interface/CompilerStack.h
@@ -178,7 +178,8 @@ public:
/// Can be one of 4 types defined at @c DocumentationType
Json::Value const& natspec(std::string const& _contractName, DocumentationType _type) const;
- Json::Value functionHashes(ContractDefinition const& _contract);
+ /// @returns a JSON representing a map of method identifiers (hashes) to function names.
+ Json::Value methodIdentifiers(std::string const& _contractName) const;
std::string const& onChainMetadata(std::string const& _contractName) const;
void useMetadataLiteralSources(bool _metadataLiteralSources) { m_metadataLiteralSources = _metadataLiteralSources; }
@@ -190,9 +191,6 @@ public:
Scanner const& scanner(std::string const& _sourceName = "") const;
/// @returns the parsed source unit with the supplied name.
SourceUnit const& ast(std::string const& _sourceName = "") const;
- /// @returns the parsed contract with the supplied name. Throws an exception if the contract
- /// does not exist.
- ContractDefinition const& contractDefinition(std::string const& _contractName) const;
/// Helper function for logs printing. Do only use in error cases, it's quite expensive.
/// line and columns are numbered starting from 1 with following order:
@@ -257,6 +255,10 @@ private:
Contract const& contract(std::string const& _contractName = "") const;
Source const& source(std::string const& _sourceName = "") const;
+ /// @returns the parsed contract with the supplied name. Throws an exception if the contract
+ /// does not exist.
+ ContractDefinition const& contractDefinition(std::string const& _contractName) const;
+
std::string createOnChainMetadata(Contract const& _contract) const;
std::string computeSourceMapping(eth::AssemblyItems const& _items) const;
Json::Value const& contractABI(Contract const&) const;
diff --git a/libsolidity/interface/Exceptions.cpp b/libsolidity/interface/Exceptions.cpp
index c09180de..deee92aa 100644
--- a/libsolidity/interface/Exceptions.cpp
+++ b/libsolidity/interface/Exceptions.cpp
@@ -21,7 +21,6 @@
*/
#include <libsolidity/interface/Exceptions.h>
-#include <libsolidity/interface/Utils.h>
using namespace std;
using namespace dev;
diff --git a/libsolidity/interface/Exceptions.h b/libsolidity/interface/Exceptions.h
index 0803d8cc..5fdb8f11 100644
--- a/libsolidity/interface/Exceptions.h
+++ b/libsolidity/interface/Exceptions.h
@@ -25,6 +25,7 @@
#include <string>
#include <utility>
#include <libdevcore/Exceptions.h>
+#include <libdevcore/Assertions.h>
#include <libevmasm/SourceLocation.h>
namespace dev
@@ -39,6 +40,16 @@ struct InternalCompilerError: virtual Exception {};
struct FatalError: virtual Exception {};
struct UnimplementedFeatureError: virtual Exception{};
+/// Assertion that throws an InternalCompilerError containing the given description if it is not met.
+#define solAssert(CONDITION, DESCRIPTION) \
+ assertThrow(CONDITION, ::dev::solidity::InternalCompilerError, DESCRIPTION)
+
+#define solUnimplementedAssert(CONDITION, DESCRIPTION) \
+ assertThrow(CONDITION, ::dev::solidity::UnimplementedFeatureError, DESCRIPTION)
+
+#define solUnimplemented(DESCRIPTION) \
+ solUnimplementedAssert(false, DESCRIPTION)
+
class Error: virtual public Exception
{
public:
diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp
index d5dbaa46..2e5005b8 100644
--- a/libsolidity/interface/StandardCompiler.cpp
+++ b/libsolidity/interface/StandardCompiler.cpp
@@ -115,14 +115,6 @@ StringMap createSourceList(Json::Value const& _input)
return sources;
}
-Json::Value methodIdentifiers(ContractDefinition const& _contract)
-{
- Json::Value methodIdentifiers(Json::objectValue);
- for (auto const& it: _contract.interfaceFunctions())
- methodIdentifiers[it.second->externalSignature()] = toHex(it.first.ref());
- return methodIdentifiers;
-}
-
Json::Value formatLinkReferences(std::map<size_t, std::string> const& linkReferences)
{
Json::Value ret(Json::objectValue);
@@ -404,7 +396,7 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
m_compilerStack.streamAssembly(tmp, contractName, createSourceList(_input), false);
evmData["assembly"] = tmp.str();
evmData["legacyAssembly"] = m_compilerStack.streamAssembly(tmp, contractName, createSourceList(_input), true);
- evmData["methodIdentifiers"] = methodIdentifiers(m_compilerStack.contractDefinition(contractName));
+ evmData["methodIdentifiers"] = m_compilerStack.methodIdentifiers(contractName);
evmData["gasEstimates"] = m_compilerStack.gasEstimates(contractName);
evmData["bytecode"] = collectEVMObject(
diff --git a/libsolidity/interface/Utils.h b/libsolidity/interface/Utils.h
deleted file mode 100644
index 0027759c..00000000
--- a/libsolidity/interface/Utils.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- This file is part of solidity.
-
- solidity is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- solidity is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with solidity. If not, see <http://www.gnu.org/licenses/>.
-*/
-/**
- * @author Christian <c@ethdev.com>
- * @date 2014
- * Solidity Utilities.
- */
-
-#pragma once
-
-#include <libdevcore/Assertions.h>
-#include <libsolidity/interface/Exceptions.h>
-
-namespace dev
-{
-namespace solidity
-{
-struct InternalCompilerError;
-struct UnimplementedFeatureError;
-}
-}
-
-/// Assertion that throws an InternalCompilerError containing the given description if it is not met.
-#define solAssert(CONDITION, DESCRIPTION) \
- assertThrow(CONDITION, ::dev::solidity::InternalCompilerError, DESCRIPTION)
-
-#define solUnimplementedAssert(CONDITION, DESCRIPTION) \
- assertThrow(CONDITION, ::dev::solidity::UnimplementedFeatureError, DESCRIPTION)
-
-#define solUnimplemented(DESCRIPTION) \
- solUnimplementedAssert(false, DESCRIPTION)
diff --git a/libsolidity/interface/Version.cpp b/libsolidity/interface/Version.cpp
index 0d23f9c3..a35bfd29 100644
--- a/libsolidity/interface/Version.cpp
+++ b/libsolidity/interface/Version.cpp
@@ -24,7 +24,7 @@
#include <string>
#include <libdevcore/CommonData.h>
#include <libdevcore/Common.h>
-#include <libsolidity/interface/Utils.h>
+#include <libsolidity/interface/Exceptions.h>
#include <solidity/BuildInfo.h>
using namespace dev;
diff --git a/libsolidity/parsing/DocStringParser.cpp b/libsolidity/parsing/DocStringParser.cpp
index cd6c1d8a..0409de72 100644
--- a/libsolidity/parsing/DocStringParser.cpp
+++ b/libsolidity/parsing/DocStringParser.cpp
@@ -1,7 +1,7 @@
#include <libsolidity/parsing/DocStringParser.h>
#include <libsolidity/interface/ErrorReporter.h>
-#include <libsolidity/interface/Utils.h>
+#include <libsolidity/interface/Exceptions.h>
#include <boost/range/irange.hpp>
#include <boost/range/algorithm.hpp>
diff --git a/libsolidity/parsing/ParserBase.cpp b/libsolidity/parsing/ParserBase.cpp
index 9987b82c..5657c2c0 100644
--- a/libsolidity/parsing/ParserBase.cpp
+++ b/libsolidity/parsing/ParserBase.cpp
@@ -43,6 +43,26 @@ int ParserBase::endPosition() const
return m_scanner->currentLocation().end;
}
+Token::Value ParserBase::currentToken() const
+{
+ return m_scanner->currentToken();
+}
+
+Token::Value ParserBase::peekNextToken() const
+{
+ return m_scanner->peekNextToken();
+}
+
+std::string ParserBase::currentLiteral() const
+{
+ return m_scanner->currentLiteral();
+}
+
+Token::Value ParserBase::advance()
+{
+ return m_scanner->next();
+}
+
void ParserBase::expectToken(Token::Value _value)
{
Token::Value tok = m_scanner->currentToken();
diff --git a/libsolidity/parsing/ParserBase.h b/libsolidity/parsing/ParserBase.h
index ae56cead..5b03ab5e 100644
--- a/libsolidity/parsing/ParserBase.h
+++ b/libsolidity/parsing/ParserBase.h
@@ -23,7 +23,6 @@
#pragma once
#include <memory>
-#include <libsolidity/parsing/Scanner.h>
#include <libsolidity/parsing/Token.h>
namespace dev
@@ -51,10 +50,10 @@ protected:
///@name Helper functions
/// If current token value is not _value, throw exception otherwise advance token.
void expectToken(Token::Value _value);
- Token::Value currentToken() const { return m_scanner->currentToken(); }
- Token::Value peekNextToken() const { return m_scanner->peekNextToken(); }
- std::string currentLiteral() const { return m_scanner->currentLiteral(); }
- Token::Value advance() { return m_scanner->next(); }
+ Token::Value currentToken() const;
+ Token::Value peekNextToken() const;
+ std::string currentLiteral() const;
+ Token::Value advance();
///@}
/// Creates a @ref ParserError and annotates it with the current position and the
diff --git a/libsolidity/parsing/Scanner.cpp b/libsolidity/parsing/Scanner.cpp
index 0e60fd0b..fdca23ea 100644
--- a/libsolidity/parsing/Scanner.cpp
+++ b/libsolidity/parsing/Scanner.cpp
@@ -52,7 +52,7 @@
#include <algorithm>
#include <tuple>
-#include <libsolidity/interface/Utils.h>
+#include <libsolidity/interface/Exceptions.h>
#include <libsolidity/parsing/Scanner.h>
using namespace std;
diff --git a/libsolidity/parsing/Token.h b/libsolidity/parsing/Token.h
index 9a557ebd..39c0eff9 100644
--- a/libsolidity/parsing/Token.h
+++ b/libsolidity/parsing/Token.h
@@ -43,7 +43,6 @@
#pragma once
#include <libdevcore/Common.h>
-#include <libsolidity/interface/Utils.h>
#include <libsolidity/interface/Exceptions.h>
#include <libdevcore/UndefMacros.h>
diff --git a/scripts/bytecodecompare/prepare_report.py b/scripts/bytecodecompare/prepare_report.py
index 5a770981..427724b7 100755
--- a/scripts/bytecodecompare/prepare_report.py
+++ b/scripts/bytecodecompare/prepare_report.py
@@ -6,7 +6,7 @@ import subprocess
import json
solc = sys.argv[1]
-report = open("report.txt", "w")
+report = open("report.txt", "wb")
for optimize in [False, True]:
for f in sorted(glob.glob("*.sol")):
diff --git a/scripts/bytecodecompare/storebytecode.bat b/scripts/bytecodecompare/storebytecode.bat
index aa829d20..e64e9276 100644
--- a/scripts/bytecodecompare/storebytecode.bat
+++ b/scripts/bytecodecompare/storebytecode.bat
@@ -27,15 +27,16 @@ cd bytecode
..\scripts\isolate_tests.py ..\test\
..\scripts\bytecodecompare\prepare_report.py ..\build\solc\%CONFIGURATION%\solc.exe
-git clone --depth 2 git@github.com:ethereum/solidity-test-bytecode.git
+REM Send to stdout instead of stderr to not confuse powershell
+git clone --depth 2 git@github.com:ethereum/solidity-test-bytecode.git 2>&1
cd solidity-test-bytecode
git config user.name "travis"
git config user.email "chris@ethereum.org"
-git clean -f -d -x
+git clean -f -d -x 2>&1
if not exist %DIRECTORY% mkdir %DIRECTORY%
set REPORT=%DIRECTORY%/windows.txt
cp ../report.txt %REPORT%
git add %REPORT%
git commit -a -m "Added report."
-git push origin
+git push origin 2>&1
diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp
index e37922c6..0dbedd3c 100644
--- a/solc/CommandLineInterface.cpp
+++ b/solc/CommandLineInterface.cpp
@@ -281,9 +281,10 @@ void CommandLineInterface::handleSignatureHashes(string const& _contract)
if (!m_args.count(g_argSignatureHashes))
return;
+ Json::Value methodIdentifiers = m_compiler->methodIdentifiers(_contract);
string out;
- for (auto const& it: m_compiler->contractDefinition(_contract).interfaceFunctions())
- out += toHex(it.first.ref()) + ": " + it.second->externalSignature() + "\n";
+ for (auto const& name: methodIdentifiers.getMemberNames())
+ out += methodIdentifiers[name].asString() + ": " + name + "\n";
if (m_args.count(g_argOutputDir))
createFile(m_compiler->filesystemFriendlyName(_contract) + ".signatures", out);
@@ -889,7 +890,7 @@ void CommandLineInterface::handleCombinedJSON()
contractData[g_strSrcMapRuntime] = map ? *map : "";
}
if (requests.count(g_strSignatureHashes))
- contractData[g_strSignatureHashes] = m_compiler->functionHashes(m_compiler->contractDefinition(contractName));
+ contractData[g_strSignatureHashes] = m_compiler->methodIdentifiers(contractName);
if (requests.count(g_strNatspecDev))
contractData[g_strNatspecDev] = dev::jsonCompactPrint(m_compiler->natspec(contractName, DocumentationType::NatspecDev));
if (requests.count(g_strNatspecUser))
diff --git a/solc/jsonCompiler.cpp b/solc/jsonCompiler.cpp
index c01c8061..1d31ea30 100644
--- a/solc/jsonCompiler.cpp
+++ b/solc/jsonCompiler.cpp
@@ -21,24 +21,9 @@
*/
#include <string>
-#include <functional>
-#include <iostream>
-#include <json/json.h>
#include <libdevcore/Common.h>
-#include <libdevcore/CommonData.h>
-#include <libdevcore/CommonIO.h>
#include <libdevcore/JSON.h>
-#include <libevmasm/Instruction.h>
-#include <libevmasm/GasMeter.h>
-#include <libsolidity/parsing/Scanner.h>
-#include <libsolidity/parsing/Parser.h>
-#include <libsolidity/ast/ASTPrinter.h>
-#include <libsolidity/analysis/NameAndTypeResolver.h>
-#include <libsolidity/interface/Exceptions.h>
-#include <libsolidity/interface/CompilerStack.h>
#include <libsolidity/interface/StandardCompiler.h>
-#include <libsolidity/interface/SourceReferenceFormatter.h>
-#include <libsolidity/ast/ASTJsonConverter.h>
#include <libsolidity/interface/Version.h>
#include "license.h"
@@ -109,9 +94,8 @@ Json::Value gasToJson(Json::Value const& _value)
return Json::Value(Json::LargestUInt(value));
}
-Json::Value estimateGas(CompilerStack const& _compiler, string const& _contract)
+Json::Value translateGasEstimates(Json::Value const& estimates)
{
- Json::Value estimates = _compiler.gasEstimates(_contract);
Json::Value output(Json::objectValue);
if (estimates["creation"].isObject())
@@ -131,121 +115,106 @@ Json::Value estimateGas(CompilerStack const& _compiler, string const& _contract)
string compile(StringMap const& _sources, bool _optimize, CStyleReadFileCallback _readCallback)
{
- Json::Value output(Json::objectValue);
- Json::Value errors(Json::arrayValue);
- CompilerStack compiler(wrapReadCallback(_readCallback));
- auto scannerFromSourceName = [&](string const& _sourceName) -> solidity::Scanner const& { return compiler.scanner(_sourceName); };
- bool success = false;
- try
- {
- compiler.addSources(_sources);
- bool succ = compiler.compile(_optimize);
- for (auto const& error: compiler.errors())
- {
- auto err = dynamic_pointer_cast<Error const>(error);
- errors.append(SourceReferenceFormatter::formatExceptionInformation(
- *error,
- (err->type() == Error::Type::Warning) ? "Warning" : "Error",
- scannerFromSourceName
- ));
- }
- success = succ; // keep success false on exception
- }
- catch (Error const& error)
- {
- errors.append(SourceReferenceFormatter::formatExceptionInformation(error, error.typeName(), scannerFromSourceName));
- }
- catch (CompilerError const& exception)
- {
- errors.append(SourceReferenceFormatter::formatExceptionInformation(exception, "Compiler error (" + exception.lineInfo() + ")", scannerFromSourceName));
- }
- catch (InternalCompilerError const& exception)
- {
- errors.append(SourceReferenceFormatter::formatExceptionInformation(exception, "Internal compiler error (" + exception.lineInfo() + ")", scannerFromSourceName));
- }
- catch (UnimplementedFeatureError const& exception)
- {
- errors.append(SourceReferenceFormatter::formatExceptionInformation(exception, "Unimplemented feature (" + exception.lineInfo() + ")", scannerFromSourceName));
- }
- catch (Exception const& exception)
+ /// create new JSON input format
+ Json::Value input = Json::objectValue;
+ input["language"] = "Solidity";
+ input["sources"] = Json::objectValue;
+ for (auto const& source: _sources)
{
- errors.append("Exception during compilation: " + boost::diagnostic_information(exception));
+ input["sources"][source.first] = Json::objectValue;
+ input["sources"][source.first]["content"] = source.second;
}
- catch (...)
+ input["settings"] = Json::objectValue;
+ input["settings"]["optimizer"] = Json::objectValue;
+ input["settings"]["optimizer"]["enabled"] = _optimize;
+ input["settings"]["optimizer"]["runs"] = 200;
+
+ StandardCompiler compiler(wrapReadCallback(_readCallback));
+ Json::Value ret = compiler.compile(input);
+
+ /// transform JSON to match the old format
+ // {
+ // "errors": [ "Error 1", "Error 2" ],
+ // "sourceList": [ "sourcename1", "sourcename2" ],
+ // "sources": {
+ // "sourcename1": {
+ // "AST": {}
+ // }
+ // },
+ // "contracts": {
+ // "Contract1": {
+ // "interface": "[...abi...]",
+ // "bytecode": "ff0011...",
+ // "runtimeBytecode": "ff0011",
+ // "opcodes": "PUSH 1 POP STOP",
+ // "metadata": "{...metadata...}",
+ // "functionHashes": {
+ // "test(uint256)": "11ff2233"
+ // },
+ // "gasEstimates": {
+ // "creation": [ 224, 42000 ],
+ // "external": {
+ // "11ff2233": null,
+ // "3322ff11": 1234
+ // },
+ // "internal": {
+ // }
+ // },
+ // "srcmap" = "0:1:2",
+ // "srcmapRuntime" = "0:1:2",
+ // "assembly" = {}
+ // }
+ // },
+ // "formal": {
+ // "errors": [ "Error 1" ],
+ // "why3": "why3 source"
+ // }
+ // }
+ Json::Value output = Json::objectValue;
+
+ if (ret.isMember("errors"))
{
- errors.append("Unknown exception during compilation.");
+ output["errors"] = Json::arrayValue;
+ for (auto const& error: ret["errors"])
+ output["errors"].append(
+ !error["formattedMessage"].empty() ? error["formattedMessage"] : error["message"]
+ );
}
- if (errors.size() > 0)
- output["errors"] = errors;
+ output["sourceList"] = Json::arrayValue;
+ for (auto const& source: _sources)
+ output["sourceList"].append(source.first);
- if (success)
+ if (ret.isMember("sources"))
{
- try
+ output["sources"] = Json::objectValue;
+ for (auto const& sourceName: ret["sources"].getMemberNames())
{
- output["contracts"] = Json::Value(Json::objectValue);
- for (string const& contractName: compiler.contractNames())
- {
- Json::Value contractData(Json::objectValue);
- contractData["interface"] = dev::jsonCompactPrint(compiler.contractABI(contractName));
- contractData["bytecode"] = compiler.object(contractName).toHex();
- contractData["runtimeBytecode"] = compiler.runtimeObject(contractName).toHex();
- contractData["opcodes"] = solidity::disassemble(compiler.object(contractName).bytecode);
- contractData["metadata"] = compiler.onChainMetadata(contractName);
- contractData["functionHashes"] = compiler.functionHashes(compiler.contractDefinition(contractName));
- contractData["gasEstimates"] = estimateGas(compiler, contractName);
- auto sourceMap = compiler.sourceMapping(contractName);
- contractData["srcmap"] = sourceMap ? *sourceMap : "";
- auto runtimeSourceMap = compiler.runtimeSourceMapping(contractName);
- contractData["srcmapRuntime"] = runtimeSourceMap ? *runtimeSourceMap : "";
- ostringstream unused;
- contractData["assembly"] = compiler.streamAssembly(unused, contractName, _sources, true);
- output["contracts"][contractName] = contractData;
- }
- }
- catch (...)
- {
- output["errors"].append("Unknown exception while generating contract data output.");
+ output["sources"][sourceName] = Json::objectValue;
+ output["sources"][sourceName]["AST"] = ret["sources"][sourceName]["legacyAST"];
}
+ }
- try
- {
- // Do not taint the internal error list
- ErrorList formalErrors;
- ErrorReporter errorReporter(formalErrors);
- if (compiler.prepareFormalAnalysis(&errorReporter))
- output["formal"]["why3"] = compiler.formalTranslation();
- if (!errorReporter.errors().empty())
+ if (ret.isMember("contracts"))
+ {
+ output["contracts"] = Json::objectValue;
+ for (auto const& sourceName: ret["contracts"].getMemberNames())
+ for (auto const& contractName: ret["contracts"][sourceName].getMemberNames())
{
- Json::Value errors(Json::arrayValue);
- for (auto const& error: errorReporter.errors())
- errors.append(SourceReferenceFormatter::formatExceptionInformation(
- *error,
- (error->type() == Error::Type::Warning) ? "Warning" : "Error",
- scannerFromSourceName
- ));
- output["formal"]["errors"] = errors;
+ Json::Value contractInput = ret["contracts"][sourceName][contractName];
+ Json::Value contractOutput = Json::objectValue;
+ contractOutput["interface"] = dev::jsonCompactPrint(contractInput["abi"]);
+ contractOutput["metadata"] = contractInput["metadata"];
+ contractOutput["functionHashes"] = contractInput["evm"]["methodIdentifiers"];
+ contractOutput["gasEstimates"] = translateGasEstimates(contractInput["evm"]["gasEstimates"]);
+ contractOutput["assembly"] = contractInput["evm"]["legacyAssembly"];
+ contractOutput["bytecode"] = contractInput["evm"]["bytecode"]["object"];
+ contractOutput["opcodes"] = contractInput["evm"]["bytecode"]["opcodes"];
+ contractOutput["srcmap"] = contractInput["evm"]["bytecode"]["sourceMap"];
+ contractOutput["runtimeBytecode"] = contractInput["evm"]["deployedBytecode"]["object"];
+ contractOutput["srcmapRuntime"] = contractInput["evm"]["deployedBytecode"]["sourceMap"];
+ output["contracts"][sourceName + ":" + contractName] = contractOutput;
}
- }
- catch (...)
- {
- output["errors"].append("Unknown exception while generating formal method output.");
- }
-
- try
- {
- // Indices into this array are used to abbreviate source names in source locations.
- output["sourceList"] = Json::Value(Json::arrayValue);
- for (auto const& source: compiler.sourceNames())
- output["sourceList"].append(source);
- output["sources"] = Json::Value(Json::objectValue);
- for (auto const& source: compiler.sourceNames())
- output["sources"][source]["AST"] = ASTJsonConverter(true, compiler.sourceIndices()).toJson(compiler.ast(source));
- }
- catch (...)
- {
- output["errors"].append("Unknown exception while generating source name output.");
- }
}
try
diff --git a/test/libdevcore/MiniMoustache.cpp b/test/libdevcore/MiniMoustache.cpp
new file mode 100644
index 00000000..84149173
--- /dev/null
+++ b/test/libdevcore/MiniMoustache.cpp
@@ -0,0 +1,127 @@
+/*
+ This file is part of solidity.
+
+ solidity is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ solidity is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with solidity. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * Unit tests for the mini moustache class.
+ */
+
+#include <libdevcore/Whiskers.h>
+
+#include "../TestHelper.h"
+
+using namespace std;
+
+namespace dev
+{
+namespace test
+{
+
+BOOST_AUTO_TEST_SUITE(WhiskersTest)
+
+BOOST_AUTO_TEST_CASE(no_templates)
+{
+ string templ = "this text does not contain templates";
+ BOOST_CHECK_EQUAL(Whiskers(templ).render(), templ);
+}
+
+BOOST_AUTO_TEST_CASE(basic_replacement)
+{
+ string templ = "a <b> x <c> -> <d>.";
+ string result = Whiskers(templ)
+ ("b", "BE")
+ ("c", "CE")
+ ("d", "DE")
+ .render();
+ BOOST_CHECK_EQUAL(result, "a BE x CE -> DE.");
+}
+
+BOOST_AUTO_TEST_CASE(tag_unavailable)
+{
+ string templ = "<b>";
+ Whiskers m(templ);
+ BOOST_CHECK_THROW(m.render(), WhiskersError);
+}
+
+BOOST_AUTO_TEST_CASE(complicated_replacement)
+{
+ string templ = "a <b> x <complicated> \n <nes<ted>>.";
+ string result = Whiskers(templ)
+ ("b", "BE")
+ ("complicated", "CO<M>PL")
+ ("nes<ted", "NEST")
+ .render();
+ BOOST_CHECK_EQUAL(result, "a BE x CO<M>PL \n NEST>.");
+}
+
+BOOST_AUTO_TEST_CASE(non_existing_list)
+{
+ string templ = "a <#b></b>";
+ Whiskers m(templ);
+ BOOST_CHECK_THROW(m.render(), WhiskersError);
+}
+
+BOOST_AUTO_TEST_CASE(empty_list)
+{
+ string templ = "a <#b></b>x";
+ string result = Whiskers(templ)("b", vector<Whiskers::StringMap>{}).render();
+ BOOST_CHECK_EQUAL(result, "a x");
+}
+
+BOOST_AUTO_TEST_CASE(list)
+{
+ string templ = "a<#b>( <g> - <h> )</b>x";
+ vector<map<string, string>> list(2);
+ list[0]["g"] = "GE";
+ list[0]["h"] = "H";
+ list[1]["g"] = "2GE";
+ list[1]["h"] = "2H";
+ string result = Whiskers(templ)("b", list).render();
+ BOOST_CHECK_EQUAL(result, "a( GE - H )( 2GE - 2H )x");
+}
+
+BOOST_AUTO_TEST_CASE(recursive_list)
+{
+ // Check that templates resulting from lists are not expanded again
+ string templ = "a<#b> 1<g>3 </b><x>";
+ vector<map<string, string>> list(1);
+ list[0]["g"] = "<x>";
+ string result = Whiskers(templ)("x", "X")("b", list).render();
+ BOOST_CHECK_EQUAL(result, "a 1<x>3 X");
+}
+
+BOOST_AUTO_TEST_CASE(list_can_access_upper)
+{
+ string templ = "<#b>(<a>)</b>";
+ vector<map<string, string>> list(2);
+ Whiskers m(templ);
+ string result = m("a", "A")("b", list).render();
+ BOOST_CHECK_EQUAL(result, "(A)(A)");
+}
+
+BOOST_AUTO_TEST_CASE(parameter_collision)
+{
+ string templ = "a <#b></b>";
+ vector<map<string, string>> list(1);
+ list[0]["a"] = "x";
+ Whiskers m(templ);
+ m("a", "X")("b", list);
+ BOOST_CHECK_THROW(m.render(), WhiskersError);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+}
+}
diff --git a/test/liblll/EndToEndTest.cpp b/test/liblll/EndToEndTest.cpp
index 3928ff45..3037b14b 100644
--- a/test/liblll/EndToEndTest.cpp
+++ b/test/liblll/EndToEndTest.cpp
@@ -57,6 +57,88 @@ BOOST_AUTO_TEST_CASE(panic)
BOOST_REQUIRE(m_output.empty());
}
+BOOST_AUTO_TEST_CASE(variables)
+{
+ char const* sourceCode = R"(
+ (returnlll
+ (seq
+ (set 'x 1)
+ (set 'y 2)
+ ;; this should equal to 3
+ (set 'z (add (get 'x) (get 'y)))
+ ;; overwriting it here
+ (set 'y 4)
+ ;; each variable has a 32 byte slot, starting from memory location 0x80
+ ;; variable addresses can also be retrieved by x or (ref 'x)
+ (set 'k (add (add (ref 'x) (ref 'y)) z))
+ (return (add (add (get 'x) (add (get 'y) (get 'z))) (get 'k)))))
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callFallback() == encodeArgs(u256(488)));
+}
+
+BOOST_AUTO_TEST_CASE(when)
+{
+ char const* sourceCode = R"(
+ (returnlll
+ (seq
+ (when (= (calldatasize) 0) (return 1))
+ (when (!= (calldatasize) 0) (return 2))))
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("test()") == encodeArgs(u256(2)));
+ BOOST_CHECK(callFallback() == toBigEndian(u256(1)));
+}
+
+BOOST_AUTO_TEST_CASE(unless)
+{
+ char const* sourceCode = R"(
+ (returnlll
+ (seq
+ (unless (!= (calldatasize) 0) (return 1))
+ (unless (= (calldatasize) 0) (return 2))))
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("test()") == encodeArgs(u256(2)));
+ BOOST_CHECK(callFallback() == toBigEndian(u256(1)));
+}
+
+BOOST_AUTO_TEST_CASE(conditional_literal)
+{
+ char const* sourceCode = R"(
+ (returnlll
+ (seq
+ (return (if (= (calldatasize) 0) 1 2))))
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("test()") == encodeArgs(u256(2)));
+ BOOST_CHECK(callFallback() == toBigEndian(u256(1)));
+}
+
+BOOST_AUTO_TEST_CASE(conditional)
+{
+ char const* sourceCode = R"(
+ (returnlll
+ (seq
+ (if (= (calldatasize) 0) (return 1) (return 2))))
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("test()") == encodeArgs(u256(2)));
+ BOOST_CHECK(callFallback() == toBigEndian(u256(1)));
+}
+
+BOOST_AUTO_TEST_CASE(conditional_seq)
+{
+ char const* sourceCode = R"(
+ (returnlll
+ (seq
+ (return (if (= (calldatasize) 0) { 0 2 1 } { 0 1 2 }))))
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("test()") == encodeArgs(u256(2)));
+ BOOST_CHECK(callFallback() == toBigEndian(u256(1)));
+}
+
BOOST_AUTO_TEST_CASE(exp_operator_const)
{
char const* sourceCode = R"(
@@ -304,6 +386,51 @@ BOOST_AUTO_TEST_CASE(keccak256_32bytes)
fromHex("b10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6")));
}
+BOOST_AUTO_TEST_CASE(msg_six_args)
+{
+ char const* sourceCode = R"(
+ (returnlll
+ (seq
+ (when (= 0 (calldatasize))
+ (seq
+ (mstore 0x40 1)
+ (def 'outsize 0x20)
+ (return (msg 1000 (address) 42 0x40 0x20 outsize) outsize)))
+ (when (= 1 (calldataload 0x00))
+ (return (callvalue)))))
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callFallbackWithValue(42) == encodeArgs(u256(42)));
+}
+
+BOOST_AUTO_TEST_CASE(create_1_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)));
+}
+
+BOOST_AUTO_TEST_CASE(create_2_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)));
+}
+
BOOST_AUTO_TEST_CASE(sha3_two_args)
{
char const* sourceCode = R"(
@@ -328,6 +455,25 @@ BOOST_AUTO_TEST_CASE(sha3_one_arg)
fromHex("b10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6")));
}
+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")));
+}
+
BOOST_AUTO_TEST_CASE(shift_left)
{
char const* sourceCode = R"(
diff --git a/test/libsolidity/Assembly.cpp b/test/libsolidity/Assembly.cpp
index e52f4d50..99a2996e 100644
--- a/test/libsolidity/Assembly.cpp
+++ b/test/libsolidity/Assembly.cpp
@@ -119,8 +119,8 @@ BOOST_AUTO_TEST_CASE(location_test)
shared_ptr<string const> n = make_shared<string>("");
AssemblyItems items = compileContract(sourceCode);
vector<SourceLocation> locations =
- vector<SourceLocation>(17, SourceLocation(2, 75, n)) +
- vector<SourceLocation>(30, SourceLocation(20, 72, n)) +
+ vector<SourceLocation>(19, SourceLocation(2, 75, n)) +
+ vector<SourceLocation>(32, SourceLocation(20, 72, n)) +
vector<SourceLocation>{SourceLocation(42, 51, n), SourceLocation(65, 67, n)} +
vector<SourceLocation>(2, SourceLocation(58, 67, n)) +
vector<SourceLocation>(3, SourceLocation(20, 72, n));
diff --git a/test/libsolidity/JSONCompiler.cpp b/test/libsolidity/JSONCompiler.cpp
index f5154395..aa690f0b 100644
--- a/test/libsolidity/JSONCompiler.cpp
+++ b/test/libsolidity/JSONCompiler.cpp
@@ -73,28 +73,36 @@ BOOST_AUTO_TEST_CASE(basic_compilation)
Json::Value contract = result["contracts"]["fileA:A"];
BOOST_CHECK(contract.isObject());
BOOST_CHECK(contract["interface"].isString());
- BOOST_CHECK(contract["interface"].asString() == "[]");
+ BOOST_CHECK_EQUAL(contract["interface"].asString(), "[]");
BOOST_CHECK(contract["bytecode"].isString());
- BOOST_CHECK(dev::test::bytecodeSansMetadata(contract["bytecode"].asString()) ==
- "60606040523415600b57fe5b5b60338060196000396000f30060606040525bfe00");
+ BOOST_CHECK_EQUAL(
+ dev::test::bytecodeSansMetadata(contract["bytecode"].asString()),
+ "60606040523415600e57600080fd5b5b603680601c6000396000f30060606040525b600080fd00"
+ );
BOOST_CHECK(contract["runtimeBytecode"].isString());
- BOOST_CHECK(dev::test::bytecodeSansMetadata(contract["runtimeBytecode"].asString()) ==
- "60606040525bfe00");
+ BOOST_CHECK_EQUAL(
+ dev::test::bytecodeSansMetadata(contract["runtimeBytecode"].asString()),
+ "60606040525b600080fd00"
+ );
BOOST_CHECK(contract["functionHashes"].isObject());
BOOST_CHECK(contract["gasEstimates"].isObject());
- BOOST_CHECK(dev::jsonCompactPrint(contract["gasEstimates"]) ==
- "{\"creation\":[62,10200],\"external\":{},\"internal\":{}}");
+ BOOST_CHECK_EQUAL(
+ dev::jsonCompactPrint(contract["gasEstimates"]),
+ "{\"creation\":[62,10800],\"external\":{},\"internal\":{}}"
+ );
BOOST_CHECK(contract["metadata"].isString());
BOOST_CHECK(dev::test::isValidMetadata(contract["metadata"].asString()));
BOOST_CHECK(result["sources"].isObject());
BOOST_CHECK(result["sources"]["fileA"].isObject());
BOOST_CHECK(result["sources"]["fileA"]["AST"].isObject());
- BOOST_CHECK(dev::jsonCompactPrint(result["sources"]["fileA"]["AST"]) ==
+ BOOST_CHECK_EQUAL(
+ dev::jsonCompactPrint(result["sources"]["fileA"]["AST"]),
"{\"attributes\":{\"absolutePath\":\"fileA\",\"exportedSymbols\":{\"A\":[1]}},"
"\"children\":[{\"attributes\":{\"baseContracts\":[null],\"contractDependencies\":[null],"
"\"contractKind\":\"contract\",\"documentation\":null,\"fullyImplemented\":true,\"linearizedBaseContracts\":[1],"
"\"name\":\"A\",\"nodes\":[null],\"scope\":2},\"id\":1,\"name\":\"ContractDefinition\","
- "\"src\":\"0:14:0\"}],\"id\":2,\"name\":\"SourceUnit\",\"src\":\"0:14:0\"}");
+ "\"src\":\"0:14:0\"}],\"id\":2,\"name\":\"SourceUnit\",\"src\":\"0:14:0\"}"
+ );
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp
index ba507e0c..823a8eda 100644
--- a/test/libsolidity/SolidityEndToEndTest.cpp
+++ b/test/libsolidity/SolidityEndToEndTest.cpp
@@ -9469,6 +9469,29 @@ BOOST_AUTO_TEST_CASE(revert)
BOOST_CHECK(callContractFunction("a()") == encodeArgs(u256(42)));
}
+BOOST_AUTO_TEST_CASE(literal_empty_string)
+{
+ char const* sourceCode = R"(
+ contract C {
+ bytes32 public x;
+ uint public a;
+ function f(bytes32 _x, uint _a) {
+ x = _x;
+ a = _a;
+ }
+ function g() {
+ this.f("", 2);
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "C");
+ BOOST_CHECK(callContractFunction("x()") == encodeArgs(u256(0)));
+ BOOST_CHECK(callContractFunction("a()") == encodeArgs(u256(0)));
+ BOOST_CHECK(callContractFunction("g()") == encodeArgs());
+ BOOST_CHECK(callContractFunction("x()") == encodeArgs(u256(0)));
+ BOOST_CHECK(callContractFunction("a()") == encodeArgs(u256(2)));
+}
+
BOOST_AUTO_TEST_CASE(scientific_notation)
{
char const* sourceCode = R"(
diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp
index fc8cdc35..e18303f5 100644
--- a/test/libsolidity/SolidityNameAndTypeResolution.cpp
+++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp
@@ -1597,6 +1597,16 @@ BOOST_AUTO_TEST_CASE(empty_name_input_parameter)
CHECK_SUCCESS(text);
}
+BOOST_AUTO_TEST_CASE(constant_input_parameter)
+{
+ char const* text = R"(
+ contract test {
+ function f(uint[] constant a) { }
+ }
+ )";
+ CHECK_ERROR_ALLOW_MULTI(text, TypeError, "Illegal use of \"constant\" specifier.");
+}
+
BOOST_AUTO_TEST_CASE(empty_name_return_parameter)
{
char const* text = R"(
@@ -2304,6 +2314,16 @@ BOOST_AUTO_TEST_CASE(constant_struct)
CHECK_ERROR(text, TypeError, "implemented");
}
+BOOST_AUTO_TEST_CASE(address_is_constant)
+{
+ char const* text = R"(
+ contract C {
+ address constant x = 0x1212121212121212121212121212121212121212;
+ }
+ )";
+ CHECK_SUCCESS_NO_WARNINGS(text);
+}
+
BOOST_AUTO_TEST_CASE(uninitialized_const_variable)
{
char const* text = R"(
@@ -5612,6 +5632,16 @@ BOOST_AUTO_TEST_CASE(interface_variables)
CHECK_ERROR(text, TypeError, "Variables cannot be declared in interfaces");
}
+BOOST_AUTO_TEST_CASE(interface_function_parameters)
+{
+ char const* text = R"(
+ interface I {
+ function f(uint a) returns(bool);
+ }
+ )";
+ success(text);
+}
+
BOOST_AUTO_TEST_CASE(interface_enums)
{
char const* text = R"(
diff --git a/test/libsolidity/StandardCompiler.cpp b/test/libsolidity/StandardCompiler.cpp
index 35644a4d..be13d46b 100644
--- a/test/libsolidity/StandardCompiler.cpp
+++ b/test/libsolidity/StandardCompiler.cpp
@@ -189,37 +189,43 @@ BOOST_AUTO_TEST_CASE(basic_compilation)
Json::Value contract = getContractResult(result, "fileA", "A");
BOOST_CHECK(contract.isObject());
BOOST_CHECK(contract["abi"].isArray());
- BOOST_CHECK(dev::jsonCompactPrint(contract["abi"]) == "[]");
+ BOOST_CHECK_EQUAL(dev::jsonCompactPrint(contract["abi"]), "[]");
BOOST_CHECK(contract["devdoc"].isObject());
- BOOST_CHECK(dev::jsonCompactPrint(contract["devdoc"]) == "{\"methods\":{}}");
+ BOOST_CHECK_EQUAL(dev::jsonCompactPrint(contract["devdoc"]), "{\"methods\":{}}");
BOOST_CHECK(contract["userdoc"].isObject());
- BOOST_CHECK(dev::jsonCompactPrint(contract["userdoc"]) == "{\"methods\":{}}");
+ BOOST_CHECK_EQUAL(dev::jsonCompactPrint(contract["userdoc"]), "{\"methods\":{}}");
BOOST_CHECK(contract["evm"].isObject());
/// @TODO check evm.methodIdentifiers, legacyAssembly, bytecode, deployedBytecode
BOOST_CHECK(contract["evm"]["bytecode"].isObject());
BOOST_CHECK(contract["evm"]["bytecode"]["object"].isString());
- BOOST_CHECK(dev::test::bytecodeSansMetadata(contract["evm"]["bytecode"]["object"].asString()) ==
- "60606040523415600b57fe5b5b60338060196000396000f30060606040525bfe00");
+ BOOST_CHECK_EQUAL(
+ dev::test::bytecodeSansMetadata(contract["evm"]["bytecode"]["object"].asString()),
+ "60606040523415600e57600080fd5b5b603680601c6000396000f30060606040525b600080fd00"
+ );
BOOST_CHECK(contract["evm"]["assembly"].isString());
BOOST_CHECK(contract["evm"]["assembly"].asString().find(
" /* \"fileA\":0:14 contract A { } */\n mstore(0x40, 0x60)\n jumpi(tag_1, iszero(callvalue))\n"
- " invalid\ntag_1:\ntag_2:\n dataSize(sub_0)\n dup1\n dataOffset(sub_0)\n 0x0\n codecopy\n 0x0\n"
+ " 0x0\n dup1\n revert\ntag_1:\ntag_2:\n dataSize(sub_0)\n dup1\n dataOffset(sub_0)\n 0x0\n codecopy\n 0x0\n"
" return\nstop\n\nsub_0: assembly {\n /* \"fileA\":0:14 contract A { } */\n"
- " mstore(0x40, 0x60)\n tag_1:\n invalid\n\n"
- " auxdata: 0xa165627a7a72305820") != std::string::npos);
+ " mstore(0x40, 0x60)\n tag_1:\n 0x0\n dup1\n revert\n\n"
+ " auxdata: 0xa165627a7a7230582") == 0);
BOOST_CHECK(contract["evm"]["gasEstimates"].isObject());
- BOOST_CHECK(dev::jsonCompactPrint(contract["evm"]["gasEstimates"]) ==
- "{\"creation\":{\"codeDepositCost\":\"10200\",\"executionCost\":\"62\",\"totalCost\":\"10262\"}}");
+ BOOST_CHECK_EQUAL(
+ dev::jsonCompactPrint(contract["evm"]["gasEstimates"]),
+ "{\"creation\":{\"codeDepositCost\":\"10800\",\"executionCost\":\"62\",\"totalCost\":\"10862\"}}"
+ );
BOOST_CHECK(contract["metadata"].isString());
BOOST_CHECK(dev::test::isValidMetadata(contract["metadata"].asString()));
BOOST_CHECK(result["sources"].isObject());
BOOST_CHECK(result["sources"]["fileA"].isObject());
BOOST_CHECK(result["sources"]["fileA"]["legacyAST"].isObject());
- BOOST_CHECK(dev::jsonCompactPrint(result["sources"]["fileA"]["legacyAST"]) ==
+ BOOST_CHECK_EQUAL(
+ dev::jsonCompactPrint(result["sources"]["fileA"]["legacyAST"]),
"{\"attributes\":{\"absolutePath\":\"fileA\",\"exportedSymbols\":{\"A\":[1]}},\"children\":"
"[{\"attributes\":{\"baseContracts\":[null],\"contractDependencies\":[null],\"contractKind\":\"contract\","
"\"documentation\":null,\"fullyImplemented\":true,\"linearizedBaseContracts\":[1],\"name\":\"A\",\"nodes\":[null],\"scope\":2},"
- "\"id\":1,\"name\":\"ContractDefinition\",\"src\":\"0:14:0\"}],\"id\":2,\"name\":\"SourceUnit\",\"src\":\"0:14:0\"}");
+ "\"id\":1,\"name\":\"ContractDefinition\",\"src\":\"0:14:0\"}],\"id\":2,\"name\":\"SourceUnit\",\"src\":\"0:14:0\"}"
+ );
}
BOOST_AUTO_TEST_SUITE_END()