mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge remote-tracking branch 'origin/develop' into breaking
This commit is contained in:
commit
7d68f9f6c3
@ -122,6 +122,10 @@ defaults:
|
||||
name: command line tests
|
||||
command: ./test/cmdlineTests.sh
|
||||
|
||||
- run_docs_version_pragma_check: &run_docs_version_pragma_check
|
||||
name: docs version pragma check
|
||||
command: ./scripts/docs_version_pragma_check.sh
|
||||
|
||||
- test_ubuntu1604_clang: &test_ubuntu1604_clang
|
||||
docker:
|
||||
- image: ethereum/solidity-buildpack-deps:ubuntu1604-clang-ossfuzz-<< pipeline.parameters.ubuntu-1604-clang-ossfuzz-docker-image-rev >>
|
||||
@ -303,6 +307,18 @@ jobs:
|
||||
name: Linting Python Scripts
|
||||
command: ./scripts/pylint_all.py
|
||||
|
||||
chk_antlr_grammar:
|
||||
docker:
|
||||
- image: buildpack-deps:eoan
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Install Java
|
||||
command: apt -q update && apt install -y openjdk-14-jdk
|
||||
- run:
|
||||
name: Run tests
|
||||
command: ./scripts/test_antlr_grammar.sh
|
||||
|
||||
chk_buglist:
|
||||
docker:
|
||||
- image: circleci/node
|
||||
@ -605,6 +621,7 @@ jobs:
|
||||
- attach_workspace:
|
||||
at: build
|
||||
- run: *run_cmdline_tests
|
||||
- run: *run_docs_version_pragma_check
|
||||
- store_test_results: *store_test_results
|
||||
- store_artifacts: *artifacts_test_results
|
||||
|
||||
@ -768,6 +785,7 @@ workflows:
|
||||
- chk_buglist: *workflow_trigger_on_tags
|
||||
- chk_proofs: *workflow_trigger_on_tags
|
||||
- chk_pylint: *workflow_trigger_on_tags
|
||||
- chk_antlr_grammar: *workflow_trigger_on_tags
|
||||
|
||||
# build-only
|
||||
- b_docs: *workflow_trigger_on_tags
|
||||
|
@ -6,26 +6,35 @@
|
||||
# Note that clang-format cannot express the style that closing parentheses
|
||||
# behave similar to closing curly braces in a multi-line setting in that
|
||||
# they have to be on a line of their own at the same indentation level
|
||||
# as the opening part.
|
||||
# as the opening part (aka "dangling parenthesis", see https://reviews.llvm.org/D33029).
|
||||
|
||||
Language: Cpp
|
||||
BasedOnStyle: LLVM
|
||||
AccessModifierOffset: -4
|
||||
AlignAfterOpenBracket: AlwaysBreak
|
||||
AlignEscapedNewlinesLeft: true
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakTemplateDeclarations: Yes
|
||||
BinPackArguments: false
|
||||
BinPackParameters: false
|
||||
BreakBeforeBinaryOperators: All
|
||||
BreakBeforeBraces: Allman
|
||||
ColumnLimit: 120
|
||||
ContinuationIndentWidth: 4
|
||||
FixNamespaceComments: false
|
||||
IndentWidth: 4
|
||||
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||
MaxEmptyLinesToKeep: 2
|
||||
PenaltyBreakBeforeFirstCallParameter: 2000
|
||||
PointerAlignment: Left
|
||||
SpaceAfterCStyleCast: true
|
||||
SpaceAfterTemplateKeyword: false
|
||||
SpaceBeforeCtorInitializerColon: false
|
||||
SpaceBeforeInheritanceColon: false
|
||||
SpaceBeforeParens: ControlStatements
|
||||
SpaceBeforeRangeBasedForLoopColon: false
|
||||
TabWidth: 4
|
||||
UseTab: ForIndentation
|
||||
UseTab: Always
|
||||
|
||||
# Local Variables:
|
||||
# mode: yaml
|
||||
|
@ -141,7 +141,7 @@ struct MeanSigma
|
||||
double const d = 0;
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
char* s;
|
||||
char* s = nullptr;
|
||||
MeanAndSigma ms meanAndSigma(std::vector<float> const& _v, Accuracy _a);
|
||||
Derived* x = dynamic_cast<Derived*>(base);
|
||||
for (auto i = x->begin(); i != x->end(); ++i) {}
|
||||
|
10
Changelog.md
10
Changelog.md
@ -18,7 +18,9 @@ Compiler Features:
|
||||
|
||||
|
||||
Bugfixes:
|
||||
* Metadata: Added support for IPFS hashes of large files that need to be split in multiple chunks.
|
||||
* Inline Assembly: Fix internal error when accessing incorrect constant variables.
|
||||
* Inheritance: Allow public state variables to override functions with dynamic memory types in their return values.
|
||||
* JSON AST: Always add pointer suffix for memory reference types.
|
||||
|
||||
|
||||
### 0.6.4 (2020-03-10)
|
||||
@ -143,6 +145,12 @@ Compiler Features:
|
||||
* ABIEncoderV2: Do not warn about enabled ABIEncoderV2 anymore (the pragma is still needed, though).
|
||||
|
||||
|
||||
### 0.5.17 (2020-03-17)
|
||||
|
||||
Bugfixes:
|
||||
* Type Checker: Disallow overriding of private functions.
|
||||
|
||||
|
||||
### 0.5.16 (2020-01-02)
|
||||
|
||||
Backported Bugfixes:
|
||||
|
@ -292,8 +292,9 @@ Consider you have the following pre-0.5.0 contract already deployed:
|
||||
|
||||
::
|
||||
|
||||
// This will not compile with the current version of the compiler
|
||||
pragma solidity ^0.4.25;
|
||||
// This will report a warning until version 0.4.25 of the compiler
|
||||
// This will not compile after 0.5.0
|
||||
contract OldContract {
|
||||
function someOldFunction(uint8 a) {
|
||||
//...
|
||||
@ -369,8 +370,8 @@ Old version:
|
||||
|
||||
::
|
||||
|
||||
// This will not compile
|
||||
pragma solidity ^0.4.25;
|
||||
// This will not compile after 0.5.0
|
||||
|
||||
contract OtherContract {
|
||||
uint x;
|
||||
@ -396,7 +397,7 @@ Old version:
|
||||
// Throw is fine in this version.
|
||||
if (x > 100)
|
||||
throw;
|
||||
bytes b = new bytes(x);
|
||||
bytes memory b = new bytes(x);
|
||||
y = -3 >> 1;
|
||||
// y == -1 (wrong, should be -2)
|
||||
do {
|
||||
@ -431,14 +432,15 @@ New version:
|
||||
|
||||
::
|
||||
|
||||
pragma solidity >=0.5.0 <0.8.0;
|
||||
pragma solidity >=0.5.0 <0.5.99;
|
||||
// This will not compile after 0.6.0
|
||||
|
||||
contract OtherContract {
|
||||
uint x;
|
||||
function f(uint y) external {
|
||||
x = y;
|
||||
}
|
||||
receive() payable external {}
|
||||
function() payable external {}
|
||||
}
|
||||
|
||||
contract New {
|
||||
|
482
docs/Solidity.g4
Normal file
482
docs/Solidity.g4
Normal file
@ -0,0 +1,482 @@
|
||||
// Copyright 2020 Gonçalo Sá <goncalo.sa@consensys.net>
|
||||
// Copyright 2016-2019 Federico Bond <federicobond@gmail.com>
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for details.
|
||||
|
||||
// This grammar is much less strict than what Solidity currently parses
|
||||
// to allow this to pass with older versions of Solidity.
|
||||
|
||||
grammar Solidity;
|
||||
|
||||
sourceUnit
|
||||
: (pragmaDirective | importDirective | structDefinition | enumDefinition | contractDefinition)* EOF ;
|
||||
|
||||
pragmaDirective
|
||||
: 'pragma' pragmaName pragmaValue ';' ;
|
||||
|
||||
pragmaName
|
||||
: identifier ;
|
||||
|
||||
pragmaValue
|
||||
: version | expression ;
|
||||
|
||||
version
|
||||
: versionConstraint versionConstraint? ;
|
||||
|
||||
versionConstraint
|
||||
: versionOperator? VersionLiteral ;
|
||||
|
||||
versionOperator
|
||||
: '^' | '~' | '>=' | '>' | '<' | '<=' | '=' ;
|
||||
|
||||
importDirective
|
||||
: 'import' StringLiteralFragment ('as' identifier)? ';'
|
||||
| 'import' ('*' | identifier) ('as' identifier)? 'from' StringLiteralFragment ';'
|
||||
| 'import' '{' importDeclaration ( ',' importDeclaration )* '}' 'from' StringLiteralFragment ';' ;
|
||||
|
||||
importDeclaration
|
||||
: identifier ('as' identifier)? ;
|
||||
|
||||
contractDefinition
|
||||
: 'abstract'? ( 'contract' | 'interface' | 'library' ) identifier
|
||||
( 'is' inheritanceSpecifier (',' inheritanceSpecifier )* )?
|
||||
'{' contractPart* '}' ;
|
||||
|
||||
inheritanceSpecifier
|
||||
: userDefinedTypeName ( '(' expressionList? ')' )? ;
|
||||
|
||||
contractPart
|
||||
: stateVariableDeclaration
|
||||
| usingForDeclaration
|
||||
| structDefinition
|
||||
| modifierDefinition
|
||||
| functionDefinition
|
||||
| eventDefinition
|
||||
| enumDefinition ;
|
||||
|
||||
stateVariableDeclaration
|
||||
: typeName
|
||||
( PublicKeyword | InternalKeyword | PrivateKeyword | ConstantKeyword | ImmutableKeyword | overrideSpecifier )*
|
||||
identifier ('=' expression)? ';' ;
|
||||
|
||||
overrideSpecifier : 'override' ( '(' userDefinedTypeName (',' userDefinedTypeName)* ')' )? ;
|
||||
|
||||
usingForDeclaration
|
||||
: 'using' identifier 'for' ('*' | typeName) ';' ;
|
||||
|
||||
structDefinition
|
||||
: 'struct' identifier
|
||||
'{' ( variableDeclaration ';' (variableDeclaration ';')* )? '}' ;
|
||||
|
||||
modifierDefinition
|
||||
: 'modifier' identifier parameterList? ( VirtualKeyword | overrideSpecifier )* block ;
|
||||
|
||||
functionDefinition
|
||||
: functionDescriptor parameterList modifierList returnParameters? ( ';' | block ) ;
|
||||
|
||||
functionDescriptor
|
||||
: 'function' ( identifier | ReceiveKeyword | FallbackKeyword )?
|
||||
| ConstructorKeyword
|
||||
| FallbackKeyword
|
||||
| ReceiveKeyword ;
|
||||
|
||||
returnParameters
|
||||
: 'returns' parameterList ;
|
||||
|
||||
modifierList
|
||||
: ( modifierInvocation | stateMutability | ExternalKeyword
|
||||
| PublicKeyword | InternalKeyword | PrivateKeyword | VirtualKeyword | overrideSpecifier )* ;
|
||||
|
||||
modifierInvocation
|
||||
: identifier ( '(' expressionList? ')' )? ;
|
||||
|
||||
eventDefinition
|
||||
: 'event' identifier eventParameterList AnonymousKeyword? ';' ;
|
||||
|
||||
enumDefinition
|
||||
: 'enum' identifier '{' enumValue? (',' enumValue)* '}' ;
|
||||
|
||||
enumValue
|
||||
: identifier ;
|
||||
|
||||
parameterList
|
||||
: '(' ( parameter (',' parameter)* )? ')' ;
|
||||
|
||||
parameter
|
||||
: typeName storageLocation? identifier? ;
|
||||
|
||||
eventParameterList
|
||||
: '(' ( eventParameter (',' eventParameter)* )? ')' ;
|
||||
|
||||
eventParameter
|
||||
: typeName IndexedKeyword? identifier? ;
|
||||
|
||||
variableDeclaration
|
||||
: typeName storageLocation? identifier ;
|
||||
|
||||
typeName
|
||||
: elementaryTypeName
|
||||
| userDefinedTypeName
|
||||
| mapping
|
||||
| typeName '[' expression? ']'
|
||||
| functionTypeName ;
|
||||
|
||||
userDefinedTypeName
|
||||
: identifier ( '.' identifier )* ;
|
||||
|
||||
mapping
|
||||
: 'mapping' '(' (elementaryTypeName | userDefinedTypeName) '=>' typeName ')' ;
|
||||
|
||||
functionTypeName
|
||||
: 'function' parameterList modifierList returnParameters? ;
|
||||
|
||||
storageLocation
|
||||
: 'memory' | 'storage' | 'calldata';
|
||||
|
||||
stateMutability
|
||||
: PureKeyword | ConstantKeyword | ViewKeyword | PayableKeyword ;
|
||||
|
||||
block
|
||||
: '{' statement* '}' ;
|
||||
|
||||
statement
|
||||
: ifStatement
|
||||
| tryStatement
|
||||
| whileStatement
|
||||
| forStatement
|
||||
| block
|
||||
| inlineAssemblyStatement
|
||||
| doWhileStatement
|
||||
| continueStatement
|
||||
| breakStatement
|
||||
| returnStatement
|
||||
| throwStatement
|
||||
| emitStatement
|
||||
| simpleStatement ;
|
||||
|
||||
expressionStatement
|
||||
: expression ';' ;
|
||||
|
||||
ifStatement
|
||||
: 'if' '(' expression ')' statement ( 'else' statement )? ;
|
||||
|
||||
tryStatement : 'try' expression returnParameters? block catchClause+ ;
|
||||
|
||||
// In reality catch clauses still are not processed as below
|
||||
// the identifier can only be a set string: "Error". But plans
|
||||
// of the Solidity team include possible expansion so we'll
|
||||
// leave this as is, befitting with the Solidity docs.
|
||||
catchClause : 'catch' ( identifier? parameterList )? block ;
|
||||
|
||||
whileStatement
|
||||
: 'while' '(' expression ')' statement ;
|
||||
|
||||
forStatement
|
||||
: 'for' '(' ( simpleStatement | ';' ) ( expressionStatement | ';' ) expression? ')' statement ;
|
||||
|
||||
simpleStatement
|
||||
: ( variableDeclarationStatement | expressionStatement ) ;
|
||||
|
||||
inlineAssemblyStatement
|
||||
: 'assembly' StringLiteralFragment? assemblyBlock ;
|
||||
|
||||
doWhileStatement
|
||||
: 'do' statement 'while' '(' expression ')' ';' ;
|
||||
|
||||
continueStatement
|
||||
: 'continue' ';' ;
|
||||
|
||||
breakStatement
|
||||
: 'break' ';' ;
|
||||
|
||||
returnStatement
|
||||
: 'return' expression? ';' ;
|
||||
|
||||
// throw is no longer supported by latest Solidity.
|
||||
throwStatement
|
||||
: 'throw' ';' ;
|
||||
|
||||
emitStatement
|
||||
: 'emit' functionCall ';' ;
|
||||
|
||||
// 'var' is no longer supported by latest Solidity.
|
||||
variableDeclarationStatement
|
||||
: ( 'var' identifierList | variableDeclaration | '(' variableDeclarationList ')' ) ( '=' expression )? ';';
|
||||
|
||||
variableDeclarationList
|
||||
: variableDeclaration? (',' variableDeclaration? )* ;
|
||||
|
||||
identifierList
|
||||
: '(' ( identifier? ',' )* identifier? ')' ;
|
||||
|
||||
elementaryTypeName
|
||||
: 'address' PayableKeyword? | 'bool' | 'string' | 'var' | Int | Uint | 'byte' | Byte | Fixed | Ufixed ;
|
||||
|
||||
Int
|
||||
: 'int' | 'int8' | 'int16' | 'int24' | 'int32' | 'int40' | 'int48' | 'int56' | 'int64' | 'int72' | 'int80' | 'int88' | 'int96' | 'int104' | 'int112' | 'int120' | 'int128' | 'int136' | 'int144' | 'int152' | 'int160' | 'int168' | 'int176' | 'int184' | 'int192' | 'int200' | 'int208' | 'int216' | 'int224' | 'int232' | 'int240' | 'int248' | 'int256' ;
|
||||
|
||||
Uint
|
||||
: 'uint' | 'uint8' | 'uint16' | 'uint24' | 'uint32' | 'uint40' | 'uint48' | 'uint56' | 'uint64' | 'uint72' | 'uint80' | 'uint88' | 'uint96' | 'uint104' | 'uint112' | 'uint120' | 'uint128' | 'uint136' | 'uint144' | 'uint152' | 'uint160' | 'uint168' | 'uint176' | 'uint184' | 'uint192' | 'uint200' | 'uint208' | 'uint216' | 'uint224' | 'uint232' | 'uint240' | 'uint248' | 'uint256' ;
|
||||
|
||||
Byte
|
||||
: 'bytes' | 'bytes1' | 'bytes2' | 'bytes3' | 'bytes4' | 'bytes5' | 'bytes6' | 'bytes7' | 'bytes8' | 'bytes9' | 'bytes10' | 'bytes11' | 'bytes12' | 'bytes13' | 'bytes14' | 'bytes15' | 'bytes16' | 'bytes17' | 'bytes18' | 'bytes19' | 'bytes20' | 'bytes21' | 'bytes22' | 'bytes23' | 'bytes24' | 'bytes25' | 'bytes26' | 'bytes27' | 'bytes28' | 'bytes29' | 'bytes30' | 'bytes31' | 'bytes32' ;
|
||||
|
||||
Fixed
|
||||
: 'fixed' | ( 'fixed' [0-9]+ 'x' [0-9]+ ) ;
|
||||
|
||||
Ufixed
|
||||
: 'ufixed' | ( 'ufixed' [0-9]+ 'x' [0-9]+ ) ;
|
||||
|
||||
expression
|
||||
: expression ('++' | '--')
|
||||
| 'new' typeName
|
||||
| expression '[' expression? ']'
|
||||
| expression '[' expression? ':' expression? ']'
|
||||
| expression '.' identifier
|
||||
| expression '{' nameValueList '}'
|
||||
| expression '(' functionCallArguments ')'
|
||||
| PayableKeyword '(' expression ')'
|
||||
| '(' expression ')'
|
||||
| ('++' | '--') expression
|
||||
| ('+' | '-') expression
|
||||
| ('after' | 'delete') expression
|
||||
| '!' expression
|
||||
| '~' expression
|
||||
| expression '**' expression
|
||||
| expression ('*' | '/' | '%') expression
|
||||
| expression ('+' | '-') expression
|
||||
| expression ('<<' | '>>') expression
|
||||
| expression '&' expression
|
||||
| expression '^' expression
|
||||
| expression '|' expression
|
||||
| expression ('<' | '>' | '<=' | '>=') expression
|
||||
| expression ('==' | '!=') expression
|
||||
| expression '&&' expression
|
||||
| expression '||' expression
|
||||
| expression '?' expression ':' expression
|
||||
| expression ('=' | '|=' | '^=' | '&=' | '<<=' | '>>=' | '+=' | '-=' | '*=' | '/=' | '%=') expression
|
||||
| primaryExpression ;
|
||||
|
||||
primaryExpression
|
||||
: BooleanLiteral
|
||||
| numberLiteral
|
||||
| hexLiteral
|
||||
| stringLiteral
|
||||
| identifier ('[' ']')?
|
||||
| TypeKeyword
|
||||
| tupleExpression
|
||||
| typeNameExpression ('[' ']')? ;
|
||||
|
||||
expressionList
|
||||
: expression (',' expression)* ;
|
||||
|
||||
nameValueList
|
||||
: nameValue (',' nameValue)* ','? ;
|
||||
|
||||
nameValue
|
||||
: identifier ':' expression ;
|
||||
|
||||
functionCallArguments
|
||||
: '{' nameValueList? '}'
|
||||
| expressionList? ;
|
||||
|
||||
functionCall
|
||||
: expression '(' functionCallArguments ')' ;
|
||||
|
||||
tupleExpression
|
||||
: '(' ( expression? ( ',' expression? )* ) ')'
|
||||
| '[' ( expression ( ',' expression )* )? ']' ;
|
||||
|
||||
typeNameExpression
|
||||
: elementaryTypeName
|
||||
| userDefinedTypeName ;
|
||||
|
||||
assemblyItem
|
||||
: identifier
|
||||
| assemblyBlock
|
||||
| assemblyExpression
|
||||
| assemblyLocalDefinition
|
||||
| assemblyAssignment
|
||||
| assemblyStackAssignment
|
||||
| labelDefinition
|
||||
| assemblySwitch
|
||||
| assemblyFunctionDefinition
|
||||
| assemblyFor
|
||||
| assemblyIf
|
||||
| BreakKeyword
|
||||
| ContinueKeyword
|
||||
| LeaveKeyword
|
||||
| subAssembly
|
||||
| numberLiteral
|
||||
| stringLiteral
|
||||
| hexLiteral ;
|
||||
|
||||
assemblyBlock
|
||||
: '{' assemblyItem* '}' ;
|
||||
|
||||
assemblyExpression
|
||||
: assemblyCall | assemblyLiteral ;
|
||||
|
||||
assemblyCall
|
||||
: ( 'return' | 'address' | 'byte' | identifier ) ( '(' assemblyExpression? ( ',' assemblyExpression )* ')' )? ;
|
||||
|
||||
assemblyLocalDefinition
|
||||
: 'let' assemblyIdentifierList ( ':=' assemblyExpression )? ;
|
||||
|
||||
assemblyAssignment
|
||||
: assemblyIdentifierList ':=' assemblyExpression ;
|
||||
|
||||
assemblyIdentifierList
|
||||
: identifier ( ',' identifier )* ;
|
||||
|
||||
assemblyStackAssignment
|
||||
: '=:' identifier ;
|
||||
|
||||
labelDefinition
|
||||
: identifier ':' ;
|
||||
|
||||
assemblySwitch
|
||||
: 'switch' assemblyExpression assemblyCase* ;
|
||||
|
||||
assemblyCase
|
||||
: 'case' assemblyLiteral assemblyType? assemblyBlock
|
||||
| 'default' assemblyBlock ;
|
||||
|
||||
assemblyFunctionDefinition
|
||||
: 'function' identifier '(' assemblyTypedVariableList? ')'
|
||||
assemblyFunctionReturns? assemblyBlock ;
|
||||
|
||||
assemblyFunctionReturns
|
||||
: ( '-' '>' assemblyTypedVariableList ) ;
|
||||
|
||||
assemblyFor
|
||||
: 'for' assemblyBlock assemblyExpression assemblyBlock assemblyBlock ;
|
||||
|
||||
assemblyIf
|
||||
: 'if' assemblyExpression assemblyBlock ;
|
||||
|
||||
assemblyLiteral
|
||||
: ( stringLiteral | DecimalNumber | HexNumber | hexLiteral | BooleanLiteral ) assemblyType? ;
|
||||
|
||||
assemblyTypedVariableList
|
||||
: identifier assemblyType? ( ',' assemblyTypedVariableList )? ;
|
||||
|
||||
assemblyType
|
||||
: ':' identifier ;
|
||||
|
||||
subAssembly
|
||||
: 'assembly' identifier assemblyBlock ;
|
||||
|
||||
numberLiteral
|
||||
: (DecimalNumber | HexNumber) NumberUnit? ;
|
||||
|
||||
identifier
|
||||
: ('from' | 'calldata' | 'address' | Identifier) ;
|
||||
|
||||
BooleanLiteral
|
||||
: 'true' | 'false' ;
|
||||
|
||||
DecimalNumber
|
||||
: ( DecimalDigits | (DecimalDigits? '.' DecimalDigits) ) ( [eE] '-'? DecimalDigits )? ;
|
||||
|
||||
fragment
|
||||
DecimalDigits
|
||||
: [0-9] ( '_'? [0-9] )* ;
|
||||
|
||||
HexNumber
|
||||
: '0' [xX] HexDigits ;
|
||||
|
||||
fragment
|
||||
HexDigits
|
||||
: HexCharacter ( '_'? HexCharacter )* ;
|
||||
|
||||
NumberUnit
|
||||
: 'wei' | 'szabo' | 'finney' | 'ether'
|
||||
| 'seconds' | 'minutes' | 'hours' | 'days' | 'weeks' | 'years' ;
|
||||
|
||||
HexLiteralFragment
|
||||
: 'hex' (('"' HexDigits? '"') | ('\'' HexDigits? '\'')) ;
|
||||
|
||||
hexLiteral : HexLiteralFragment+ ;
|
||||
|
||||
fragment
|
||||
HexPair
|
||||
: HexCharacter HexCharacter ;
|
||||
|
||||
fragment
|
||||
HexCharacter
|
||||
: [0-9A-Fa-f] ;
|
||||
|
||||
ReservedKeyword
|
||||
: 'after'
|
||||
| 'case'
|
||||
| 'default'
|
||||
| 'final'
|
||||
| 'in'
|
||||
| 'inline'
|
||||
| 'let'
|
||||
| 'match'
|
||||
| 'null'
|
||||
| 'of'
|
||||
| 'relocatable'
|
||||
| 'static'
|
||||
| 'switch'
|
||||
| 'typeof' ;
|
||||
|
||||
AnonymousKeyword : 'anonymous' ;
|
||||
BreakKeyword : 'break' ;
|
||||
ConstantKeyword : 'constant' ;
|
||||
ImmutableKeyword : 'immutable' ;
|
||||
ContinueKeyword : 'continue' ;
|
||||
LeaveKeyword : 'leave' ;
|
||||
ExternalKeyword : 'external' ;
|
||||
IndexedKeyword : 'indexed' ;
|
||||
InternalKeyword : 'internal' ;
|
||||
PayableKeyword : 'payable' ;
|
||||
PrivateKeyword : 'private' ;
|
||||
PublicKeyword : 'public' ;
|
||||
VirtualKeyword : 'virtual' ;
|
||||
PureKeyword : 'pure' ;
|
||||
TypeKeyword : 'type' ;
|
||||
ViewKeyword : 'view' ;
|
||||
|
||||
ConstructorKeyword : 'constructor' ;
|
||||
FallbackKeyword : 'fallback' ;
|
||||
ReceiveKeyword : 'receive' ;
|
||||
|
||||
Identifier
|
||||
: IdentifierStart IdentifierPart* ;
|
||||
|
||||
fragment
|
||||
IdentifierStart
|
||||
: [a-zA-Z$_] ;
|
||||
|
||||
fragment
|
||||
IdentifierPart
|
||||
: [a-zA-Z0-9$_] ;
|
||||
|
||||
stringLiteral
|
||||
: StringLiteralFragment+ ;
|
||||
|
||||
StringLiteralFragment
|
||||
: '"' DoubleQuotedStringCharacter* '"'
|
||||
| '\'' SingleQuotedStringCharacter* '\'' ;
|
||||
|
||||
fragment
|
||||
DoubleQuotedStringCharacter
|
||||
: ~["\r\n\\] | ('\\' .) ;
|
||||
|
||||
fragment
|
||||
SingleQuotedStringCharacter
|
||||
: ~['\r\n\\] | ('\\' .) ;
|
||||
|
||||
VersionLiteral
|
||||
: [0-9]+ '.' [0-9]+ ('.' [0-9]+)? ;
|
||||
|
||||
WS
|
||||
: [ \t\r\n\u000C]+ -> skip ;
|
||||
|
||||
COMMENT
|
||||
: '/*' .*? '*/' -> channel(HIDDEN) ;
|
||||
|
||||
LINE_COMMENT
|
||||
: '//' ~[\r\n]* -> channel(HIDDEN) ;
|
@ -234,7 +234,6 @@ Given the contract:
|
||||
|
||||
pragma solidity >=0.4.16 <0.8.0;
|
||||
|
||||
|
||||
contract Foo {
|
||||
function bar(bytes3[2] memory) public pure {}
|
||||
function baz(uint32 x, bool y) public pure returns (bool r) { r = x > 32 || y; }
|
||||
@ -583,12 +582,11 @@ As an example, the code
|
||||
pragma solidity >=0.4.19 <0.8.0;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
|
||||
contract Test {
|
||||
struct S { uint a; uint[] b; T[] c; }
|
||||
struct T { uint x; uint y; }
|
||||
function f(S memory s, T memory t, uint a) public {}
|
||||
function g() public returns (S memory s, T memory t, uint a) {}
|
||||
function f(S memory, T memory, uint) public pure {}
|
||||
function g() public pure returns (S memory, T memory, uint) {}
|
||||
}
|
||||
|
||||
would result in the JSON:
|
||||
|
@ -41,7 +41,7 @@ without a compiler change.
|
||||
|
||||
.. code::
|
||||
|
||||
pragma solidity >=0.4.0 <0.8.0;
|
||||
pragma solidity >=0.4.16 <0.8.0;
|
||||
|
||||
library GetCode {
|
||||
function at(address _addr) public view returns (bytes memory o_code) {
|
||||
@ -136,7 +136,7 @@ Local Solidity variables are available for assignments, for example:
|
||||
|
||||
.. code::
|
||||
|
||||
pragma solidity >=0.4.11 <0.8.0;
|
||||
pragma solidity >=0.4.16 <0.8.0;
|
||||
|
||||
contract C {
|
||||
uint b;
|
||||
|
@ -10,6 +10,14 @@
|
||||
"yulOptimizer": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "privateCanBeOverridden",
|
||||
"summary": "Private methods can be overridden by inheriting contracts.",
|
||||
"description": "While private methods of base contracts are not visible and cannot be called directly from the derived contract, it is still possible to declare a function of the same name and type and thus change the behaviour of the base contract's function.",
|
||||
"introduced": "0.3.0",
|
||||
"fixed": "0.5.17",
|
||||
"severity": "low"
|
||||
},
|
||||
{
|
||||
"name": "YulOptimizerRedundantAssignmentBreakContinue0.5",
|
||||
"summary": "The Yul optimizer can remove essential assignments to variables declared inside for loops when Yul's continue or break statement is used. You are unlikely to be affected if you do not use inline assembly with for loops and continue and break statements.",
|
||||
|
@ -211,6 +211,7 @@
|
||||
},
|
||||
"0.3.0": {
|
||||
"bugs": [
|
||||
"privateCanBeOverridden",
|
||||
"IncorrectEventSignatureInLibraries_0.4.x",
|
||||
"ExpExponentCleanup",
|
||||
"NestedArrayFunctionCallDecoder",
|
||||
@ -232,6 +233,7 @@
|
||||
},
|
||||
"0.3.1": {
|
||||
"bugs": [
|
||||
"privateCanBeOverridden",
|
||||
"IncorrectEventSignatureInLibraries_0.4.x",
|
||||
"ExpExponentCleanup",
|
||||
"NestedArrayFunctionCallDecoder",
|
||||
@ -252,6 +254,7 @@
|
||||
},
|
||||
"0.3.2": {
|
||||
"bugs": [
|
||||
"privateCanBeOverridden",
|
||||
"IncorrectEventSignatureInLibraries_0.4.x",
|
||||
"ExpExponentCleanup",
|
||||
"NestedArrayFunctionCallDecoder",
|
||||
@ -272,6 +275,7 @@
|
||||
},
|
||||
"0.3.3": {
|
||||
"bugs": [
|
||||
"privateCanBeOverridden",
|
||||
"IncorrectEventSignatureInLibraries_0.4.x",
|
||||
"ExpExponentCleanup",
|
||||
"NestedArrayFunctionCallDecoder",
|
||||
@ -291,6 +295,7 @@
|
||||
},
|
||||
"0.3.4": {
|
||||
"bugs": [
|
||||
"privateCanBeOverridden",
|
||||
"IncorrectEventSignatureInLibraries_0.4.x",
|
||||
"ExpExponentCleanup",
|
||||
"NestedArrayFunctionCallDecoder",
|
||||
@ -310,6 +315,7 @@
|
||||
},
|
||||
"0.3.5": {
|
||||
"bugs": [
|
||||
"privateCanBeOverridden",
|
||||
"IncorrectEventSignatureInLibraries_0.4.x",
|
||||
"ExpExponentCleanup",
|
||||
"NestedArrayFunctionCallDecoder",
|
||||
@ -329,6 +335,7 @@
|
||||
},
|
||||
"0.3.6": {
|
||||
"bugs": [
|
||||
"privateCanBeOverridden",
|
||||
"IncorrectEventSignatureInLibraries_0.4.x",
|
||||
"ExpExponentCleanup",
|
||||
"NestedArrayFunctionCallDecoder",
|
||||
@ -346,6 +353,7 @@
|
||||
},
|
||||
"0.4.0": {
|
||||
"bugs": [
|
||||
"privateCanBeOverridden",
|
||||
"IncorrectEventSignatureInLibraries_0.4.x",
|
||||
"ExpExponentCleanup",
|
||||
"NestedArrayFunctionCallDecoder",
|
||||
@ -363,6 +371,7 @@
|
||||
},
|
||||
"0.4.1": {
|
||||
"bugs": [
|
||||
"privateCanBeOverridden",
|
||||
"IncorrectEventSignatureInLibraries_0.4.x",
|
||||
"ExpExponentCleanup",
|
||||
"NestedArrayFunctionCallDecoder",
|
||||
@ -380,6 +389,7 @@
|
||||
},
|
||||
"0.4.10": {
|
||||
"bugs": [
|
||||
"privateCanBeOverridden",
|
||||
"SignedArrayStorageCopy",
|
||||
"UninitializedFunctionPointerInConstructor_0.4.x",
|
||||
"IncorrectEventSignatureInLibraries_0.4.x",
|
||||
@ -395,6 +405,7 @@
|
||||
},
|
||||
"0.4.11": {
|
||||
"bugs": [
|
||||
"privateCanBeOverridden",
|
||||
"SignedArrayStorageCopy",
|
||||
"UninitializedFunctionPointerInConstructor_0.4.x",
|
||||
"IncorrectEventSignatureInLibraries_0.4.x",
|
||||
@ -409,6 +420,7 @@
|
||||
},
|
||||
"0.4.12": {
|
||||
"bugs": [
|
||||
"privateCanBeOverridden",
|
||||
"SignedArrayStorageCopy",
|
||||
"UninitializedFunctionPointerInConstructor_0.4.x",
|
||||
"IncorrectEventSignatureInLibraries_0.4.x",
|
||||
@ -422,6 +434,7 @@
|
||||
},
|
||||
"0.4.13": {
|
||||
"bugs": [
|
||||
"privateCanBeOverridden",
|
||||
"SignedArrayStorageCopy",
|
||||
"UninitializedFunctionPointerInConstructor_0.4.x",
|
||||
"IncorrectEventSignatureInLibraries_0.4.x",
|
||||
@ -435,6 +448,7 @@
|
||||
},
|
||||
"0.4.14": {
|
||||
"bugs": [
|
||||
"privateCanBeOverridden",
|
||||
"SignedArrayStorageCopy",
|
||||
"UninitializedFunctionPointerInConstructor_0.4.x",
|
||||
"IncorrectEventSignatureInLibraries_0.4.x",
|
||||
@ -447,6 +461,7 @@
|
||||
},
|
||||
"0.4.15": {
|
||||
"bugs": [
|
||||
"privateCanBeOverridden",
|
||||
"SignedArrayStorageCopy",
|
||||
"UninitializedFunctionPointerInConstructor_0.4.x",
|
||||
"IncorrectEventSignatureInLibraries_0.4.x",
|
||||
@ -458,6 +473,7 @@
|
||||
},
|
||||
"0.4.16": {
|
||||
"bugs": [
|
||||
"privateCanBeOverridden",
|
||||
"SignedArrayStorageCopy",
|
||||
"ABIEncoderV2StorageArrayWithMultiSlotElement",
|
||||
"DynamicConstructorArgumentsClippedABIV2",
|
||||
@ -471,6 +487,7 @@
|
||||
},
|
||||
"0.4.17": {
|
||||
"bugs": [
|
||||
"privateCanBeOverridden",
|
||||
"SignedArrayStorageCopy",
|
||||
"ABIEncoderV2StorageArrayWithMultiSlotElement",
|
||||
"DynamicConstructorArgumentsClippedABIV2",
|
||||
@ -485,6 +502,7 @@
|
||||
},
|
||||
"0.4.18": {
|
||||
"bugs": [
|
||||
"privateCanBeOverridden",
|
||||
"SignedArrayStorageCopy",
|
||||
"ABIEncoderV2StorageArrayWithMultiSlotElement",
|
||||
"DynamicConstructorArgumentsClippedABIV2",
|
||||
@ -498,6 +516,7 @@
|
||||
},
|
||||
"0.4.19": {
|
||||
"bugs": [
|
||||
"privateCanBeOverridden",
|
||||
"SignedArrayStorageCopy",
|
||||
"ABIEncoderV2StorageArrayWithMultiSlotElement",
|
||||
"DynamicConstructorArgumentsClippedABIV2",
|
||||
@ -512,6 +531,7 @@
|
||||
},
|
||||
"0.4.2": {
|
||||
"bugs": [
|
||||
"privateCanBeOverridden",
|
||||
"IncorrectEventSignatureInLibraries_0.4.x",
|
||||
"ExpExponentCleanup",
|
||||
"NestedArrayFunctionCallDecoder",
|
||||
@ -528,6 +548,7 @@
|
||||
},
|
||||
"0.4.20": {
|
||||
"bugs": [
|
||||
"privateCanBeOverridden",
|
||||
"SignedArrayStorageCopy",
|
||||
"ABIEncoderV2StorageArrayWithMultiSlotElement",
|
||||
"DynamicConstructorArgumentsClippedABIV2",
|
||||
@ -542,6 +563,7 @@
|
||||
},
|
||||
"0.4.21": {
|
||||
"bugs": [
|
||||
"privateCanBeOverridden",
|
||||
"SignedArrayStorageCopy",
|
||||
"ABIEncoderV2StorageArrayWithMultiSlotElement",
|
||||
"DynamicConstructorArgumentsClippedABIV2",
|
||||
@ -556,6 +578,7 @@
|
||||
},
|
||||
"0.4.22": {
|
||||
"bugs": [
|
||||
"privateCanBeOverridden",
|
||||
"SignedArrayStorageCopy",
|
||||
"ABIEncoderV2StorageArrayWithMultiSlotElement",
|
||||
"DynamicConstructorArgumentsClippedABIV2",
|
||||
@ -570,6 +593,7 @@
|
||||
},
|
||||
"0.4.23": {
|
||||
"bugs": [
|
||||
"privateCanBeOverridden",
|
||||
"SignedArrayStorageCopy",
|
||||
"ABIEncoderV2StorageArrayWithMultiSlotElement",
|
||||
"DynamicConstructorArgumentsClippedABIV2",
|
||||
@ -583,6 +607,7 @@
|
||||
},
|
||||
"0.4.24": {
|
||||
"bugs": [
|
||||
"privateCanBeOverridden",
|
||||
"SignedArrayStorageCopy",
|
||||
"ABIEncoderV2StorageArrayWithMultiSlotElement",
|
||||
"DynamicConstructorArgumentsClippedABIV2",
|
||||
@ -596,6 +621,7 @@
|
||||
},
|
||||
"0.4.25": {
|
||||
"bugs": [
|
||||
"privateCanBeOverridden",
|
||||
"SignedArrayStorageCopy",
|
||||
"ABIEncoderV2StorageArrayWithMultiSlotElement",
|
||||
"DynamicConstructorArgumentsClippedABIV2",
|
||||
@ -607,6 +633,7 @@
|
||||
},
|
||||
"0.4.26": {
|
||||
"bugs": [
|
||||
"privateCanBeOverridden",
|
||||
"SignedArrayStorageCopy",
|
||||
"ABIEncoderV2StorageArrayWithMultiSlotElement",
|
||||
"DynamicConstructorArgumentsClippedABIV2"
|
||||
@ -615,6 +642,7 @@
|
||||
},
|
||||
"0.4.3": {
|
||||
"bugs": [
|
||||
"privateCanBeOverridden",
|
||||
"IncorrectEventSignatureInLibraries_0.4.x",
|
||||
"ExpExponentCleanup",
|
||||
"NestedArrayFunctionCallDecoder",
|
||||
@ -630,6 +658,7 @@
|
||||
},
|
||||
"0.4.4": {
|
||||
"bugs": [
|
||||
"privateCanBeOverridden",
|
||||
"IncorrectEventSignatureInLibraries_0.4.x",
|
||||
"ExpExponentCleanup",
|
||||
"NestedArrayFunctionCallDecoder",
|
||||
@ -644,6 +673,7 @@
|
||||
},
|
||||
"0.4.5": {
|
||||
"bugs": [
|
||||
"privateCanBeOverridden",
|
||||
"UninitializedFunctionPointerInConstructor_0.4.x",
|
||||
"IncorrectEventSignatureInLibraries_0.4.x",
|
||||
"ExpExponentCleanup",
|
||||
@ -660,6 +690,7 @@
|
||||
},
|
||||
"0.4.6": {
|
||||
"bugs": [
|
||||
"privateCanBeOverridden",
|
||||
"UninitializedFunctionPointerInConstructor_0.4.x",
|
||||
"IncorrectEventSignatureInLibraries_0.4.x",
|
||||
"ExpExponentCleanup",
|
||||
@ -675,6 +706,7 @@
|
||||
},
|
||||
"0.4.7": {
|
||||
"bugs": [
|
||||
"privateCanBeOverridden",
|
||||
"SignedArrayStorageCopy",
|
||||
"UninitializedFunctionPointerInConstructor_0.4.x",
|
||||
"IncorrectEventSignatureInLibraries_0.4.x",
|
||||
@ -690,6 +722,7 @@
|
||||
},
|
||||
"0.4.8": {
|
||||
"bugs": [
|
||||
"privateCanBeOverridden",
|
||||
"SignedArrayStorageCopy",
|
||||
"UninitializedFunctionPointerInConstructor_0.4.x",
|
||||
"IncorrectEventSignatureInLibraries_0.4.x",
|
||||
@ -705,6 +738,7 @@
|
||||
},
|
||||
"0.4.9": {
|
||||
"bugs": [
|
||||
"privateCanBeOverridden",
|
||||
"SignedArrayStorageCopy",
|
||||
"UninitializedFunctionPointerInConstructor_0.4.x",
|
||||
"IncorrectEventSignatureInLibraries_0.4.x",
|
||||
@ -720,6 +754,7 @@
|
||||
},
|
||||
"0.5.0": {
|
||||
"bugs": [
|
||||
"privateCanBeOverridden",
|
||||
"SignedArrayStorageCopy",
|
||||
"ABIEncoderV2StorageArrayWithMultiSlotElement",
|
||||
"DynamicConstructorArgumentsClippedABIV2",
|
||||
@ -731,6 +766,7 @@
|
||||
},
|
||||
"0.5.1": {
|
||||
"bugs": [
|
||||
"privateCanBeOverridden",
|
||||
"SignedArrayStorageCopy",
|
||||
"ABIEncoderV2StorageArrayWithMultiSlotElement",
|
||||
"DynamicConstructorArgumentsClippedABIV2",
|
||||
@ -742,6 +778,7 @@
|
||||
},
|
||||
"0.5.10": {
|
||||
"bugs": [
|
||||
"privateCanBeOverridden",
|
||||
"YulOptimizerRedundantAssignmentBreakContinue0.5",
|
||||
"ABIEncoderV2CalldataStructsWithStaticallySizedAndDynamicallyEncodedMembers"
|
||||
],
|
||||
@ -749,24 +786,28 @@
|
||||
},
|
||||
"0.5.11": {
|
||||
"bugs": [
|
||||
"privateCanBeOverridden",
|
||||
"YulOptimizerRedundantAssignmentBreakContinue0.5"
|
||||
],
|
||||
"released": "2019-08-12"
|
||||
},
|
||||
"0.5.12": {
|
||||
"bugs": [
|
||||
"privateCanBeOverridden",
|
||||
"YulOptimizerRedundantAssignmentBreakContinue0.5"
|
||||
],
|
||||
"released": "2019-10-01"
|
||||
},
|
||||
"0.5.13": {
|
||||
"bugs": [
|
||||
"privateCanBeOverridden",
|
||||
"YulOptimizerRedundantAssignmentBreakContinue0.5"
|
||||
],
|
||||
"released": "2019-11-14"
|
||||
},
|
||||
"0.5.14": {
|
||||
"bugs": [
|
||||
"privateCanBeOverridden",
|
||||
"YulOptimizerRedundantAssignmentBreakContinue0.5",
|
||||
"ABIEncoderV2LoopYulOptimizer"
|
||||
],
|
||||
@ -774,16 +815,24 @@
|
||||
},
|
||||
"0.5.15": {
|
||||
"bugs": [
|
||||
"privateCanBeOverridden",
|
||||
"YulOptimizerRedundantAssignmentBreakContinue0.5"
|
||||
],
|
||||
"released": "2019-12-17"
|
||||
},
|
||||
"0.5.16": {
|
||||
"bugs": [],
|
||||
"bugs": [
|
||||
"privateCanBeOverridden"
|
||||
],
|
||||
"released": "2020-01-02"
|
||||
},
|
||||
"0.5.17": {
|
||||
"bugs": [],
|
||||
"released": "2020-03-17"
|
||||
},
|
||||
"0.5.2": {
|
||||
"bugs": [
|
||||
"privateCanBeOverridden",
|
||||
"SignedArrayStorageCopy",
|
||||
"ABIEncoderV2StorageArrayWithMultiSlotElement",
|
||||
"DynamicConstructorArgumentsClippedABIV2",
|
||||
@ -795,6 +844,7 @@
|
||||
},
|
||||
"0.5.3": {
|
||||
"bugs": [
|
||||
"privateCanBeOverridden",
|
||||
"SignedArrayStorageCopy",
|
||||
"ABIEncoderV2StorageArrayWithMultiSlotElement",
|
||||
"DynamicConstructorArgumentsClippedABIV2",
|
||||
@ -806,6 +856,7 @@
|
||||
},
|
||||
"0.5.4": {
|
||||
"bugs": [
|
||||
"privateCanBeOverridden",
|
||||
"SignedArrayStorageCopy",
|
||||
"ABIEncoderV2StorageArrayWithMultiSlotElement",
|
||||
"DynamicConstructorArgumentsClippedABIV2",
|
||||
@ -817,6 +868,7 @@
|
||||
},
|
||||
"0.5.5": {
|
||||
"bugs": [
|
||||
"privateCanBeOverridden",
|
||||
"SignedArrayStorageCopy",
|
||||
"ABIEncoderV2StorageArrayWithMultiSlotElement",
|
||||
"DynamicConstructorArgumentsClippedABIV2",
|
||||
@ -830,6 +882,7 @@
|
||||
},
|
||||
"0.5.6": {
|
||||
"bugs": [
|
||||
"privateCanBeOverridden",
|
||||
"ABIEncoderV2CalldataStructsWithStaticallySizedAndDynamicallyEncodedMembers",
|
||||
"SignedArrayStorageCopy",
|
||||
"ABIEncoderV2StorageArrayWithMultiSlotElement",
|
||||
@ -843,6 +896,7 @@
|
||||
},
|
||||
"0.5.7": {
|
||||
"bugs": [
|
||||
"privateCanBeOverridden",
|
||||
"ABIEncoderV2CalldataStructsWithStaticallySizedAndDynamicallyEncodedMembers",
|
||||
"SignedArrayStorageCopy",
|
||||
"ABIEncoderV2StorageArrayWithMultiSlotElement",
|
||||
@ -854,6 +908,7 @@
|
||||
},
|
||||
"0.5.8": {
|
||||
"bugs": [
|
||||
"privateCanBeOverridden",
|
||||
"YulOptimizerRedundantAssignmentBreakContinue0.5",
|
||||
"ABIEncoderV2CalldataStructsWithStaticallySizedAndDynamicallyEncodedMembers",
|
||||
"SignedArrayStorageCopy",
|
||||
@ -864,6 +919,7 @@
|
||||
},
|
||||
"0.5.9": {
|
||||
"bugs": [
|
||||
"privateCanBeOverridden",
|
||||
"YulOptimizerRedundantAssignmentBreakContinue0.5",
|
||||
"ABIEncoderV2CalldataStructsWithStaticallySizedAndDynamicallyEncodedMembers",
|
||||
"SignedArrayStorageCopy",
|
||||
|
@ -13,7 +13,7 @@ This can be done by using the ``abstract`` keyword as shown in the following exa
|
||||
defined as abstract, because the function ``utterance()`` was defined, but no implementation was
|
||||
provided (no implementation body ``{ }`` was given).::
|
||||
|
||||
pragma solidity >=0.4.0 <0.8.0;
|
||||
pragma solidity >=0.6.0 <0.8.0;
|
||||
|
||||
abstract contract Feline {
|
||||
function utterance() public virtual returns (bytes32);
|
||||
|
@ -335,7 +335,7 @@ operations as long as there is enough gas passed on to it.
|
||||
|
||||
::
|
||||
|
||||
pragma solidity >0.6.1 <0.8.0;
|
||||
pragma solidity >=0.6.2 <0.8.0;
|
||||
|
||||
contract Test {
|
||||
// This function is called for all messages sent to
|
||||
|
@ -154,7 +154,7 @@ A call to ``Final.destroy()`` will call ``Base2.destroy`` because we specify it
|
||||
explicitly in the final override, but this function will bypass
|
||||
``Base1.destroy``. The way around this is to use ``super``::
|
||||
|
||||
pragma solidity >=0.4.22 <0.8.0;
|
||||
pragma solidity >=0.6.0 <0.8.0;
|
||||
|
||||
contract owned {
|
||||
constructor() public { owner = msg.sender; }
|
||||
@ -204,7 +204,7 @@ use the ``override`` keyword in the function header as shown in this example:
|
||||
|
||||
::
|
||||
|
||||
pragma solidity >=0.5.0 <0.8.0;
|
||||
pragma solidity >=0.6.0 <0.8.0;
|
||||
|
||||
contract Base
|
||||
{
|
||||
@ -227,7 +227,7 @@ bases, it has to explicitly override it:
|
||||
|
||||
::
|
||||
|
||||
pragma solidity >=0.5.0 <0.8.0;
|
||||
pragma solidity >=0.6.0 <0.8.0;
|
||||
|
||||
contract Base1
|
||||
{
|
||||
@ -253,7 +253,7 @@ that already overrides all other functions.
|
||||
|
||||
::
|
||||
|
||||
pragma solidity >=0.5.0 <0.8.0;
|
||||
pragma solidity >=0.6.0 <0.8.0;
|
||||
|
||||
contract A { function f() public pure{} }
|
||||
contract B is A {}
|
||||
@ -293,7 +293,7 @@ of the variable:
|
||||
|
||||
::
|
||||
|
||||
pragma solidity >=0.5.0 <0.8.0;
|
||||
pragma solidity >=0.6.0 <0.8.0;
|
||||
|
||||
contract A
|
||||
{
|
||||
@ -324,7 +324,7 @@ and the ``override`` keyword must be used in the overriding modifier:
|
||||
|
||||
::
|
||||
|
||||
pragma solidity >=0.5.0 <0.8.0;
|
||||
pragma solidity >=0.6.0 <0.8.0;
|
||||
|
||||
contract Base
|
||||
{
|
||||
@ -342,7 +342,7 @@ explicitly:
|
||||
|
||||
::
|
||||
|
||||
pragma solidity >=0.5.0 <0.8.0;
|
||||
pragma solidity >=0.6.0 <0.8.0;
|
||||
|
||||
contract Base1
|
||||
{
|
||||
@ -498,7 +498,7 @@ One area where inheritance linearization is especially important and perhaps not
|
||||
|
||||
::
|
||||
|
||||
pragma solidity >=0.4.0 <0.8.0;
|
||||
pragma solidity >=0.4.22 <0.8.0;
|
||||
|
||||
contract Base1 {
|
||||
constructor() public {}
|
||||
|
@ -22,7 +22,7 @@ Interfaces are denoted by their own keyword:
|
||||
|
||||
::
|
||||
|
||||
pragma solidity >=0.5.0 <0.8.0;
|
||||
pragma solidity >=0.6.2 <0.8.0;
|
||||
|
||||
interface Token {
|
||||
enum TokenType { Fungible, NonFungible }
|
||||
@ -42,7 +42,7 @@ inheritance.
|
||||
|
||||
::
|
||||
|
||||
pragma solidity >0.6.1 <0.8.0;
|
||||
pragma solidity >=0.6.2 <0.8.0;
|
||||
|
||||
interface ParentA {
|
||||
function test() external returns (uint256);
|
||||
|
@ -47,12 +47,14 @@ more advanced example to implement a set).
|
||||
|
||||
::
|
||||
|
||||
pragma solidity >=0.4.22 <0.8.0;
|
||||
pragma solidity >=0.6.0 <0.8.0;
|
||||
|
||||
|
||||
// We define a new struct datatype that will be used to
|
||||
// hold its data in the calling contract.
|
||||
struct Data { mapping(uint => bool) flags; }
|
||||
struct Data {
|
||||
mapping(uint => bool) flags;
|
||||
}
|
||||
|
||||
library Set {
|
||||
// Note that the first parameter is of type "storage
|
||||
@ -123,7 +125,7 @@ custom types without the overhead of external function calls:
|
||||
|
||||
::
|
||||
|
||||
pragma solidity >=0.4.16 <0.8.0;
|
||||
pragma solidity >=0.6.0 <0.8.0;
|
||||
|
||||
struct bigint {
|
||||
uint[] limbs;
|
||||
@ -237,7 +239,7 @@ Its value can be obtained from Solidity using the ``.selector`` member as follow
|
||||
|
||||
::
|
||||
|
||||
pragma solidity >0.5.13 <0.8.0;
|
||||
pragma solidity >=0.5.14 <0.8.0;
|
||||
|
||||
library L {
|
||||
function f(uint256) external {}
|
||||
|
@ -29,7 +29,7 @@ may only be used inside a contract, not inside any of its functions.
|
||||
Let us rewrite the set example from the
|
||||
:ref:`libraries` in this way::
|
||||
|
||||
pragma solidity >=0.4.16 <0.8.0;
|
||||
pragma solidity >=0.6.0 <0.8.0;
|
||||
|
||||
|
||||
// This is the same code as before, just without comments
|
||||
|
@ -68,7 +68,7 @@ In the following example, ``D``, can call ``c.getData()`` to retrieve the value
|
||||
|
||||
::
|
||||
|
||||
pragma solidity >=0.4.0 <0.8.0;
|
||||
pragma solidity >=0.4.16 <0.8.0;
|
||||
|
||||
contract C {
|
||||
uint private data;
|
||||
@ -112,7 +112,7 @@ when they are declared.
|
||||
|
||||
::
|
||||
|
||||
pragma solidity >=0.4.0 <0.8.0;
|
||||
pragma solidity >=0.4.16 <0.8.0;
|
||||
|
||||
contract C {
|
||||
uint public data = 42;
|
||||
@ -151,7 +151,7 @@ to write a function, for example:
|
||||
|
||||
::
|
||||
|
||||
pragma solidity >=0.4.0 <0.8.0;
|
||||
pragma solidity >=0.4.16 <0.8.0;
|
||||
|
||||
contract arrayExample {
|
||||
// public state variable
|
||||
|
@ -41,7 +41,7 @@ Internal Function Calls
|
||||
Functions of the current contract can be called directly ("internally"), also recursively, as seen in
|
||||
this nonsensical example::
|
||||
|
||||
pragma solidity >=0.4.16 <0.8.0;
|
||||
pragma solidity >=0.4.22 <0.8.0;
|
||||
|
||||
contract C {
|
||||
function g(uint a) public pure returns (uint ret) { return a + f(); }
|
||||
@ -82,7 +82,7 @@ to the total balance of that contract:
|
||||
|
||||
::
|
||||
|
||||
pragma solidity >=0.4.0 <0.8.0;
|
||||
pragma solidity >=0.6.2 <0.8.0;
|
||||
|
||||
contract InfoFeed {
|
||||
function info() public payable returns (uint ret) { return 42; }
|
||||
@ -160,7 +160,7 @@ Those parameters will still be present on the stack, but they are inaccessible.
|
||||
|
||||
::
|
||||
|
||||
pragma solidity >=0.4.16 <0.8.0;
|
||||
pragma solidity >=0.4.22 <0.8.0;
|
||||
|
||||
contract C {
|
||||
// omitted name for parameter
|
||||
@ -183,7 +183,7 @@ is compiled so recursive creation-dependencies are not possible.
|
||||
|
||||
::
|
||||
|
||||
pragma solidity >=0.5.0 <0.8.0;
|
||||
pragma solidity >=0.6.2 <0.8.0;
|
||||
|
||||
contract D {
|
||||
uint public x;
|
||||
@ -238,7 +238,7 @@ which only need to be created if there is a dispute.
|
||||
|
||||
::
|
||||
|
||||
pragma solidity >0.6.1 <0.8.0;
|
||||
pragma solidity >=0.6.2 <0.8.0;
|
||||
|
||||
contract D {
|
||||
uint public x;
|
||||
@ -307,7 +307,7 @@ groupings of expressions.
|
||||
|
||||
::
|
||||
|
||||
pragma solidity >0.4.23 <0.8.0;
|
||||
pragma solidity >=0.5.0 <0.8.0;
|
||||
|
||||
contract C {
|
||||
uint index;
|
||||
@ -352,7 +352,7 @@ because only a reference and not a copy is passed.
|
||||
|
||||
::
|
||||
|
||||
pragma solidity >=0.4.16 <0.8.0;
|
||||
pragma solidity >=0.4.22 <0.8.0;
|
||||
|
||||
contract C {
|
||||
uint[20] x;
|
||||
|
@ -24,7 +24,7 @@ to receive their money - contracts cannot activate themselves.
|
||||
|
||||
::
|
||||
|
||||
pragma solidity >=0.4.22 <0.8.0;
|
||||
pragma solidity >=0.5.0 <0.8.0;
|
||||
|
||||
contract SimpleAuction {
|
||||
// Parameters of the auction. Times are either
|
||||
|
@ -338,7 +338,7 @@ The full contract
|
||||
|
||||
::
|
||||
|
||||
pragma solidity >=0.4.24 <0.8.0;
|
||||
pragma solidity >=0.5.0 <0.8.0;
|
||||
|
||||
contract SimplePaymentChannel {
|
||||
address payable public sender; // The account sending payments.
|
||||
|
@ -19,7 +19,7 @@ and the sum of all balances is an invariant across the lifetime of the contract.
|
||||
|
||||
::
|
||||
|
||||
pragma solidity >=0.4.22 <0.8.0;
|
||||
pragma solidity >=0.5.0 <0.8.0;
|
||||
|
||||
library Balances {
|
||||
function move(mapping(address => uint256) storage balances, address from, address to, uint amount) internal {
|
||||
|
@ -25,7 +25,7 @@ you can use state machine-like constructs inside a contract.
|
||||
|
||||
::
|
||||
|
||||
pragma solidity >=0.4.22 <0.8.0;
|
||||
pragma solidity >=0.5.0 <0.8.0;
|
||||
|
||||
contract Purchase {
|
||||
uint public value;
|
||||
|
@ -17,7 +17,7 @@ Storage Example
|
||||
|
||||
::
|
||||
|
||||
pragma solidity >=0.4.0 <0.8.0;
|
||||
pragma solidity >=0.4.16 <0.8.0;
|
||||
|
||||
contract SimpleStorage {
|
||||
uint storedData;
|
||||
|
@ -284,7 +284,7 @@ for the two function parameters and two return variables.
|
||||
|
||||
::
|
||||
|
||||
pragma solidity >=0.4.0 <0.8.0;
|
||||
pragma solidity >=0.4.21 <0.8.0;
|
||||
|
||||
/** @title Shape calculator. */
|
||||
contract ShapeCalculator {
|
||||
|
@ -81,7 +81,7 @@ as it uses ``call`` which forwards all remaining gas by default:
|
||||
|
||||
::
|
||||
|
||||
pragma solidity >=0.4.0 <0.8.0;
|
||||
pragma solidity >=0.6.2 <0.8.0;
|
||||
|
||||
// THIS CONTRACT CONTAINS A BUG - DO NOT USE
|
||||
contract Fund {
|
||||
@ -277,7 +277,7 @@ field of a ``struct`` that is the base type of a dynamic storage array. The
|
||||
|
||||
::
|
||||
|
||||
pragma solidity >=0.5.0 <0.8.0;
|
||||
pragma solidity >=0.6.0 <0.8.0;
|
||||
|
||||
contract Map {
|
||||
mapping (uint => uint)[] array;
|
||||
|
@ -109,7 +109,7 @@ Yes::
|
||||
|
||||
No::
|
||||
|
||||
pragma solidity >=0.4.0 <0.8.0;
|
||||
pragma solidity >=0.6.0 <0.8.0;
|
||||
|
||||
abstract contract A {
|
||||
function spam() virtual pure public;
|
||||
@ -326,7 +326,7 @@ Yes::
|
||||
|
||||
No::
|
||||
|
||||
pragma solidity >=0.4.0 <0.8.0;
|
||||
pragma solidity >=0.6.0 <0.8.0;
|
||||
|
||||
contract A {
|
||||
|
||||
@ -745,7 +745,7 @@ manner as modifiers if the function declaration is long or hard to read.
|
||||
|
||||
Yes::
|
||||
|
||||
pragma solidity >=0.4.0 <0.8.0;
|
||||
pragma solidity >=0.4.22 <0.8.0;
|
||||
|
||||
// Base contracts just to make this compile
|
||||
contract B {
|
||||
@ -777,7 +777,7 @@ Yes::
|
||||
|
||||
No::
|
||||
|
||||
pragma solidity >=0.4.0 <0.8.0;
|
||||
pragma solidity >=0.4.22 <0.8.0;
|
||||
|
||||
|
||||
// Base contracts just to make this compile
|
||||
@ -1000,7 +1000,7 @@ As shown in the example below, if the contract name is `Congress` and the librar
|
||||
|
||||
Yes::
|
||||
|
||||
pragma solidity >=0.4.0 <0.8.0;
|
||||
pragma solidity >=0.4.22 <0.8.0;
|
||||
|
||||
|
||||
// Owned.sol
|
||||
@ -1034,7 +1034,7 @@ and in ``Congress.sol``::
|
||||
|
||||
No::
|
||||
|
||||
pragma solidity >=0.4.0 <0.8.0;
|
||||
pragma solidity >=0.4.22 <0.8.0;
|
||||
|
||||
|
||||
// owned.sol
|
||||
@ -1138,7 +1138,7 @@ multiline comment starting with `/**` and ending with `*/`.
|
||||
For example, the contract from `a simple smart contract <simple-smart-contract>`_ with the comments
|
||||
added looks like the one below::
|
||||
|
||||
pragma solidity >=0.4.0 <0.8.0;
|
||||
pragma solidity >=0.4.16 <0.8.0;
|
||||
|
||||
|
||||
/// @author The Solidity Team
|
||||
|
@ -66,7 +66,7 @@ The example below uses ``_allowances`` to record the amount someone else is allo
|
||||
|
||||
::
|
||||
|
||||
pragma solidity >=0.4.0 <0.8.0;
|
||||
pragma solidity >=0.4.22 <0.8.0;
|
||||
|
||||
contract MappingExample {
|
||||
|
||||
@ -120,7 +120,7 @@ the ``sum`` function iterates over to sum all the values.
|
||||
|
||||
::
|
||||
|
||||
pragma solidity >=0.5.99 <0.8.0;
|
||||
pragma solidity >=0.6.0 <0.8.0;
|
||||
|
||||
struct IndexValue { uint keyIndex; uint value; }
|
||||
struct KeyFlag { uint key; bool deleted; }
|
||||
|
@ -57,7 +57,7 @@ Data locations are not only relevant for persistency of data, but also for the s
|
||||
|
||||
::
|
||||
|
||||
pragma solidity >=0.4.0 <0.8.0;
|
||||
pragma solidity >=0.5.0 <0.8.0;
|
||||
|
||||
contract C {
|
||||
// The data location of x is storage.
|
||||
@ -268,7 +268,7 @@ Array Members
|
||||
|
||||
::
|
||||
|
||||
pragma solidity >=0.4.16 <0.8.0;
|
||||
pragma solidity >=0.6.0 <0.8.0;
|
||||
|
||||
contract ArrayContract {
|
||||
uint[2**20] m_aLotOfIntegers;
|
||||
@ -400,7 +400,7 @@ Array slices are useful to ABI-decode secondary data passed in function paramete
|
||||
|
||||
::
|
||||
|
||||
pragma solidity >=0.4.99 <0.8.0;
|
||||
pragma solidity >=0.6.0 <0.8.0;
|
||||
|
||||
contract Proxy {
|
||||
/// Address of the client contract managed by proxy i.e., this contract
|
||||
@ -437,7 +437,7 @@ shown in the following example:
|
||||
|
||||
::
|
||||
|
||||
pragma solidity >=0.4.11 <0.8.0;
|
||||
pragma solidity >=0.6.0 <0.8.0;
|
||||
|
||||
// Defines a new type with two fields.
|
||||
// Declaring a struct outside of a contract allows
|
||||
|
@ -300,6 +300,11 @@ All three functions ``call``, ``delegatecall`` and ``staticcall`` are very low-l
|
||||
The ``gas`` option is available on all three methods, while the ``value`` option is not
|
||||
supported for ``delegatecall``.
|
||||
|
||||
.. note::
|
||||
It is best to avoid relying on hardcoded gas values in your smart contract code,
|
||||
regardless of whether state is read from or written to, as this can have many pitfalls.
|
||||
Also, access to gas might change in the future.
|
||||
|
||||
.. note::
|
||||
All contracts can be converted to ``address`` type, so it is possible to query the balance of the
|
||||
current contract using ``address(this).balance``.
|
||||
@ -645,7 +650,7 @@ External (or public) functions have the following members:
|
||||
|
||||
Example that shows how to use the members::
|
||||
|
||||
pragma solidity >=0.4.16 <0.8.0;
|
||||
pragma solidity >=0.6.0 <0.8.0;
|
||||
// This will report a warning
|
||||
|
||||
contract Example {
|
||||
@ -665,7 +670,6 @@ Example that shows how to use internal function types::
|
||||
|
||||
pragma solidity >=0.4.16 <0.8.0;
|
||||
|
||||
|
||||
library ArrayUtils {
|
||||
// internal functions can be used in internal library functions because
|
||||
// they will be part of the same code context
|
||||
|
@ -687,7 +687,7 @@ The command above applies all changes as shown below. Please review them careful
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
pragma solidity >0.4.23;
|
||||
pragma solidity >=0.6.0 <0.7.0;
|
||||
|
||||
abstract contract Updateable {
|
||||
function run() public view virtual returns (bool);
|
||||
|
@ -111,7 +111,7 @@ Stand-Alone Usage
|
||||
=================
|
||||
|
||||
You can use Yul in its stand-alone form in the EVM dialect using the Solidity compiler.
|
||||
This will use the `Yul object notation <yul-objects>`_ so that it is possible to refer
|
||||
This will use the :ref:`Yul object notation <yul-object>` so that it is possible to refer
|
||||
to code as data to deploy contracts. This Yul mode is available for the commandline compiler
|
||||
(use ``--strict-assembly``) and for the :ref:`standard-json interface <compiler-api>`:
|
||||
|
||||
@ -146,7 +146,7 @@ so you can e.g. use ``//`` and ``/* */`` to denote comments.
|
||||
There is one exception: Identifiers in Yul can contain dots: ``.``.
|
||||
|
||||
Yul can specify "objects" that consist of code, data and sub-objects.
|
||||
Please see `Yul Objects <yul-objects>`_ below for details on that.
|
||||
Please see :ref:`Yul Objects <yul-object>` below for details on that.
|
||||
In this section, we are only concerned with the code part of such an object.
|
||||
This code part always consists of a curly-braces
|
||||
delimited block. Most tools support specifying just a code block
|
||||
|
@ -153,7 +153,7 @@ void DocStringAnalyser::parseDocStrings(
|
||||
appendError(
|
||||
_node.documentation()->location(),
|
||||
"Documentation tag \"@" + docTag.first + " " + docTag.second.content + "\"" +
|
||||
" exceedes the number of return parameters."
|
||||
" exceeds the number of return parameters."
|
||||
);
|
||||
else
|
||||
{
|
||||
|
@ -328,6 +328,8 @@ void ReferencesResolver::endVisit(VariableDeclaration const& _variable)
|
||||
|
||||
if (_variable.isConstant() && !_variable.isStateVariable())
|
||||
m_errorReporter.declarationError(_variable.location(), "The \"constant\" keyword can only be used for state variables.");
|
||||
if (_variable.immutable() && !_variable.isStateVariable())
|
||||
m_errorReporter.declarationError(_variable.location(), "The \"immutable\" keyword can only be used for state variables.");
|
||||
|
||||
if (!_variable.typeName())
|
||||
{
|
||||
@ -394,7 +396,7 @@ void ReferencesResolver::endVisit(VariableDeclaration const& _variable)
|
||||
else if (_variable.isStateVariable())
|
||||
{
|
||||
solAssert(varLoc == Location::Unspecified, "");
|
||||
typeLoc = _variable.isConstant() ? DataLocation::Memory : DataLocation::Storage;
|
||||
typeLoc = (_variable.isConstant() || _variable.immutable()) ? DataLocation::Memory : DataLocation::Storage;
|
||||
}
|
||||
else if (
|
||||
dynamic_cast<StructDefinition const*>(_variable.scope()) ||
|
||||
|
@ -480,6 +480,10 @@ bool TypeChecker::visit(VariableDeclaration const& _variable)
|
||||
"Initial value for constant variable has to be compile-time constant."
|
||||
);
|
||||
}
|
||||
else if (_variable.immutable())
|
||||
if (!_variable.type()->isValueType())
|
||||
m_errorReporter.typeError(_variable.location(), "Immutable variables cannot have a non-value type.");
|
||||
|
||||
if (!_variable.isStateVariable())
|
||||
{
|
||||
if (varType->dataStoredIn(DataLocation::Memory) || varType->dataStoredIn(DataLocation::CallData))
|
||||
@ -641,16 +645,21 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
|
||||
if (auto var = dynamic_cast<VariableDeclaration const*>(declaration))
|
||||
{
|
||||
solAssert(var->type(), "Expected variable type!");
|
||||
if (var->immutable())
|
||||
{
|
||||
m_errorReporter.typeError(_identifier.location, "Assembly access to immutable variables is not supported.");
|
||||
return size_t(-1);
|
||||
}
|
||||
if (var->isConstant())
|
||||
{
|
||||
var = rootVariableDeclaration(*var);
|
||||
var = rootConstVariableDeclaration(*var);
|
||||
|
||||
if (!var->value())
|
||||
if (var && !var->value())
|
||||
{
|
||||
m_errorReporter.typeError(_identifier.location, "Constant has no value.");
|
||||
return size_t(-1);
|
||||
}
|
||||
else if (!type(*var)->isValueType() || (
|
||||
else if (!var || !type(*var)->isValueType() || (
|
||||
dynamic_cast<Literal const*>(var->value().get()) == nullptr &&
|
||||
type(*var->value())->category() != Type::Category::RationalNumber
|
||||
))
|
||||
|
@ -508,8 +508,6 @@ set<VariableDeclaration::Location> VariableDeclaration::allowedDataLocations() c
|
||||
|
||||
if (!hasReferenceOrMappingType() || isStateVariable() || isEventParameter())
|
||||
return set<Location>{ Location::Unspecified };
|
||||
else if (isStateVariable() && isConstant())
|
||||
return set<Location>{ Location::Memory };
|
||||
else if (isExternalCallableParameter())
|
||||
{
|
||||
set<Location> locations{ Location::CallData };
|
||||
|
@ -879,6 +879,7 @@ public:
|
||||
bool isStateVariable() const { return m_isStateVariable; }
|
||||
bool isIndexed() const { return m_isIndexed; }
|
||||
bool isConstant() const { return m_constantness == Constantness::Constant; }
|
||||
bool immutable() const { return m_constantness == Constantness::Immutable; }
|
||||
ASTPointer<OverrideSpecifier> const& overrides() const { return m_overrides; }
|
||||
Location referenceLocation() const { return m_location; }
|
||||
/// @returns a set of allowed storage locations for the variable.
|
||||
|
@ -21,7 +21,7 @@
|
||||
namespace solidity::frontend
|
||||
{
|
||||
|
||||
VariableDeclaration const* rootVariableDeclaration(VariableDeclaration const& _varDecl)
|
||||
VariableDeclaration const* rootConstVariableDeclaration(VariableDeclaration const& _varDecl)
|
||||
{
|
||||
solAssert(_varDecl.isConstant(), "Constant variable expected");
|
||||
|
||||
@ -30,7 +30,8 @@ VariableDeclaration const* rootVariableDeclaration(VariableDeclaration const& _v
|
||||
while ((identifier = dynamic_cast<Identifier const*>(rootDecl->value().get())))
|
||||
{
|
||||
auto referencedVarDecl = dynamic_cast<VariableDeclaration const*>(identifier->annotation().referencedDeclaration);
|
||||
solAssert(referencedVarDecl && referencedVarDecl->isConstant(), "Identifier is not referencing a variable declaration");
|
||||
if (!referencedVarDecl || !referencedVarDecl->isConstant())
|
||||
return nullptr;
|
||||
rootDecl = referencedVarDecl;
|
||||
}
|
||||
return rootDecl;
|
||||
|
@ -22,8 +22,9 @@ namespace solidity::frontend
|
||||
|
||||
class VariableDeclaration;
|
||||
|
||||
/// Find the topmost referenced variable declaration when the given variable
|
||||
/// Find the topmost referenced constant variable declaration when the given variable
|
||||
/// declaration value is an identifier. Works only for constant variable declarations.
|
||||
VariableDeclaration const* rootVariableDeclaration(VariableDeclaration const& _varDecl);
|
||||
/// Returns nullptr if an identifier in the chain is not referencing a constant variable declaration.
|
||||
VariableDeclaration const* rootConstVariableDeclaration(VariableDeclaration const& _varDecl);
|
||||
|
||||
}
|
||||
|
@ -1487,11 +1487,19 @@ TypeResult ReferenceType::unaryOperatorResult(Token _operator) const
|
||||
case DataLocation::Memory:
|
||||
return TypeProvider::emptyTuple();
|
||||
case DataLocation::Storage:
|
||||
return m_isPointer ? nullptr : TypeProvider::emptyTuple();
|
||||
return isPointer() ? nullptr : TypeProvider::emptyTuple();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool ReferenceType::isPointer() const
|
||||
{
|
||||
if (m_location == DataLocation::Storage)
|
||||
return m_isPointer;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
TypePointer ReferenceType::copyForLocationIfReference(Type const* _type) const
|
||||
{
|
||||
return TypeProvider::withLocationIfReference(m_location, _type);
|
||||
@ -1502,7 +1510,7 @@ string ReferenceType::stringForReferencePart() const
|
||||
switch (m_location)
|
||||
{
|
||||
case DataLocation::Storage:
|
||||
return string("storage ") + (m_isPointer ? "pointer" : "ref");
|
||||
return string("storage ") + (isPointer() ? "pointer" : "ref");
|
||||
case DataLocation::CallData:
|
||||
return "calldata";
|
||||
case DataLocation::Memory:
|
||||
@ -1868,7 +1876,8 @@ u256 ArrayType::memoryDataSize() const
|
||||
std::unique_ptr<ReferenceType> ArrayType::copyForLocation(DataLocation _location, bool _isPointer) const
|
||||
{
|
||||
auto copy = make_unique<ArrayType>(_location);
|
||||
copy->m_isPointer = _isPointer;
|
||||
if (_location == DataLocation::Storage)
|
||||
copy->m_isPointer = _isPointer;
|
||||
copy->m_arrayKind = m_arrayKind;
|
||||
copy->m_baseType = copy->copyForLocationIfReference(m_baseType);
|
||||
copy->m_hasDynamicLength = m_hasDynamicLength;
|
||||
@ -1988,7 +1997,7 @@ vector<tuple<VariableDeclaration const*, u256, unsigned>> ContractType::stateVar
|
||||
vector<VariableDeclaration const*> variables;
|
||||
for (ContractDefinition const* contract: boost::adaptors::reverse(m_contract.annotation().linearizedBaseContracts))
|
||||
for (VariableDeclaration const* variable: contract->stateVariables())
|
||||
if (!variable->isConstant())
|
||||
if (!(variable->isConstant() || variable->immutable()))
|
||||
variables.push_back(variable);
|
||||
TypePointers types;
|
||||
for (auto variable: variables)
|
||||
@ -2247,7 +2256,8 @@ TypeResult StructType::interfaceType(bool _inLibrary) const
|
||||
std::unique_ptr<ReferenceType> StructType::copyForLocation(DataLocation _location, bool _isPointer) const
|
||||
{
|
||||
auto copy = make_unique<StructType>(m_struct, _location);
|
||||
copy->m_isPointer = _isPointer;
|
||||
if (_location == DataLocation::Storage)
|
||||
copy->m_isPointer = _isPointer;
|
||||
return copy;
|
||||
}
|
||||
|
||||
@ -2954,7 +2964,7 @@ vector<tuple<string, TypePointer>> FunctionType::makeStackItems() const
|
||||
if (m_valueSet)
|
||||
slots.emplace_back("value", TypeProvider::uint256());
|
||||
if (m_saltSet)
|
||||
slots.emplace_back("salt", TypeProvider::uint256());
|
||||
slots.emplace_back("salt", TypeProvider::fixedBytes(32));
|
||||
if (bound())
|
||||
for (auto const& [boundName, boundType]: m_parameterTypes.front()->stackItems())
|
||||
slots.emplace_back("self_" + boundName, boundType);
|
||||
|
@ -701,7 +701,9 @@ public:
|
||||
/// pointer type, state variables are bound references. Assignments to pointers or deleting
|
||||
/// them will not modify storage (that will only change the pointer). Assignment from
|
||||
/// non-storage objects to a variable of storage pointer type is not possible.
|
||||
bool isPointer() const { return m_isPointer; }
|
||||
/// For anything other than storage, this always returns true because assignments
|
||||
/// never change the contents of the original value.
|
||||
bool isPointer() const;
|
||||
|
||||
bool operator==(ReferenceType const& _other) const
|
||||
{
|
||||
|
@ -525,7 +525,7 @@ void ContractCompiler::initializeStateVariables(ContractDefinition const& _contr
|
||||
{
|
||||
solAssert(!_contract.isLibrary(), "Tried to initialize state variables of library.");
|
||||
for (VariableDeclaration const* variable: _contract.stateVariables())
|
||||
if (variable->value() && !variable->isConstant())
|
||||
if (variable->value() && !variable->isConstant() && !variable->immutable())
|
||||
ExpressionCompiler(m_context, m_optimiserSettings.runOrderLiterals).appendStateVariableInitialization(*variable);
|
||||
}
|
||||
|
||||
@ -541,6 +541,8 @@ bool ContractCompiler::visit(VariableDeclaration const& _variableDeclaration)
|
||||
if (_variableDeclaration.isConstant())
|
||||
ExpressionCompiler(m_context, m_optimiserSettings.runOrderLiterals)
|
||||
.appendConstStateVariableAccessor(_variableDeclaration);
|
||||
else if (_variableDeclaration.immutable())
|
||||
solUnimplementedAssert(false, "");
|
||||
else
|
||||
ExpressionCompiler(m_context, m_optimiserSettings.runOrderLiterals)
|
||||
.appendStateVariableAccessor(_variableDeclaration);
|
||||
@ -680,9 +682,15 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly)
|
||||
}
|
||||
else if (auto variable = dynamic_cast<VariableDeclaration const*>(decl))
|
||||
{
|
||||
solAssert(!variable->immutable(), "");
|
||||
if (variable->isConstant())
|
||||
{
|
||||
variable = rootVariableDeclaration(*variable);
|
||||
variable = rootConstVariableDeclaration(*variable);
|
||||
// If rootConstVariableDeclaration fails and returns nullptr,
|
||||
// it should have failed in TypeChecker already, causing a compilation error.
|
||||
// In such case we should not get here.
|
||||
solAssert(variable, "");
|
||||
|
||||
u256 value;
|
||||
if (variable->value()->annotation().type->category() == Type::Category::RationalNumber)
|
||||
{
|
||||
|
@ -88,7 +88,7 @@ void ExpressionCompiler::appendConstStateVariableAccessor(VariableDeclaration co
|
||||
|
||||
void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& _varDecl)
|
||||
{
|
||||
solAssert(!_varDecl.isConstant(), "");
|
||||
solAssert(!_varDecl.isConstant() && !_varDecl.immutable(), "");
|
||||
CompilerContext::LocationSetter locationSetter(m_context, _varDecl);
|
||||
FunctionType accessorType(_varDecl);
|
||||
|
||||
@ -1800,6 +1800,7 @@ bool ExpressionCompiler::visit(IndexRangeAccess const& _indexAccess)
|
||||
{
|
||||
CompilerContext::LocationSetter locationSetter(m_context, _indexAccess);
|
||||
_indexAccess.baseExpression().accept(*this);
|
||||
// stack: offset length
|
||||
|
||||
Type const& baseType = *_indexAccess.baseExpression().annotation().type;
|
||||
|
||||
@ -1815,27 +1816,21 @@ bool ExpressionCompiler::visit(IndexRangeAccess const& _indexAccess)
|
||||
acceptAndConvert(*_indexAccess.startExpression(), *TypeProvider::uint256());
|
||||
else
|
||||
m_context << u256(0);
|
||||
// stack: offset length sliceStart
|
||||
|
||||
m_context << Instruction::SWAP1;
|
||||
// stack: offset sliceStart length
|
||||
|
||||
if (_indexAccess.endExpression())
|
||||
acceptAndConvert(*_indexAccess.endExpression(), *TypeProvider::uint256());
|
||||
else
|
||||
m_context << Instruction::DUP2;
|
||||
m_context << Instruction::DUP1;
|
||||
// stack: offset sliceStart length sliceEnd
|
||||
|
||||
m_context.appendInlineAssembly(
|
||||
Whiskers(R"({
|
||||
if gt(sliceStart, sliceEnd) { <revertStringStartEnd> }
|
||||
if gt(sliceEnd, length) { <revertStringEndLength> }
|
||||
m_context << Instruction::SWAP3;
|
||||
// stack: sliceEnd sliceStart length offset
|
||||
|
||||
offset := add(offset, mul(sliceStart, <stride>))
|
||||
length := sub(sliceEnd, sliceStart)
|
||||
})")
|
||||
("stride", toString(arrayType->calldataStride()))
|
||||
("revertStringStartEnd", m_context.revertReasonIfDebug("Slice starts after end"))
|
||||
("revertStringEndLength", m_context.revertReasonIfDebug("Slice is greater than length"))
|
||||
.render(),
|
||||
{"offset", "length", "sliceStart", "sliceEnd"}
|
||||
);
|
||||
|
||||
m_context << Instruction::POP << Instruction::POP;
|
||||
m_context.callYulFunction(m_context.utilFunctions().calldataArrayIndexRangeAccess(*arrayType), 4, 2);
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -2438,10 +2433,12 @@ void ExpressionCompiler::appendExpressionCopyToMemory(Type const& _expectedType,
|
||||
|
||||
void ExpressionCompiler::appendVariable(VariableDeclaration const& _variable, Expression const& _expression)
|
||||
{
|
||||
if (!_variable.isConstant())
|
||||
setLValueFromDeclaration(_variable, _expression);
|
||||
else
|
||||
if (_variable.isConstant())
|
||||
acceptAndConvert(*_variable.value(), *_variable.annotation().type);
|
||||
else if (_variable.immutable())
|
||||
solUnimplemented("");
|
||||
else
|
||||
setLValueFromDeclaration(_variable, _expression);
|
||||
}
|
||||
|
||||
void ExpressionCompiler::setLValueFromDeclaration(Declaration const& _declaration, Expression const& _expression)
|
||||
|
@ -933,6 +933,28 @@ string YulUtilFunctions::calldataArrayIndexAccessFunction(ArrayType const& _type
|
||||
});
|
||||
}
|
||||
|
||||
string YulUtilFunctions::calldataArrayIndexRangeAccess(ArrayType const& _type)
|
||||
{
|
||||
solAssert(_type.dataStoredIn(DataLocation::CallData), "");
|
||||
solAssert(_type.isDynamicallySized(), "");
|
||||
string functionName = "calldata_array_index_range_access_" + _type.identifier();
|
||||
return m_functionCollector.createFunction(functionName, [&]() {
|
||||
return Whiskers(R"(
|
||||
function <functionName>(offset, length, startIndex, endIndex) -> offsetOut, lengthOut {
|
||||
if gt(startIndex, endIndex) { <revertSliceStartAfterEnd> }
|
||||
if gt(endIndex, length) { <revertSliceGreaterThanLength> }
|
||||
offsetOut := add(offset, mul(startIndex, <stride>))
|
||||
lengthOut := sub(endIndex, startIndex)
|
||||
}
|
||||
)")
|
||||
("functionName", functionName)
|
||||
("stride", to_string(_type.calldataStride()))
|
||||
("revertSliceStartAfterEnd", revertReasonIfDebug("Slice starts after end"))
|
||||
("revertSliceGreaterThanLength", revertReasonIfDebug("Slice is greater than length"))
|
||||
.render();
|
||||
});
|
||||
}
|
||||
|
||||
string YulUtilFunctions::accessCalldataTailFunction(Type const& _type)
|
||||
{
|
||||
solAssert(_type.isDynamicallyEncoded(), "");
|
||||
@ -1365,6 +1387,37 @@ string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to)
|
||||
});
|
||||
}
|
||||
|
||||
if (_from.category() == Type::Category::ArraySlice)
|
||||
{
|
||||
solAssert(_from.isDynamicallySized(), "");
|
||||
solAssert(_from.dataStoredIn(DataLocation::CallData), "");
|
||||
solAssert(_to.category() == Type::Category::Array, "");
|
||||
|
||||
ArraySliceType const& fromType = dynamic_cast<ArraySliceType const&>(_from);
|
||||
ArrayType const& targetType = dynamic_cast<ArrayType const&>(_to);
|
||||
|
||||
solAssert(
|
||||
*fromType.arrayType().baseType() == *targetType.baseType(),
|
||||
"Converting arrays of different type is not possible"
|
||||
);
|
||||
|
||||
string const functionName =
|
||||
"convert_" +
|
||||
_from.identifier() +
|
||||
"_to_" +
|
||||
_to.identifier();
|
||||
return m_functionCollector.createFunction(functionName, [&]() {
|
||||
return Whiskers(R"(
|
||||
function <functionName>(offset, length) -> outOffset, outLength {
|
||||
outOffset := offset
|
||||
outLength := length
|
||||
}
|
||||
)")
|
||||
("functionName", functionName)
|
||||
.render();
|
||||
});
|
||||
}
|
||||
|
||||
if (_from.sizeOnStack() != 1 || _to.sizeOnStack() != 1)
|
||||
return conversionFunctionSpecial(_from, _to);
|
||||
|
||||
|
@ -175,6 +175,11 @@ public:
|
||||
/// signature: (baseRef, index) -> offset[, length]
|
||||
std::string calldataArrayIndexAccessFunction(ArrayType const& _type);
|
||||
|
||||
/// @returns the name of a function that returns offset and length for array slice
|
||||
/// for the given array offset, length and start and end indices for slice
|
||||
/// signature: (arrayOffset, arrayLength, sliceStart, sliceEnd) -> offset, length
|
||||
std::string calldataArrayIndexRangeAccess(ArrayType const& _type);
|
||||
|
||||
/// @returns the name of a function that follows a calldata tail while performing
|
||||
/// bounds checks.
|
||||
/// signature: (baseRef, tailPointer) -> offset[, length]
|
||||
|
@ -157,6 +157,7 @@ string IRGenerator::generateGetter(VariableDeclaration const& _varDecl)
|
||||
Type const* type = _varDecl.annotation().type;
|
||||
|
||||
solAssert(!_varDecl.isConstant(), "");
|
||||
solAssert(!_varDecl.immutable(), "");
|
||||
solAssert(_varDecl.isStateVariable(), "");
|
||||
|
||||
if (auto const* mappingType = dynamic_cast<MappingType const*>(type))
|
||||
@ -249,7 +250,7 @@ string IRGenerator::constructorCode(ContractDefinition const& _contract)
|
||||
|
||||
IRGeneratorForStatements generator{m_context, m_utils};
|
||||
for (VariableDeclaration const* variable: contract->stateVariables())
|
||||
if (!variable->isConstant())
|
||||
if (!variable->isConstant() && !variable->immutable())
|
||||
generator.initializeStateVar(*variable);
|
||||
out << generator.code();
|
||||
|
||||
|
@ -140,6 +140,7 @@ void IRGeneratorForStatements::initializeStateVar(VariableDeclaration const& _va
|
||||
{
|
||||
solAssert(m_context.isStateVariable(_varDecl), "Must be a state variable.");
|
||||
solAssert(!_varDecl.isConstant(), "");
|
||||
solAssert(!_varDecl.immutable(), "");
|
||||
if (_varDecl.value())
|
||||
{
|
||||
_varDecl.value()->accept(*this);
|
||||
@ -739,6 +740,25 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
||||
}
|
||||
}
|
||||
|
||||
void IRGeneratorForStatements::endVisit(FunctionCallOptions const& _options)
|
||||
{
|
||||
FunctionType const& previousType = dynamic_cast<FunctionType const&>(*_options.expression().annotation().type);
|
||||
|
||||
solUnimplementedAssert(!previousType.bound(), "");
|
||||
|
||||
// Copy over existing values.
|
||||
for (auto const& item: previousType.stackItems())
|
||||
define(IRVariable(_options).part(get<0>(item)), IRVariable(_options.expression()).part(get<0>(item)));
|
||||
|
||||
for (size_t i = 0; i < _options.names().size(); ++i)
|
||||
{
|
||||
string const& name = *_options.names()[i];
|
||||
solAssert(name == "salt" || name == "gas" || name == "value", "");
|
||||
|
||||
define(IRVariable(_options).part(name), *_options.options()[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
|
||||
{
|
||||
ASTString const& member = _memberAccess.memberName();
|
||||
@ -981,9 +1001,16 @@ void IRGeneratorForStatements::endVisit(IndexAccess const& _indexAccess)
|
||||
}
|
||||
});
|
||||
}
|
||||
else if (baseType.category() == Type::Category::Array)
|
||||
else if (baseType.category() == Type::Category::Array || baseType.category() == Type::Category::ArraySlice)
|
||||
{
|
||||
ArrayType const& arrayType = dynamic_cast<ArrayType const&>(baseType);
|
||||
ArrayType const& arrayType =
|
||||
baseType.category() == Type::Category::Array ?
|
||||
dynamic_cast<ArrayType const&>(baseType) :
|
||||
dynamic_cast<ArraySliceType const&>(baseType).arrayType();
|
||||
|
||||
if (baseType.category() == Type::Category::ArraySlice)
|
||||
solAssert(arrayType.dataStoredIn(DataLocation::CallData) && arrayType.isDynamicallySized(), "");
|
||||
|
||||
solAssert(_indexAccess.indexExpression(), "Index expression expected.");
|
||||
|
||||
switch (arrayType.location())
|
||||
@ -1066,9 +1093,50 @@ void IRGeneratorForStatements::endVisit(IndexAccess const& _indexAccess)
|
||||
solAssert(false, "Index access only allowed for mappings or arrays.");
|
||||
}
|
||||
|
||||
void IRGeneratorForStatements::endVisit(IndexRangeAccess const&)
|
||||
void IRGeneratorForStatements::endVisit(IndexRangeAccess const& _indexRangeAccess)
|
||||
{
|
||||
solUnimplementedAssert(false, "Index range accesses not yet implemented.");
|
||||
Type const& baseType = *_indexRangeAccess.baseExpression().annotation().type;
|
||||
solAssert(
|
||||
baseType.category() == Type::Category::Array || baseType.category() == Type::Category::ArraySlice,
|
||||
"Index range accesses is available only on arrays and array slices."
|
||||
);
|
||||
|
||||
ArrayType const& arrayType =
|
||||
baseType.category() == Type::Category::Array ?
|
||||
dynamic_cast<ArrayType const &>(baseType) :
|
||||
dynamic_cast<ArraySliceType const &>(baseType).arrayType();
|
||||
|
||||
switch (arrayType.location())
|
||||
{
|
||||
case DataLocation::CallData:
|
||||
{
|
||||
solAssert(baseType.isDynamicallySized(), "");
|
||||
IRVariable sliceStart{m_context.newYulVariable(), *TypeProvider::uint256()};
|
||||
if (_indexRangeAccess.startExpression())
|
||||
define(sliceStart, IRVariable{*_indexRangeAccess.startExpression()});
|
||||
else
|
||||
define(sliceStart) << u256(0) << "\n";
|
||||
|
||||
IRVariable sliceEnd{
|
||||
m_context.newYulVariable(),
|
||||
*TypeProvider::uint256()
|
||||
};
|
||||
if (_indexRangeAccess.endExpression())
|
||||
define(sliceEnd, IRVariable{*_indexRangeAccess.endExpression()});
|
||||
else
|
||||
define(sliceEnd, IRVariable{_indexRangeAccess.baseExpression()}.part("length"));
|
||||
|
||||
IRVariable range{_indexRangeAccess};
|
||||
define(range) <<
|
||||
m_utils.calldataArrayIndexRangeAccess(arrayType) << "(" <<
|
||||
IRVariable{_indexRangeAccess.baseExpression()}.commaSeparatedList() << ", " <<
|
||||
sliceStart.name() << ", " <<
|
||||
sliceEnd.name() << ")\n";
|
||||
break;
|
||||
}
|
||||
default:
|
||||
solUnimplementedAssert(false, "Index range accesses is implemented only on calldata arrays.");
|
||||
}
|
||||
}
|
||||
|
||||
void IRGeneratorForStatements::endVisit(Identifier const& _identifier)
|
||||
@ -1104,6 +1172,7 @@ void IRGeneratorForStatements::endVisit(Identifier const& _identifier)
|
||||
// If the value is visited twice, `defineExpression` is called twice on
|
||||
// the same expression.
|
||||
solUnimplementedAssert(!varDecl->isConstant(), "");
|
||||
solUnimplementedAssert(!varDecl->immutable(), "");
|
||||
if (m_context.isLocalVariable(*varDecl))
|
||||
setLValue(_identifier, IRLValue{
|
||||
*varDecl->annotation().type,
|
||||
@ -1519,7 +1588,7 @@ void IRGeneratorForStatements::writeToLValue(IRLValue const& _lvalue, IRVariable
|
||||
solAssert(dynamic_cast<ReferenceType const*>(&_lvalue.type), "");
|
||||
auto const* valueReferenceType = dynamic_cast<ReferenceType const*>(&_value.type());
|
||||
solAssert(valueReferenceType && valueReferenceType->dataStoredIn(DataLocation::Memory), "");
|
||||
m_code << "mstore(" + _memory.address + ", " + _value.name() + ")\n";
|
||||
m_code << "mstore(" + _memory.address + ", " + _value.part("mpos").name() + ")\n";
|
||||
}
|
||||
},
|
||||
[&](IRLValue::Stack const& _stack) { assign(_stack.variable, _value); },
|
||||
|
@ -60,6 +60,7 @@ public:
|
||||
void endVisit(UnaryOperation const& _unaryOperation) override;
|
||||
bool visit(BinaryOperation const& _binOp) override;
|
||||
void endVisit(FunctionCall const& _funCall) override;
|
||||
void endVisit(FunctionCallOptions const& _funCallOptions) override;
|
||||
void endVisit(MemberAccess const& _memberAccess) override;
|
||||
bool visit(InlineAssembly const& _inlineAsm) override;
|
||||
void endVisit(IndexAccess const& _indexAccess) override;
|
||||
|
@ -54,7 +54,7 @@ inline std::optional<RevertStrings> revertStringsFromString(std::string const& _
|
||||
for (auto i: {RevertStrings::Default, RevertStrings::Strip, RevertStrings::Debug, RevertStrings::VerboseDebug})
|
||||
if (revertStringsToString(i) == _str)
|
||||
return i;
|
||||
return {};
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -730,8 +730,18 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
|
||||
{
|
||||
if (_options.allowIndexed && token == Token::Indexed)
|
||||
isIndexed = true;
|
||||
else if (token == Token::Constant)
|
||||
constantness = VariableDeclaration::Constantness::Constant;
|
||||
else if (token == Token::Constant || token == Token::Immutable)
|
||||
{
|
||||
if (constantness != VariableDeclaration::Constantness::Mutable)
|
||||
parserError(
|
||||
string("Constantness already set to ") +
|
||||
(constantness == VariableDeclaration::Constantness::Constant ? "\"constant\"" : "\"immutable\"")
|
||||
);
|
||||
else if (token == Token::Constant)
|
||||
constantness = VariableDeclaration::Constantness::Constant;
|
||||
else if (token == Token::Immutable)
|
||||
constantness = VariableDeclaration::Constantness::Immutable;
|
||||
}
|
||||
else if (_options.allowLocationSpecifier && TokenTraits::isLocationSpecifier(token))
|
||||
{
|
||||
if (location != VariableDeclaration::Location::Unspecified)
|
||||
|
@ -44,7 +44,6 @@ public:
|
||||
std::vector<unsigned char> toBytes() const { return std::vector<unsigned char>(reinterpret_cast<unsigned char const*>(m_data), reinterpret_cast<unsigned char const*>(m_data) + m_count * sizeof(T)); }
|
||||
std::string toString() const { return std::string((char const*)m_data, ((char const*)m_data) + m_count * sizeof(T)); }
|
||||
|
||||
template <class T2> explicit operator vector_ref<T2>() const { assert(m_count * sizeof(T) / sizeof(T2) * sizeof(T2) / sizeof(T) == m_count); return vector_ref<T2>(reinterpret_cast<T2*>(m_data), m_count * sizeof(T) / sizeof(T2)); }
|
||||
operator vector_ref<T const>() const { return vector_ref<T const>(m_data, m_count); }
|
||||
|
||||
T* data() const { return m_data; }
|
||||
|
@ -324,6 +324,7 @@ Parser::ElementaryOperation Parser::parseElementaryOperation()
|
||||
case Token::Byte:
|
||||
case Token::Bool:
|
||||
case Token::Address:
|
||||
case Token::Var:
|
||||
{
|
||||
YulString literal{currentLiteral()};
|
||||
if (m_dialect.builtin(literal))
|
||||
@ -513,6 +514,7 @@ YulString Parser::expectAsmIdentifier()
|
||||
case Token::Address:
|
||||
case Token::Bool:
|
||||
case Token::Identifier:
|
||||
case Token::Var:
|
||||
break;
|
||||
default:
|
||||
expectToken(Token::Identifier);
|
||||
|
79
scripts/common_cmdline.sh
Normal file
79
scripts/common_cmdline.sh
Normal file
@ -0,0 +1,79 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# vim:ts=4:et
|
||||
# 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/>
|
||||
#
|
||||
# (c) 2016-2019 solidity contributors.
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
FULLARGS="--optimize --ignore-missing --combined-json abi,asm,ast,bin,bin-runtime,compact-format,devdoc,hashes,interface,metadata,opcodes,srcmap,srcmap-runtime,userdoc"
|
||||
OLDARGS="--optimize --combined-json abi,asm,ast,bin,bin-runtime,devdoc,interface,metadata,opcodes,srcmap,srcmap-runtime,userdoc"
|
||||
function compileFull()
|
||||
{
|
||||
local expected_exit_code=0
|
||||
local expect_output=0
|
||||
if [[ $1 = '-e' ]]; then
|
||||
expected_exit_code=1
|
||||
expect_output=1
|
||||
shift;
|
||||
fi
|
||||
if [[ $1 = '-w' ]]; then
|
||||
expect_output=1
|
||||
shift;
|
||||
fi
|
||||
if [[ $1 = '-o' ]]; then
|
||||
expect_output=2
|
||||
shift;
|
||||
fi
|
||||
local args=$FULLARGS
|
||||
if [[ $1 = '-v' ]]; then
|
||||
if (echo $2 | grep -Po '(?<=0.4.)\d+' >/dev/null); then
|
||||
patch=$(echo $2 | grep -Po '(?<=0.4.)\d+')
|
||||
if (( patch < 22 )); then
|
||||
args=$OLDARGS
|
||||
fi
|
||||
fi
|
||||
shift 2
|
||||
fi
|
||||
|
||||
local files="$*"
|
||||
local output
|
||||
|
||||
local stderr_path=$(mktemp)
|
||||
|
||||
set +e
|
||||
"$SOLC" ${args} ${files} >/dev/null 2>"$stderr_path"
|
||||
local exit_code=$?
|
||||
local errors=$(grep -v -E 'Warning: This is a pre-release compiler version|Warning: Experimental features are turned on|pragma experimental ABIEncoderV2|^ +--> |^ +\||^[0-9]+ +\|' < "$stderr_path")
|
||||
set -e
|
||||
rm "$stderr_path"
|
||||
|
||||
if [[ \
|
||||
("$exit_code" -ne "$expected_exit_code" || \
|
||||
( $expect_output -eq 0 && -n "$errors" ) || \
|
||||
( $expect_output -ne 0 && $expected_exit_code -eq 0 && $expect_output -ne 2 && -z "$errors" ))
|
||||
]]
|
||||
then
|
||||
printError "Unexpected compilation result:"
|
||||
printError "Expected failure: $expected_exit_code - Expected warning / error output: $expect_output"
|
||||
printError "Was failure: $exit_code"
|
||||
echo "$errors"
|
||||
printError "While calling:"
|
||||
echo "\"$SOLC\" $ARGS $files"
|
||||
printError "Inside directory:"
|
||||
pwd
|
||||
false
|
||||
fi
|
||||
}
|
95
scripts/docs_version_pragma_check.sh
Executable file
95
scripts/docs_version_pragma_check.sh
Executable file
@ -0,0 +1,95 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# 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/>
|
||||
#
|
||||
# (c) 2016 solidity contributors.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
# This script verifies that the examples compile with the oldest version mentioned in the pragma.
|
||||
# It does not verify that it cannot be compiled with an older version
|
||||
# and it also does not verify that it can be compiled with the newest version compatible with the pragma.
|
||||
|
||||
set -e
|
||||
|
||||
## GLOBAL VARIABLES
|
||||
|
||||
REPO_ROOT=$(cd $(dirname "$0")/.. && pwd)
|
||||
SOLIDITY_BUILD_DIR=${SOLIDITY_BUILD_DIR:-build}
|
||||
source "${REPO_ROOT}/scripts/common.sh"
|
||||
source "${REPO_ROOT}/scripts/common_cmdline.sh"
|
||||
|
||||
printTask "Verifying that all examples from the documentation have the correct version range..."
|
||||
SOLTMPDIR=$(mktemp -d)
|
||||
(
|
||||
set -e
|
||||
cd "$SOLTMPDIR"
|
||||
"$REPO_ROOT"/scripts/isolate_tests.py "$REPO_ROOT"/docs/ docs
|
||||
|
||||
for f in *.sol
|
||||
do
|
||||
# The contributors guide uses syntax tests, but we cannot
|
||||
# really handle them here.
|
||||
if grep -E 'DeclarationError:|// ----' "$f" >/dev/null
|
||||
then
|
||||
continue
|
||||
fi
|
||||
echo "$f"
|
||||
|
||||
opts=''
|
||||
# We expect errors if explicitly stated, or if imports
|
||||
# are used (in the style guide)
|
||||
if ( ! grep -E "This will not compile after" "$f" >/dev/null && \
|
||||
grep -E "This will not compile|import \"" "$f" >/dev/null )
|
||||
then
|
||||
opts="-e"
|
||||
fi
|
||||
|
||||
# ignore warnings in this case
|
||||
opts="$opts -o"
|
||||
|
||||
# Get minimum compiler version defined by pragma
|
||||
if (grep -Po '(?<=pragma solidity >=)\d+.\d+.\d+' "$f" >/dev/null); then
|
||||
version="$(grep -Po '(?<=pragma solidity >=)\d+.\d+.\d+' "$f")"
|
||||
if (echo $version | grep -Po '(?<=0.4.)\d+' >/dev/null); then
|
||||
patch=$(echo $version | grep -Po '(?<=0.4.)\d+')
|
||||
if (( patch < 11 )); then
|
||||
version="0.4.11" # first available release on github
|
||||
fi
|
||||
fi
|
||||
elif (grep -Po '(?<=pragma solidity \^)\d+.\d+.\d+' "$f" >/dev/null); then
|
||||
version="$(grep -Po '(?<=pragma solidity \^)\d+.\d+.\d+' "$f")"
|
||||
fi
|
||||
|
||||
opts="$opts -v $version"
|
||||
|
||||
solc_bin="solc-$version"
|
||||
echo "$solc_bin"
|
||||
if [[ ! -f "$solc_bin" ]]; then
|
||||
echo "Downloading release from github..."
|
||||
wget https://github.com/ethereum/solidity/releases/download/v$version/solc-static-linux
|
||||
mv solc-static-linux $solc_bin
|
||||
fi
|
||||
|
||||
ln -sf "$solc_bin" "solc"
|
||||
chmod a+x solc
|
||||
|
||||
SOLC="$SOLTMPDIR/solc"
|
||||
compileFull $opts "$SOLTMPDIR/$f"
|
||||
done
|
||||
)
|
||||
rm -rf "$SOLTMPDIR"
|
||||
echo "Done."
|
22
scripts/endToEndExtraction/create_traces.sh
Executable file
22
scripts/endToEndExtraction/create_traces.sh
Executable file
@ -0,0 +1,22 @@
|
||||
BASE_PATH="$( cd "$(dirname "$0")" >/dev/null 2>&1 || exit ; pwd -P )"
|
||||
|
||||
mkdir -p build
|
||||
cd build || exit
|
||||
cmake ../../../
|
||||
make soltest
|
||||
cd test/ || exit
|
||||
echo "running soltest on 'semanticTests/extracted'..."
|
||||
./soltest --color_output=false --log_level=test_suite -t semanticTests/extracted/ -- --testpath ${BASE_PATH}/../../test --no-smt --evmonepath /Users/alex/evmone/lib/libevmone.dylib --show-messages --show-metadata > ${BASE_PATH}/extracted-tests.trace
|
||||
echo "running soltest on 'semanticTests/extracted'... done"
|
||||
|
||||
cd $BASE_PATH || exit
|
||||
git clone git@github.com:ethereum/solidity.git solidity-develop
|
||||
cd solidity-develop || exit
|
||||
mkdir -p build
|
||||
cd build || exit
|
||||
cmake ..
|
||||
make soltest
|
||||
cd test/ || exit
|
||||
echo "running soltest on 'SolidityEndToEndTest'..."
|
||||
./soltest --color_output=false --log_level=test_suite -t SolidityEndToEndTest/ -- --testpath ${BASE_PATH}/solidity-develop/test --no-smt --evmonepath /Users/alex/evmone/lib/libevmone.dylib --show-messages --show-metadata > ${BASE_PATH}/endToEndExtraction-tests.trace
|
||||
echo "running soltest on 'SolidityEndToEndTest'... done"
|
183
scripts/endToEndExtraction/remove-testcases.py
Executable file
183
scripts/endToEndExtraction/remove-testcases.py
Executable file
@ -0,0 +1,183 @@
|
||||
#!/usr/bin/env python3
|
||||
# pylint: disable=consider-using-enumerate, import-error
|
||||
|
||||
import re
|
||||
import os
|
||||
import sys
|
||||
import getopt
|
||||
import tempfile
|
||||
from getkey import getkey
|
||||
|
||||
|
||||
def parse_call(call):
|
||||
function = ''
|
||||
arguments = ""
|
||||
results = ""
|
||||
search = re.search(r'// (.*):(.*)\s->\s(.*)', call, re.MULTILINE | re.DOTALL)
|
||||
if search:
|
||||
function = search.group(1)
|
||||
arguments = search.group(2)
|
||||
results = search.group(3)
|
||||
if results.find("#") != -1:
|
||||
results = results[:results.find("#")]
|
||||
else:
|
||||
search = re.search(r'// (.*)(.*)\s->\s(.*)', call, re.MULTILINE | re.DOTALL)
|
||||
if search:
|
||||
function = search.group(1)
|
||||
arguments = search.group(2)
|
||||
results = search.group(3)
|
||||
if results.find("#") != -1:
|
||||
results = results[:results.find("#")]
|
||||
if function.find("wei") >= 0:
|
||||
function = function[:function.find(",")]
|
||||
return function.strip(), arguments.strip(), results.strip()
|
||||
|
||||
|
||||
def colorize(left, right, id):
|
||||
red = "\x1b[31m"
|
||||
yellow = "\x1b[33m"
|
||||
reset = "\x1b[0m"
|
||||
colors = [red, yellow]
|
||||
color = colors[id % len(colors)]
|
||||
function, arguments, results = parse_call(right)
|
||||
left = left.replace("compileAndRun", color + "compileAndRun" + reset)
|
||||
right = right.replace("constructor", color + "constructor" + reset)
|
||||
if function:
|
||||
left = left.replace(function, color + function + reset)
|
||||
right = right.replace(function, color + function + reset)
|
||||
if left.find(function):
|
||||
bottom = " " * (left.find(function) - 4) + right
|
||||
else:
|
||||
bottom = " " + right
|
||||
return " " + left + "\n" + bottom # " {:<90} {:<90}\n{}".format(left, right, bottom)
|
||||
|
||||
|
||||
def get_checks(content, sol_file_path):
|
||||
constructors = []
|
||||
checks = []
|
||||
for line in content.split("\n"):
|
||||
line = line.strip()
|
||||
if line.startswith("compileAndRun"):
|
||||
constructors.append(line)
|
||||
if line.startswith("ABI_CHECK") or line.startswith("BOOST_REQUIRE"):
|
||||
checks.append(line)
|
||||
sol_file = open(sol_file_path, "r")
|
||||
sol_constructors = []
|
||||
sol_checks = []
|
||||
inside_expectations = False
|
||||
for line in sol_file.readlines():
|
||||
if line.startswith("// constructor()"):
|
||||
sol_constructors.append(line)
|
||||
elif inside_expectations and line.startswith("// "):
|
||||
sol_checks.append(line)
|
||||
if line.startswith("// ----"):
|
||||
inside_expectations = True
|
||||
sol_file.close()
|
||||
if len(constructors) == len(sol_constructors) == 1:
|
||||
checks.insert(0, constructors[0])
|
||||
sol_checks.insert(0, sol_constructors[0])
|
||||
return checks, sol_checks
|
||||
|
||||
|
||||
def show_test(name, content, sol_file_path, current_test, test_count):
|
||||
cpp_file = tempfile.NamedTemporaryFile(delete=False)
|
||||
cpp_file.write(content.encode())
|
||||
cpp_file.close()
|
||||
|
||||
os.system("clear")
|
||||
print(str(current_test) + " / " + str(test_count) + " - " + name + "\n")
|
||||
diff_env = os.getenv('DIFF', "/usr/local/bin/colordiff -a -d -w -y -W 200 ")
|
||||
os.system(diff_env + " " + cpp_file.name + " " + sol_file_path)
|
||||
os.unlink(cpp_file.name)
|
||||
print("\n")
|
||||
|
||||
checks, sol_checks = get_checks(content, sol_file_path)
|
||||
|
||||
if len(checks) == len(sol_checks):
|
||||
for i in range(0, len(checks)):
|
||||
print(colorize(checks[i].strip(), sol_checks[i].strip(), i))
|
||||
else:
|
||||
print("warning: check count not matching. this should not happen!")
|
||||
|
||||
what = ""
|
||||
print("\nContinue? (ENTER) Abort? (ANY OTHER KEY)")
|
||||
while what != '\n':
|
||||
what = getkey()
|
||||
if what != '\n':
|
||||
sys.exit(0)
|
||||
print()
|
||||
|
||||
|
||||
def get_tests(e2e_path):
|
||||
tests = []
|
||||
for f in os.listdir(e2e_path):
|
||||
if f.endswith(".sol"):
|
||||
tests.append(f.replace(".sol", ""))
|
||||
return tests
|
||||
|
||||
|
||||
def process_input_file(e2e_path, input_file, interactive):
|
||||
tests = get_tests(e2e_path)
|
||||
cpp_file = open(input_file, "r")
|
||||
inside_test = False
|
||||
test_name = ""
|
||||
inside_extracted_test = False
|
||||
new_lines = 0
|
||||
count = 0
|
||||
test_content = ""
|
||||
for line in cpp_file.readlines():
|
||||
test = re.search(r'BOOST_AUTO_TEST_CASE\((.*)\)', line, re.M | re.I)
|
||||
if test:
|
||||
test_name = test.group(1)
|
||||
inside_test = True
|
||||
inside_extracted_test = inside_test & (test_name in tests)
|
||||
if inside_extracted_test:
|
||||
count = count + 1
|
||||
|
||||
if interactive and inside_extracted_test:
|
||||
test_content = test_content + line
|
||||
|
||||
if not inside_extracted_test:
|
||||
if line == "\n":
|
||||
new_lines = new_lines + 1
|
||||
else:
|
||||
new_lines = 0
|
||||
if not interactive and new_lines <= 1:
|
||||
sys.stdout.write(line)
|
||||
|
||||
if line == "}\n":
|
||||
if interactive and inside_extracted_test:
|
||||
show_test(test_name, test_content.strip(), e2e_path + "/" + test_name + ".sol", count, len(tests))
|
||||
test_content = ""
|
||||
inside_test = False
|
||||
cpp_file.close()
|
||||
sys.stdout.flush()
|
||||
|
||||
|
||||
def main(argv):
|
||||
interactive = False
|
||||
input_file = None
|
||||
try:
|
||||
opts, args = getopt.getopt(argv, "if:")
|
||||
except getopt.GetoptError:
|
||||
print("./remove-testcases.py [-i] [-f <full path to SolidityEndToEndTest.cpp>]")
|
||||
sys.exit(1)
|
||||
|
||||
for opt, arg in opts:
|
||||
if opt == '-i':
|
||||
interactive = True
|
||||
elif opt in '-f':
|
||||
input_file = arg
|
||||
|
||||
base_path = os.path.dirname(__file__)
|
||||
|
||||
if not input_file:
|
||||
input_file = base_path + "/../../test/libsolidity/SolidityEndToEndTest.cpp"
|
||||
|
||||
e2e_path = base_path + "/../../test/libsolidity/semanticTests/extracted"
|
||||
|
||||
process_input_file(e2e_path, input_file, interactive)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main(sys.argv[1:])
|
215
scripts/endToEndExtraction/verify-testcases.py
Executable file
215
scripts/endToEndExtraction/verify-testcases.py
Executable file
@ -0,0 +1,215 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# - SolidityEndToEndTest.trace was created with soltest with the following command on
|
||||
# ./soltest --color_output=false --log_level=test_suite -t SolidityEndToEndTest/ -- --no-smt
|
||||
# --evmonepath /Users/alex/evmone/lib/libevmone.dylib --show-messages > SolidityEndToEndTest.trace
|
||||
# - a trace of the semantic tests can be created by using
|
||||
# ./soltest --color_output=false --log_level=test_suite -t semanticTests/extracted/ -- --no-smt
|
||||
# --evmonepath /Users/alex/evmone/lib/libevmone.dylib --show-messages > semanticTests.trace
|
||||
#
|
||||
# verify-testcases.py will compare both traces. If these traces are identical, the extracted tests where
|
||||
# identical with the tests specified in SolidityEndToEndTest.cpp.
|
||||
#
|
||||
# pylint: disable=too-many-instance-attributes
|
||||
|
||||
import re
|
||||
import os
|
||||
import sys
|
||||
import getopt
|
||||
import json
|
||||
|
||||
|
||||
class Trace:
|
||||
def __init__(self, kind, parameter):
|
||||
self.kind = kind
|
||||
self.parameter = parameter
|
||||
self._input = ""
|
||||
self._output = ""
|
||||
self.value = ""
|
||||
self.result = ""
|
||||
self.gas = ""
|
||||
|
||||
def get_input(self):
|
||||
return self._input
|
||||
|
||||
def set_input(self, input):
|
||||
if self.kind == "create":
|
||||
# remove cbor encoded metadata from bytecode
|
||||
length = int(input[-4:], 16) * 2
|
||||
self._input = input[:len(input) - length - 4]
|
||||
|
||||
def get_output(self):
|
||||
return self._output
|
||||
|
||||
def set_output(self, output):
|
||||
if self.kind == "create":
|
||||
# remove cbor encoded metadata from bytecode
|
||||
length = int(output[-4:], 16) * 2
|
||||
self._output = output[:len(output) - length - 4]
|
||||
|
||||
def __str__(self):
|
||||
# we ignore the used gas
|
||||
result = str(
|
||||
"kind='" + self.kind + "' parameter='" + self.parameter + "' input='" + self._input +
|
||||
"' output='" + self._output + "' value='" + self.value + "' result='" + self.result + "'"
|
||||
)
|
||||
return result
|
||||
|
||||
|
||||
class TestCase:
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.metadata = None
|
||||
self.traces = []
|
||||
|
||||
def add_trace(self, kind, parameter):
|
||||
trace = Trace(kind, parameter)
|
||||
self.traces.append(trace)
|
||||
return trace
|
||||
|
||||
|
||||
class TraceAnalyser:
|
||||
def __init__(self, file):
|
||||
self.file = file
|
||||
self.tests = {}
|
||||
self.ready = False
|
||||
|
||||
def analyse(self):
|
||||
trace_file = open(self.file, "r")
|
||||
trace = None
|
||||
test_case = None
|
||||
for line in trace_file.readlines():
|
||||
test = re.search(r'Entering test case "(.*)"', line, re.M | re.I)
|
||||
if test:
|
||||
test_name = test.group(1)
|
||||
test_case = TestCase(test_name)
|
||||
self.tests[test_name] = test_case
|
||||
|
||||
metadata = re.search(r'\s*metadata:\s*(.*)$', line, re.M | re.I)
|
||||
if metadata:
|
||||
test_case.metadata = json.loads(metadata.group(1))
|
||||
del test_case.metadata["sources"]
|
||||
del test_case.metadata["compiler"]["version"]
|
||||
|
||||
create = re.search(r'CREATE\s*([a-fA-F0-9]*):', line, re.M | re.I)
|
||||
if create:
|
||||
trace = test_case.add_trace("create", create.group(1))
|
||||
|
||||
call = re.search(r'CALL\s*([a-fA-F0-9]*)\s*->\s*([a-fA-F0-9]*):', line, re.M | re.I)
|
||||
if call:
|
||||
trace = test_case.add_trace("call", call.group(1)) # + "->" + call.group(2))
|
||||
|
||||
if not create and not call:
|
||||
self.parse_parameters(line, trace)
|
||||
|
||||
trace_file.close()
|
||||
|
||||
print(self.file + ":", len(self.tests), "test-cases.")
|
||||
|
||||
self.ready = True
|
||||
|
||||
@staticmethod
|
||||
def parse_parameters(line, trace):
|
||||
input = re.search(r'\s*in:\s*([a-fA-F0-9]*)', line, re.M | re.I)
|
||||
if input:
|
||||
trace.input = input.group(1)
|
||||
output = re.search(r'\s*out:\s*([a-fA-F0-9]*)', line, re.M | re.I)
|
||||
if output:
|
||||
trace.output = output.group(1)
|
||||
result = re.search(r'\s*result:\s*([a-fA-F0-9]*)', line, re.M | re.I)
|
||||
if result:
|
||||
trace.result = result.group(1)
|
||||
gas_used = re.search(r'\s*gas\sused:\s*([a-fA-F0-9]*)', line, re.M | re.I)
|
||||
if gas_used:
|
||||
trace.gas = gas_used.group(1)
|
||||
value = re.search(r'\s*value:\s*([a-fA-F0-9]*)', line, re.M | re.I)
|
||||
if value:
|
||||
trace.value = value.group(1)
|
||||
|
||||
def diff(self, analyser):
|
||||
if not self.ready:
|
||||
self.analyse()
|
||||
if not analyser.ready:
|
||||
analyser.analyse()
|
||||
|
||||
intersection = set(self.tests.keys()) & set(analyser.tests.keys())
|
||||
mismatches = set()
|
||||
|
||||
for test_name in intersection:
|
||||
left = self.tests[test_name]
|
||||
right = analyser.tests[test_name]
|
||||
if json.dumps(left.metadata) != json.dumps(right.metadata):
|
||||
mismatches.add(
|
||||
(test_name, "metadata where different: " + json.dumps(left.metadata) + " != " + json.dumps(
|
||||
right.metadata)))
|
||||
if len(left.traces) != len(right.traces):
|
||||
mismatches.add((test_name, "trace count are different: " + str(len(left.traces)) +
|
||||
" != " + str(len(right.traces))))
|
||||
else:
|
||||
self.check_traces(test_name, left, right, mismatches)
|
||||
|
||||
for mismatch in mismatches:
|
||||
print(mismatch[0])
|
||||
print(mismatch[1])
|
||||
|
||||
print(len(intersection), "test-cases - ", len(mismatches), " mismatche(s)")
|
||||
|
||||
def check_traces(self, test_name, left, right, mismatches):
|
||||
for trace_id in range(0, len(left.traces)):
|
||||
left_trace = left.traces[trace_id]
|
||||
right_trace = right.traces[trace_id]
|
||||
assert (left_trace.kind == right_trace.kind)
|
||||
if str(left_trace) != str(right_trace):
|
||||
mismatch_info = " " + str(left_trace) + "\n"
|
||||
mismatch_info += " " + str(right_trace) + "\n"
|
||||
mismatch_info += " "
|
||||
for ch in range(0, len(str(left_trace))):
|
||||
if ch < len(str(left_trace)) and ch < len(str(right_trace)):
|
||||
if str(left_trace)[ch] != str(right_trace)[ch]:
|
||||
mismatch_info += "|"
|
||||
else:
|
||||
mismatch_info += " "
|
||||
else:
|
||||
mismatch_info += "|"
|
||||
mismatch_info += "\n"
|
||||
mismatches.add((test_name, mismatch_info))
|
||||
|
||||
|
||||
def main(argv):
|
||||
extracted_tests_trace_file = None
|
||||
end_to_end_trace_file = None
|
||||
try:
|
||||
opts, args = getopt.getopt(argv, "s:e:")
|
||||
except getopt.GetoptError:
|
||||
print("verify-testcases.py [-s <path to semantic-trace>] [-e <path to endToEndExtraction-trace>]")
|
||||
sys.exit(2)
|
||||
|
||||
for opt, arg in opts:
|
||||
if opt in '-s':
|
||||
extracted_tests_trace_file = arg
|
||||
elif opt in '-e':
|
||||
end_to_end_trace_file = arg
|
||||
|
||||
base_path = os.path.dirname(__file__)
|
||||
if not extracted_tests_trace_file:
|
||||
extracted_tests_trace_file = base_path + "/extracted-tests.trace"
|
||||
if not end_to_end_trace_file:
|
||||
end_to_end_trace_file = base_path + "/endToEndExtraction-tests.trace"
|
||||
|
||||
for f in [extracted_tests_trace_file, end_to_end_trace_file]:
|
||||
if not os.path.isfile(f):
|
||||
print("trace file '" + f + "' not found. aborting.")
|
||||
sys.exit(1)
|
||||
|
||||
if not os.path.isfile(extracted_tests_trace_file):
|
||||
print("semantic trace file '" + extracted_tests_trace_file + "' not found. aborting.")
|
||||
sys.exit(1)
|
||||
|
||||
semantic_trace = TraceAnalyser(extracted_tests_trace_file)
|
||||
end_to_end_trace = TraceAnalyser(end_to_end_trace_file)
|
||||
|
||||
semantic_trace.diff(end_to_end_trace)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main(sys.argv[1:])
|
101
scripts/test_antlr_grammar.sh
Executable file
101
scripts/test_antlr_grammar.sh
Executable file
@ -0,0 +1,101 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
ROOT_DIR="$(dirname "$0")"/..
|
||||
WORKDIR="${ROOT_DIR}/build/antlr"
|
||||
ANTLR_JAR="${ROOT_DIR}/build/deps/antlr4.jar"
|
||||
ANTLR_JAR_URI="https://www.antlr.org/download/antlr-4.7.2-complete.jar"
|
||||
GRAMMAR_FILE="$(readlink -f "${ROOT_DIR}/docs/Solidity.g4")"
|
||||
|
||||
SGR_RESET="\033[0m"
|
||||
SGR_BOLD="\033[1m"
|
||||
SGR_GREEN="\033[32m"
|
||||
SGR_RED="\033[31m"
|
||||
SGR_BLUE="\033[34m"
|
||||
|
||||
vt_cursor_up() { echo -ne "\033[A"; }
|
||||
vt_cursor_begin_of_line() { echo -ne "\r"; }
|
||||
|
||||
download_antlr4()
|
||||
{
|
||||
if [[ ! -e "$ANTLR_JAR" ]]
|
||||
then
|
||||
curl -o "${ANTLR_JAR}" "${ANTLR_JAR_URI}"
|
||||
fi
|
||||
}
|
||||
|
||||
prepare_workdir()
|
||||
{
|
||||
mkdir -p "${ROOT_DIR}/build/deps"
|
||||
mkdir -p "${WORKDIR}"
|
||||
mkdir -p "${WORKDIR}/src"
|
||||
mkdir -p "${WORKDIR}/target"
|
||||
}
|
||||
|
||||
prepare_workdir
|
||||
download_antlr4
|
||||
|
||||
if [[ ! -f "${WORKDIR}/target/SolidityParser.class" ]] || \
|
||||
[ "${GRAMMAR_FILE}" -nt "${WORKDIR}/target/SolidityParser.class" ]
|
||||
then
|
||||
echo "Creating parser"
|
||||
# Create lexer/parser from grammar
|
||||
java -jar "${ANTLR_JAR}" "${GRAMMAR_FILE}" -o "${WORKDIR}/src/"
|
||||
|
||||
# Compile lexer/parser sources
|
||||
javac -classpath "${ANTLR_JAR}" "${WORKDIR}/src/"*.java -d "${WORKDIR}/target/"
|
||||
fi
|
||||
|
||||
# Run tests
|
||||
failed_count=0
|
||||
test_file()
|
||||
{
|
||||
local SOL_FILE
|
||||
SOL_FILE="$(readlink -m "${1}")"
|
||||
local cur=${2}
|
||||
local max=${3}
|
||||
|
||||
echo -e "${SGR_BLUE}[${cur}/${max}] Testing ${SOL_FILE}${SGR_RESET} ..."
|
||||
local output
|
||||
output=$(
|
||||
java \
|
||||
-classpath "${ANTLR_JAR}:${WORKDIR}/target/" \
|
||||
"org.antlr.v4.gui.TestRig" \
|
||||
Solidity \
|
||||
sourceUnit <"${SOL_FILE}" 2>&1
|
||||
)
|
||||
vt_cursor_up
|
||||
vt_cursor_begin_of_line
|
||||
if [[ "${output}" == "" ]]
|
||||
then
|
||||
echo -e "${SGR_BLUE}[${cur}/${max}] Testing ${SOL_FILE}${SGR_RESET} ${SGR_BOLD}${SGR_GREEN}OK${SGR_RESET}"
|
||||
else
|
||||
echo -e "${SGR_BLUE}[${cur}/${max}] Testing ${SOL_FILE}${SGR_RESET} ${SGR_BOLD}${SGR_RED}FAILED${SGR_RESET}"
|
||||
echo "${output}"
|
||||
failed_count=$((failed_count + 1))
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# we only want to use files that do not contain errors or multi-source files.
|
||||
SOL_FILES=()
|
||||
while IFS='' read -r line
|
||||
do
|
||||
SOL_FILES+=("$line")
|
||||
done < <(
|
||||
grep -riL -E \
|
||||
"^\/\/ (Syntax|Type|Parser|Declaration)Error|^==== Source:" \
|
||||
"${ROOT_DIR}/test/libsolidity/syntaxTests" \
|
||||
"${ROOT_DIR}/test/libsolidity/semanticTests" \
|
||||
)
|
||||
|
||||
test_count=0
|
||||
for SOL_FILE in "${SOL_FILES[@]}"
|
||||
do
|
||||
test_count=$((test_count + 1))
|
||||
test_file "${SOL_FILE}" ${test_count} ${#SOL_FILES[*]}
|
||||
done
|
||||
|
||||
echo "Summary: ${failed_count} of ${#SOL_FILES[*]} sources failed."
|
||||
exit ${failed_count}
|
@ -13,6 +13,8 @@ set(sources
|
||||
Metadata.h
|
||||
TestCase.cpp
|
||||
TestCase.h
|
||||
TestCaseReader.cpp
|
||||
TestCaseReader.h
|
||||
)
|
||||
detect_stray_source_files("${sources}" ".")
|
||||
|
||||
@ -139,12 +141,17 @@ set(libyul_sources
|
||||
detect_stray_source_files("${libyul_sources}" "libyul/")
|
||||
|
||||
set(yul_phaser_sources
|
||||
yulPhaser/Common.h
|
||||
yulPhaser/TestHelpers.h
|
||||
yulPhaser/TestHelpers.cpp
|
||||
yulPhaser/TestHelpersTest.cpp
|
||||
yulPhaser/Common.cpp
|
||||
yulPhaser/CommonTest.cpp
|
||||
yulPhaser/Chromosome.cpp
|
||||
yulPhaser/FitnessMetrics.cpp
|
||||
yulPhaser/AlgorithmRunner.cpp
|
||||
yulPhaser/GeneticAlgorithms.cpp
|
||||
yulPhaser/Mutations.cpp
|
||||
yulPhaser/PairSelections.cpp
|
||||
yulPhaser/Phaser.cpp
|
||||
yulPhaser/Population.cpp
|
||||
yulPhaser/Program.cpp
|
||||
yulPhaser/Selections.cpp
|
||||
@ -153,9 +160,14 @@ set(yul_phaser_sources
|
||||
# FIXME: yul-phaser is not a library so I can't just add it to target_link_libraries().
|
||||
# My current workaround is just to include its source files here but this introduces
|
||||
# unnecessary duplication. Create a library or find a way to reuse the list in both places.
|
||||
../tools/yulPhaser/AlgorithmRunner.cpp
|
||||
../tools/yulPhaser/Common.cpp
|
||||
../tools/yulPhaser/Chromosome.cpp
|
||||
../tools/yulPhaser/FitnessMetrics.cpp
|
||||
../tools/yulPhaser/GeneticAlgorithms.cpp
|
||||
../tools/yulPhaser/Mutations.cpp
|
||||
../tools/yulPhaser/PairSelections.cpp
|
||||
../tools/yulPhaser/Phaser.cpp
|
||||
../tools/yulPhaser/Population.cpp
|
||||
../tools/yulPhaser/Program.cpp
|
||||
../tools/yulPhaser/Selections.cpp
|
||||
|
@ -96,7 +96,8 @@ CommonOptions::CommonOptions(std::string _caption):
|
||||
("optimize", po::bool_switch(&optimize), "enables optimization")
|
||||
("optimize-yul", po::bool_switch(&optimizeYul), "enables Yul optimization")
|
||||
("abiencoderv2", po::bool_switch(&useABIEncoderV2), "enables abi encoder v2")
|
||||
("show-messages", po::bool_switch(&showMessages), "enables message output");
|
||||
("show-messages", po::bool_switch(&showMessages), "enables message output")
|
||||
("show-metadata", po::bool_switch(&showMetadata), "enables metadata output");
|
||||
}
|
||||
|
||||
void CommonOptions::validate() const
|
||||
|
@ -50,6 +50,7 @@ struct CommonOptions: boost::noncopyable
|
||||
bool disableSMT = false;
|
||||
bool useABIEncoderV2 = false;
|
||||
bool showMessages = false;
|
||||
bool showMetadata = false;
|
||||
|
||||
langutil::EVMVersion evmVersion() const;
|
||||
|
||||
|
@ -56,37 +56,37 @@ int parseUnsignedInteger(string::iterator& _it, string::iterator _end)
|
||||
|
||||
}
|
||||
|
||||
CommonSyntaxTest::CommonSyntaxTest(string const& _filename, langutil::EVMVersion _evmVersion): m_evmVersion(_evmVersion)
|
||||
CommonSyntaxTest::CommonSyntaxTest(string const& _filename, langutil::EVMVersion _evmVersion):
|
||||
EVMVersionRestrictedTestCase(_filename),
|
||||
m_evmVersion(_evmVersion)
|
||||
{
|
||||
ifstream file(_filename);
|
||||
if (!file)
|
||||
BOOST_THROW_EXCEPTION(runtime_error("Cannot open test contract: \"" + _filename + "\"."));
|
||||
file.exceptions(ios::badbit);
|
||||
|
||||
m_sources = parseSourcesAndSettings(file);
|
||||
|
||||
m_expectations = parseExpectations(file);
|
||||
m_sources = m_reader.sources();
|
||||
m_expectations = parseExpectations(m_reader.stream());
|
||||
}
|
||||
|
||||
TestCase::TestResult CommonSyntaxTest::run(ostream& _stream, string const& _linePrefix, bool _formatted)
|
||||
{
|
||||
parseAndAnalyze();
|
||||
|
||||
return printExpectationAndError(_stream, _linePrefix, _formatted) ? TestResult::Success : TestResult::Failure;
|
||||
return conclude(_stream, _linePrefix, _formatted);
|
||||
}
|
||||
|
||||
bool CommonSyntaxTest::printExpectationAndError(ostream& _stream, string const& _linePrefix, bool _formatted)
|
||||
TestCase::TestResult CommonSyntaxTest::conclude(ostream& _stream, string const& _linePrefix, bool _formatted)
|
||||
{
|
||||
if (m_expectations != m_errorList)
|
||||
{
|
||||
string nextIndentLevel = _linePrefix + " ";
|
||||
AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Expected result:" << endl;
|
||||
printErrorList(_stream, m_expectations, nextIndentLevel, _formatted);
|
||||
AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Obtained result:" << endl;
|
||||
printErrorList(_stream, m_errorList, nextIndentLevel, _formatted);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
if (m_expectations == m_errorList)
|
||||
return TestResult::Success;
|
||||
|
||||
printExpectationAndError(_stream, _linePrefix, _formatted);
|
||||
return TestResult::Failure;
|
||||
}
|
||||
|
||||
void CommonSyntaxTest::printExpectationAndError(ostream& _stream, string const& _linePrefix, bool _formatted)
|
||||
{
|
||||
string nextIndentLevel = _linePrefix + " ";
|
||||
AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Expected result:" << endl;
|
||||
printErrorList(_stream, m_expectations, nextIndentLevel, _formatted);
|
||||
AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Obtained result:" << endl;
|
||||
printErrorList(_stream, m_errorList, nextIndentLevel, _formatted);
|
||||
}
|
||||
|
||||
void CommonSyntaxTest::printSource(ostream& _stream, string const& _linePrefix, bool _formatted) const
|
||||
|
@ -73,7 +73,8 @@ protected:
|
||||
bool _formatted = false
|
||||
);
|
||||
|
||||
virtual bool printExpectationAndError(std::ostream& _stream, std::string const& _linePrefix = "", bool _formatted = false);
|
||||
TestResult conclude(std::ostream& _stream, std::string const& _linePrefix = "", bool _formatted = false);
|
||||
void printExpectationAndError(std::ostream& _stream, std::string const& _linePrefix = "", bool _formatted = false);
|
||||
|
||||
static std::vector<SyntaxTestError> parseExpectations(std::istream& _stream);
|
||||
|
||||
|
@ -18,16 +18,9 @@
|
||||
#include <test/Common.h>
|
||||
#include <test/TestCase.h>
|
||||
|
||||
#include <libsolutil/StringUtils.h>
|
||||
|
||||
#include <boost/algorithm/cxx11/none_of.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/algorithm/string/trim.hpp>
|
||||
#include <boost/range/adaptor/map.hpp>
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
@ -35,13 +28,14 @@ using namespace solidity;
|
||||
using namespace solidity::frontend;
|
||||
using namespace solidity::frontend::test;
|
||||
|
||||
void TestCase::printUpdatedSettings(ostream& _stream, const string& _linePrefix, const bool)
|
||||
void TestCase::printSettings(ostream& _stream, const string& _linePrefix, const bool)
|
||||
{
|
||||
if (m_validatedSettings.empty())
|
||||
auto& settings = m_reader.settings();
|
||||
if (settings.empty())
|
||||
return;
|
||||
|
||||
_stream << _linePrefix << "// ====" << endl;
|
||||
for (auto const& setting: m_validatedSettings)
|
||||
for (auto const& setting: settings)
|
||||
_stream << _linePrefix << "// " << setting.first << ": " << setting.second << endl;
|
||||
}
|
||||
|
||||
@ -53,108 +47,12 @@ bool TestCase::isTestFilename(boost::filesystem::path const& _filename)
|
||||
!boost::starts_with(_filename.string(), ".");
|
||||
}
|
||||
|
||||
void TestCase::validateSettings()
|
||||
{
|
||||
if (!m_settings.empty())
|
||||
throw runtime_error(
|
||||
"Unknown setting(s): " +
|
||||
util::joinHumanReadable(m_settings | boost::adaptors::map_keys)
|
||||
);
|
||||
}
|
||||
|
||||
bool TestCase::shouldRun()
|
||||
{
|
||||
m_reader.ensureAllSettingsRead();
|
||||
return m_shouldRun;
|
||||
}
|
||||
|
||||
pair<map<string, string>, size_t> TestCase::parseSourcesAndSettingsWithLineNumbers(istream& _stream)
|
||||
{
|
||||
map<string, string> sources;
|
||||
string currentSourceName;
|
||||
string currentSource;
|
||||
string line;
|
||||
size_t lineNumber = 1;
|
||||
static string const sourceDelimiterStart("==== Source:");
|
||||
static string const sourceDelimiterEnd("====");
|
||||
static string const comment("// ");
|
||||
static string const settingsDelimiter("// ====");
|
||||
static string const delimiter("// ----");
|
||||
bool sourcePart = true;
|
||||
while (getline(_stream, line))
|
||||
{
|
||||
lineNumber++;
|
||||
|
||||
if (boost::algorithm::starts_with(line, delimiter))
|
||||
break;
|
||||
else if (boost::algorithm::starts_with(line, settingsDelimiter))
|
||||
sourcePart = false;
|
||||
else if (sourcePart)
|
||||
{
|
||||
if (boost::algorithm::starts_with(line, sourceDelimiterStart) && boost::algorithm::ends_with(line, sourceDelimiterEnd))
|
||||
{
|
||||
if (!(currentSourceName.empty() && currentSource.empty()))
|
||||
sources[currentSourceName] = std::move(currentSource);
|
||||
currentSource = {};
|
||||
currentSourceName = boost::trim_copy(line.substr(
|
||||
sourceDelimiterStart.size(),
|
||||
line.size() - sourceDelimiterEnd.size() - sourceDelimiterStart.size()
|
||||
));
|
||||
if (sources.count(currentSourceName))
|
||||
throw runtime_error("Multiple definitions of test source \"" + currentSourceName + "\".");
|
||||
}
|
||||
else
|
||||
currentSource += line + "\n";
|
||||
}
|
||||
else if (boost::algorithm::starts_with(line, comment))
|
||||
{
|
||||
size_t colon = line.find(':');
|
||||
if (colon == string::npos)
|
||||
throw runtime_error(string("Expected \":\" inside setting."));
|
||||
string key = line.substr(comment.size(), colon - comment.size());
|
||||
string value = line.substr(colon + 1);
|
||||
boost::algorithm::trim(key);
|
||||
boost::algorithm::trim(value);
|
||||
m_settings[key] = value;
|
||||
}
|
||||
else
|
||||
throw runtime_error(string("Expected \"//\" or \"// ---\" to terminate settings and source."));
|
||||
}
|
||||
sources[currentSourceName] = currentSource;
|
||||
return {sources, lineNumber};
|
||||
}
|
||||
|
||||
map<string, string> TestCase::parseSourcesAndSettings(istream& _stream)
|
||||
{
|
||||
return get<0>(parseSourcesAndSettingsWithLineNumbers(_stream));
|
||||
}
|
||||
|
||||
pair<string, size_t> TestCase::parseSourceAndSettingsWithLineNumbers(istream& _stream)
|
||||
{
|
||||
auto [sourceMap, lineOffset] = parseSourcesAndSettingsWithLineNumbers(_stream);
|
||||
if (sourceMap.size() != 1)
|
||||
BOOST_THROW_EXCEPTION(runtime_error("Expected single source definition, but got multiple sources."));
|
||||
return {std::move(sourceMap.begin()->second), lineOffset};
|
||||
}
|
||||
|
||||
string TestCase::parseSourceAndSettings(istream& _stream)
|
||||
{
|
||||
return parseSourceAndSettingsWithLineNumbers(_stream).first;
|
||||
}
|
||||
|
||||
string TestCase::parseSimpleExpectations(std::istream& _file)
|
||||
{
|
||||
string result;
|
||||
string line;
|
||||
while (getline(_file, line))
|
||||
if (boost::algorithm::starts_with(line, "// "))
|
||||
result += line.substr(3) + "\n";
|
||||
else if (line == "//")
|
||||
result += "\n";
|
||||
else
|
||||
BOOST_THROW_EXCEPTION(runtime_error("Test expectations must start with \"// \"."));
|
||||
return result;
|
||||
}
|
||||
|
||||
void TestCase::expect(string::iterator& _it, string::iterator _end, string::value_type _c)
|
||||
{
|
||||
if (_it == _end || *_it != _c)
|
||||
@ -162,18 +60,11 @@ void TestCase::expect(string::iterator& _it, string::iterator _end, string::valu
|
||||
++_it;
|
||||
}
|
||||
|
||||
void EVMVersionRestrictedTestCase::validateSettings()
|
||||
EVMVersionRestrictedTestCase::EVMVersionRestrictedTestCase(string const& _filename):
|
||||
TestCase(_filename)
|
||||
{
|
||||
if (!m_settings.count("EVMVersion"))
|
||||
return;
|
||||
|
||||
string versionString = m_settings["EVMVersion"];
|
||||
m_validatedSettings["EVMVersion"] = versionString;
|
||||
m_settings.erase("EVMVersion");
|
||||
|
||||
TestCase::validateSettings();
|
||||
|
||||
if (versionString.empty())
|
||||
string versionString = m_reader.stringSetting("EVMVersion", "any");
|
||||
if (versionString == "any")
|
||||
return;
|
||||
|
||||
string comparator;
|
||||
|
@ -17,16 +17,13 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <test/TestCaseReader.h>
|
||||
|
||||
#include <liblangutil/EVMVersion.h>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#include <functional>
|
||||
#include <iosfwd>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
namespace solidity::frontend::test
|
||||
{
|
||||
@ -60,31 +57,27 @@ public:
|
||||
/// If @arg _formatted is true, color-coding may be used to indicate
|
||||
/// error locations in the contract, if applicable.
|
||||
virtual void printSource(std::ostream &_stream, std::string const &_linePrefix = "", bool const _formatted = false) const = 0;
|
||||
/// Outputs the updated settings.
|
||||
virtual void printUpdatedSettings(std::ostream &_stream, std::string const &_linePrefix = "", bool const _formatted = false);
|
||||
/// Outputs settings.
|
||||
virtual void printSettings(std::ostream &_stream, std::string const &_linePrefix = "", bool const _formatted = false);
|
||||
/// Outputs test expectations to @arg _stream that match the actual results of the test.
|
||||
/// Each line of output is prefixed with @arg _linePrefix.
|
||||
virtual void printUpdatedExpectations(std::ostream& _stream, std::string const& _linePrefix) const = 0;
|
||||
|
||||
static bool isTestFilename(boost::filesystem::path const& _filename);
|
||||
|
||||
/// Validates the settings, i.e. moves them from m_settings to m_validatedSettings.
|
||||
/// Throws a runtime exception if any setting is left at this class (i.e. unknown setting).
|
||||
virtual void validateSettings();
|
||||
|
||||
/// Returns true, if the test case is supported in the current environment and false
|
||||
/// otherwise which causes this test to be skipped.
|
||||
/// This might check e.g. for restrictions on the EVM version.
|
||||
/// The function throws an exception if there are unread settings.
|
||||
bool shouldRun();
|
||||
|
||||
protected:
|
||||
std::pair<std::map<std::string, std::string>, std::size_t> parseSourcesAndSettingsWithLineNumbers(std::istream& _file);
|
||||
std::map<std::string, std::string> parseSourcesAndSettings(std::istream& _file);
|
||||
std::pair<std::string, std::size_t> parseSourceAndSettingsWithLineNumbers(std::istream& _file);
|
||||
std::string parseSourceAndSettings(std::istream& _file);
|
||||
static void expect(std::string::iterator& _it, std::string::iterator _end, std::string::value_type _c);
|
||||
// Used by ASTJSONTest, the only TestCase class with a custom parser of the test files.
|
||||
TestCase() = default;
|
||||
|
||||
static std::string parseSimpleExpectations(std::istream& _file);
|
||||
TestCase(std::string const& _filename): m_reader(_filename) {}
|
||||
|
||||
static void expect(std::string::iterator& _it, std::string::iterator _end, std::string::value_type _c);
|
||||
|
||||
template<typename IteratorType>
|
||||
static void skipWhitespace(IteratorType& _it, IteratorType _end)
|
||||
@ -100,18 +93,14 @@ protected:
|
||||
++_it;
|
||||
}
|
||||
|
||||
/// Parsed settings.
|
||||
std::map<std::string, std::string> m_settings;
|
||||
/// Updated settings after validation.
|
||||
std::map<std::string, std::string> m_validatedSettings;
|
||||
|
||||
TestCaseReader m_reader;
|
||||
bool m_shouldRun = true;
|
||||
};
|
||||
|
||||
class EVMVersionRestrictedTestCase: public TestCase
|
||||
{
|
||||
public:
|
||||
void validateSettings() override;
|
||||
protected:
|
||||
EVMVersionRestrictedTestCase(std::string const& _filename);
|
||||
};
|
||||
|
||||
}
|
||||
|
164
test/TestCaseReader.cpp
Normal file
164
test/TestCaseReader.cpp
Normal file
@ -0,0 +1,164 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <test/TestCaseReader.h>
|
||||
|
||||
#include <libsolutil/StringUtils.h>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/range/adaptor/map.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity::frontend::test;
|
||||
|
||||
TestCaseReader::TestCaseReader(string const& _filename):
|
||||
m_file(_filename)
|
||||
{
|
||||
if (!m_file)
|
||||
BOOST_THROW_EXCEPTION(runtime_error("Cannot open file: \"" + _filename + "\"."));
|
||||
m_file.exceptions(ios::badbit);
|
||||
|
||||
tie(m_sources, m_lineNumber) = parseSourcesAndSettingsWithLineNumber(m_file);
|
||||
m_unreadSettings = m_settings;
|
||||
}
|
||||
|
||||
string const& TestCaseReader::source()
|
||||
{
|
||||
if (m_sources.size() != 1)
|
||||
BOOST_THROW_EXCEPTION(runtime_error("Expected single source definition, but got multiple sources."));
|
||||
return m_sources.begin()->second;
|
||||
}
|
||||
|
||||
string TestCaseReader::simpleExpectations()
|
||||
{
|
||||
return parseSimpleExpectations(m_file);
|
||||
}
|
||||
|
||||
bool TestCaseReader::boolSetting(std::string const& _name, bool _defaultValue)
|
||||
{
|
||||
if (m_settings.count(_name) == 0)
|
||||
return _defaultValue;
|
||||
|
||||
m_unreadSettings.erase(_name);
|
||||
string value = m_settings.at(_name);
|
||||
if (value == "false")
|
||||
return false;
|
||||
if (value == "true")
|
||||
return true;
|
||||
|
||||
BOOST_THROW_EXCEPTION(runtime_error("Invalid Boolean value: " + value + "."));
|
||||
}
|
||||
|
||||
size_t TestCaseReader::sizetSetting(std::string const& _name, size_t _defaultValue)
|
||||
{
|
||||
if (m_settings.count(_name) == 0)
|
||||
return _defaultValue;
|
||||
|
||||
m_unreadSettings.erase(_name);
|
||||
|
||||
static_assert(sizeof(unsigned long) <= sizeof(size_t));
|
||||
return stoul(m_settings.at(_name));
|
||||
}
|
||||
|
||||
string TestCaseReader::stringSetting(string const& _name, string const& _defaultValue)
|
||||
{
|
||||
if (m_settings.count(_name) == 0)
|
||||
return _defaultValue;
|
||||
|
||||
m_unreadSettings.erase(_name);
|
||||
return m_settings.at(_name);
|
||||
}
|
||||
|
||||
void TestCaseReader::ensureAllSettingsRead() const
|
||||
{
|
||||
if (!m_unreadSettings.empty())
|
||||
throw runtime_error(
|
||||
"Unknown setting(s): " +
|
||||
util::joinHumanReadable(m_unreadSettings | boost::adaptors::map_keys)
|
||||
);
|
||||
}
|
||||
|
||||
pair<map<string, string>, size_t> TestCaseReader::parseSourcesAndSettingsWithLineNumber(istream& _stream)
|
||||
{
|
||||
map<string, string> sources;
|
||||
string currentSourceName;
|
||||
string currentSource;
|
||||
string line;
|
||||
size_t lineNumber = 1;
|
||||
static string const sourceDelimiterStart("==== Source:");
|
||||
static string const sourceDelimiterEnd("====");
|
||||
static string const comment("// ");
|
||||
static string const settingsDelimiter("// ====");
|
||||
static string const delimiter("// ----");
|
||||
bool sourcePart = true;
|
||||
while (getline(_stream, line))
|
||||
{
|
||||
lineNumber++;
|
||||
|
||||
if (boost::algorithm::starts_with(line, delimiter))
|
||||
break;
|
||||
else if (boost::algorithm::starts_with(line, settingsDelimiter))
|
||||
sourcePart = false;
|
||||
else if (sourcePart)
|
||||
{
|
||||
if (boost::algorithm::starts_with(line, sourceDelimiterStart) && boost::algorithm::ends_with(line, sourceDelimiterEnd))
|
||||
{
|
||||
if (!(currentSourceName.empty() && currentSource.empty()))
|
||||
sources[currentSourceName] = std::move(currentSource);
|
||||
currentSource = {};
|
||||
currentSourceName = boost::trim_copy(line.substr(
|
||||
sourceDelimiterStart.size(),
|
||||
line.size() - sourceDelimiterEnd.size() - sourceDelimiterStart.size()
|
||||
));
|
||||
if (sources.count(currentSourceName))
|
||||
throw runtime_error("Multiple definitions of test source \"" + currentSourceName + "\".");
|
||||
}
|
||||
else
|
||||
currentSource += line + "\n";
|
||||
}
|
||||
else if (boost::algorithm::starts_with(line, comment))
|
||||
{
|
||||
size_t colon = line.find(':');
|
||||
if (colon == string::npos)
|
||||
throw runtime_error(string("Expected \":\" inside setting."));
|
||||
string key = line.substr(comment.size(), colon - comment.size());
|
||||
string value = line.substr(colon + 1);
|
||||
boost::algorithm::trim(key);
|
||||
boost::algorithm::trim(value);
|
||||
m_settings[key] = value;
|
||||
}
|
||||
else
|
||||
throw runtime_error(string("Expected \"//\" or \"// ---\" to terminate settings and source."));
|
||||
}
|
||||
sources[currentSourceName] = currentSource;
|
||||
return { sources, lineNumber };
|
||||
}
|
||||
|
||||
string TestCaseReader::parseSimpleExpectations(istream& _file)
|
||||
{
|
||||
string result;
|
||||
string line;
|
||||
while (getline(_file, line))
|
||||
if (boost::algorithm::starts_with(line, "// "))
|
||||
result += line.substr(3) + "\n";
|
||||
else if (line == "//")
|
||||
result += "\n";
|
||||
else
|
||||
BOOST_THROW_EXCEPTION(runtime_error("Test expectations must start with \"// \"."));
|
||||
return result;
|
||||
}
|
59
test/TestCaseReader.h
Normal file
59
test/TestCaseReader.h
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace solidity::frontend::test
|
||||
{
|
||||
/**
|
||||
* A reader for test case data file, which parses source, settings and (optionally) simple expectations.
|
||||
*/
|
||||
class TestCaseReader
|
||||
{
|
||||
public:
|
||||
TestCaseReader() = default;
|
||||
explicit TestCaseReader(std::string const& _filename);
|
||||
|
||||
std::map<std::string, std::string> const& sources() { return m_sources; }
|
||||
std::string const& source();
|
||||
std::size_t lineNumber() { return m_lineNumber; }
|
||||
std::map<std::string, std::string> const& settings() { return m_settings; }
|
||||
std::ifstream& stream() { return m_file; }
|
||||
|
||||
std::string simpleExpectations();
|
||||
|
||||
bool boolSetting(std::string const& _name, bool _defaultValue);
|
||||
size_t sizetSetting(std::string const& _name, size_t _defaultValue);
|
||||
std::string stringSetting(std::string const& _name, std::string const& _defaultValue);
|
||||
|
||||
void ensureAllSettingsRead() const;
|
||||
|
||||
private:
|
||||
std::pair<std::map<std::string, std::string>, std::size_t> parseSourcesAndSettingsWithLineNumber(std::istream& _file);
|
||||
static std::string parseSimpleExpectations(std::istream& _file);
|
||||
|
||||
std::ifstream m_file;
|
||||
std::map<std::string, std::string> m_sources;
|
||||
std::size_t m_lineNumber = 0;
|
||||
std::map<std::string, std::string> m_settings;
|
||||
std::map<std::string, std::string> m_unreadSettings; ///< tracks which settings are left unread
|
||||
};
|
||||
}
|
@ -94,7 +94,6 @@ int registerTests(
|
||||
{
|
||||
stringstream errorStream;
|
||||
auto testCase = _testCaseCreator(config);
|
||||
testCase->validateSettings();
|
||||
if (testCase->shouldRun())
|
||||
switch (testCase->run(errorStream))
|
||||
{
|
||||
|
@ -33,6 +33,7 @@ set -e
|
||||
REPO_ROOT=$(cd $(dirname "$0")/.. && pwd)
|
||||
SOLIDITY_BUILD_DIR=${SOLIDITY_BUILD_DIR:-build}
|
||||
source "${REPO_ROOT}/scripts/common.sh"
|
||||
source "${REPO_ROOT}/scripts/common_cmdline.sh"
|
||||
|
||||
case "$OSTYPE" in
|
||||
msys)
|
||||
@ -45,6 +46,7 @@ case "$OSTYPE" in
|
||||
SOLC="$REPO_ROOT/${SOLIDITY_BUILD_DIR}/solc/solc"
|
||||
;;
|
||||
esac
|
||||
echo "${SOLC}"
|
||||
|
||||
INTERACTIVE=true
|
||||
if ! tty -s || [ "$CI" ]
|
||||
@ -52,8 +54,6 @@ then
|
||||
INTERACTIVE=""
|
||||
fi
|
||||
|
||||
FULLARGS="--optimize --ignore-missing --combined-json abi,asm,ast,bin,bin-runtime,compact-format,devdoc,hashes,interface,metadata,opcodes,srcmap,srcmap-runtime,userdoc"
|
||||
|
||||
# extend stack size in case we run via ASAN
|
||||
if [[ -n "${CIRCLECI}" ]] || [[ -n "$CI" ]]; then
|
||||
ulimit -s 16384
|
||||
@ -62,57 +62,6 @@ fi
|
||||
|
||||
## FUNCTIONS
|
||||
|
||||
function compileFull()
|
||||
{
|
||||
local expected_exit_code=0
|
||||
local expect_output=0
|
||||
if [[ $1 = '-e' ]]
|
||||
then
|
||||
expected_exit_code=1
|
||||
expect_output=1
|
||||
shift;
|
||||
fi
|
||||
if [[ $1 = '-w' ]]
|
||||
then
|
||||
expect_output=1
|
||||
shift;
|
||||
fi
|
||||
if [[ $1 = '-o' ]]
|
||||
then
|
||||
expect_output=2
|
||||
shift;
|
||||
fi
|
||||
|
||||
local files="$*"
|
||||
local output
|
||||
|
||||
local stderr_path=$(mktemp)
|
||||
|
||||
set +e
|
||||
"$SOLC" $FULLARGS $files >/dev/null 2>"$stderr_path"
|
||||
local exit_code=$?
|
||||
local errors=$(grep -v -E 'Warning: This is a pre-release compiler version|Warning: Experimental features are turned on|pragma experimental ABIEncoderV2|^ +--> |^ +\||^[0-9]+ +\|' < "$stderr_path")
|
||||
set -e
|
||||
rm "$stderr_path"
|
||||
|
||||
if [[ \
|
||||
"$exit_code" -ne "$expected_exit_code" || \
|
||||
( $expect_output -eq 0 && -n "$errors" ) || \
|
||||
( $expect_output -eq 1 && -z "$errors" ) \
|
||||
]]
|
||||
then
|
||||
printError "Unexpected compilation result:"
|
||||
printError "Expected failure: $expected_exit_code - Expected warning / error output: $expect_output"
|
||||
printError "Was failure: $exit_code"
|
||||
echo "$errors"
|
||||
printError "While calling:"
|
||||
echo "\"$SOLC\" $FULLARGS $files"
|
||||
printError "Inside directory:"
|
||||
pwd
|
||||
false
|
||||
fi
|
||||
}
|
||||
|
||||
function ask_expectation_update()
|
||||
{
|
||||
if [ $INTERACTIVE ]
|
||||
|
@ -36,15 +36,11 @@ using namespace solidity::util;
|
||||
using namespace solidity::frontend;
|
||||
using namespace solidity::frontend::test;
|
||||
|
||||
ABIJsonTest::ABIJsonTest(string const& _filename)
|
||||
ABIJsonTest::ABIJsonTest(string const& _filename):
|
||||
TestCase(_filename)
|
||||
{
|
||||
ifstream file(_filename);
|
||||
if (!file)
|
||||
BOOST_THROW_EXCEPTION(runtime_error("Cannot open test contract: \"" + _filename + "\"."));
|
||||
file.exceptions(ios::badbit);
|
||||
|
||||
m_source = parseSourceAndSettings(file);
|
||||
m_expectation = parseSimpleExpectations(file);
|
||||
m_source = m_reader.source();
|
||||
m_expectation = m_reader.simpleExpectations();
|
||||
}
|
||||
|
||||
TestCase::TestResult ABIJsonTest::run(ostream& _stream, string const& _linePrefix, bool _formatted)
|
||||
|
@ -54,7 +54,7 @@
|
||||
"storageLocation": "memory",
|
||||
"typeDescriptions":
|
||||
{
|
||||
"typeIdentifier": "t_array$_t_array$_t_uint256_$dyn_memory_$dyn_memory_ptr",
|
||||
"typeIdentifier": "t_array$_t_array$_t_uint256_$dyn_memory_ptr_$dyn_memory_ptr",
|
||||
"typeString": "uint256[][]"
|
||||
},
|
||||
"typeName":
|
||||
|
@ -36,35 +36,14 @@ using namespace std;
|
||||
namespace fs = boost::filesystem;
|
||||
using namespace boost::unit_test;
|
||||
|
||||
GasTest::GasTest(string const& _filename)
|
||||
GasTest::GasTest(string const& _filename):
|
||||
TestCase(_filename)
|
||||
{
|
||||
ifstream file(_filename);
|
||||
if (!file)
|
||||
BOOST_THROW_EXCEPTION(runtime_error("Cannot open test contract: \"" + _filename + "\"."));
|
||||
file.exceptions(ios::badbit);
|
||||
|
||||
m_source = parseSourceAndSettings(file);
|
||||
|
||||
if (m_settings.count("optimize"))
|
||||
{
|
||||
m_optimise = true;
|
||||
m_validatedSettings["optimize"] = "true";
|
||||
m_settings.erase("optimize");
|
||||
}
|
||||
if (m_settings.count("optimize-yul"))
|
||||
{
|
||||
m_optimiseYul = true;
|
||||
m_validatedSettings["optimize-yul"] = "true";
|
||||
m_settings.erase("optimize-yul");
|
||||
}
|
||||
if (m_settings.count("optimize-runs"))
|
||||
{
|
||||
m_optimiseRuns = stoul(m_settings["optimize-runs"]);
|
||||
m_validatedSettings["optimize-runs"] = m_settings["optimize-runs"];
|
||||
m_settings.erase("optimize-runs");
|
||||
}
|
||||
|
||||
parseExpectations(file);
|
||||
m_source = m_reader.source();
|
||||
m_optimise = m_reader.boolSetting("optimize", false);
|
||||
m_optimiseYul = m_reader.boolSetting("optimize-yul", false);
|
||||
m_optimiseRuns = m_reader.sizetSetting("optimize-runs", 200);
|
||||
parseExpectations(m_reader.stream());
|
||||
}
|
||||
|
||||
void GasTest::parseExpectations(std::istream& _stream)
|
||||
|
@ -135,7 +135,7 @@ TestCase::TestResult SMTCheckerJSONTest::run(ostream& _stream, string const& _li
|
||||
}
|
||||
}
|
||||
|
||||
return printExpectationAndError(_stream, _linePrefix, _formatted) ? TestResult::Success : TestResult::Failure;
|
||||
return conclude(_stream, _linePrefix, _formatted);
|
||||
}
|
||||
|
||||
vector<string> SMTCheckerJSONTest::hashesFromJson(Json::Value const& _jsonObj, string const& _auxInput, string const& _smtlib)
|
||||
|
@ -28,22 +28,17 @@ using namespace solidity::frontend::test;
|
||||
|
||||
SMTCheckerTest::SMTCheckerTest(string const& _filename, langutil::EVMVersion _evmVersion): SyntaxTest(_filename, _evmVersion)
|
||||
{
|
||||
if (m_settings.count("SMTSolvers"))
|
||||
{
|
||||
auto const& choice = m_settings.at("SMTSolvers");
|
||||
if (choice == "any")
|
||||
m_enabledSolvers = smt::SMTSolverChoice::All();
|
||||
else if (choice == "z3")
|
||||
m_enabledSolvers = smt::SMTSolverChoice::Z3();
|
||||
else if (choice == "cvc4")
|
||||
m_enabledSolvers = smt::SMTSolverChoice::CVC4();
|
||||
else if (choice == "none")
|
||||
m_enabledSolvers = smt::SMTSolverChoice::None();
|
||||
else
|
||||
BOOST_THROW_EXCEPTION(runtime_error("Invalid SMT solver choice."));
|
||||
}
|
||||
else
|
||||
auto const& choice = m_reader.stringSetting("SMTSolvers", "any");
|
||||
if (choice == "any")
|
||||
m_enabledSolvers = smt::SMTSolverChoice::All();
|
||||
else if (choice == "z3")
|
||||
m_enabledSolvers = smt::SMTSolverChoice::Z3();
|
||||
else if (choice == "cvc4")
|
||||
m_enabledSolvers = smt::SMTSolverChoice::CVC4();
|
||||
else if (choice == "none")
|
||||
m_enabledSolvers = smt::SMTSolverChoice::None();
|
||||
else
|
||||
BOOST_THROW_EXCEPTION(runtime_error("Invalid SMT solver choice."));
|
||||
|
||||
auto available = ModelChecker::availableSolvers();
|
||||
if (!available.z3)
|
||||
@ -62,5 +57,5 @@ TestCase::TestResult SMTCheckerTest::run(ostream& _stream, string const& _linePr
|
||||
parseAndAnalyze();
|
||||
filterObtainedErrors();
|
||||
|
||||
return printExpectationAndError(_stream, _linePrefix, _formatted) ? TestResult::Success : TestResult::Failure;
|
||||
return conclude(_stream, _linePrefix, _formatted);
|
||||
}
|
||||
|
@ -37,59 +37,42 @@ namespace fs = boost::filesystem;
|
||||
|
||||
|
||||
SemanticTest::SemanticTest(string const& _filename, langutil::EVMVersion _evmVersion):
|
||||
SolidityExecutionFramework(_evmVersion)
|
||||
SolidityExecutionFramework(_evmVersion),
|
||||
EVMVersionRestrictedTestCase(_filename)
|
||||
{
|
||||
ifstream file(_filename);
|
||||
soltestAssert(file, "Cannot open test contract: \"" + _filename + "\".");
|
||||
file.exceptions(ios::badbit);
|
||||
m_source = m_reader.source();
|
||||
m_lineOffset = m_reader.lineNumber();
|
||||
|
||||
std::tie(m_source, m_lineOffset) = parseSourceAndSettingsWithLineNumbers(file);
|
||||
|
||||
if (m_settings.count("compileViaYul"))
|
||||
string choice = m_reader.stringSetting("compileViaYul", "false");
|
||||
if (choice == "also")
|
||||
{
|
||||
if (m_settings["compileViaYul"] == "also")
|
||||
{
|
||||
m_validatedSettings["compileViaYul"] = m_settings["compileViaYul"];
|
||||
m_runWithYul = true;
|
||||
m_runWithoutYul = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_validatedSettings["compileViaYul"] = "only";
|
||||
m_runWithYul = true;
|
||||
m_runWithoutYul = false;
|
||||
}
|
||||
m_settings.erase("compileViaYul");
|
||||
m_runWithYul = true;
|
||||
m_runWithoutYul = true;
|
||||
}
|
||||
if (m_settings.count("ABIEncoderV1Only"))
|
||||
else if (choice == "true")
|
||||
{
|
||||
if (m_settings["ABIEncoderV1Only"] == "true")
|
||||
{
|
||||
m_validatedSettings["ABIEncoderV1Only"] = "true";
|
||||
m_runWithABIEncoderV1Only = true;
|
||||
}
|
||||
m_settings.erase("ABIEncoderV1Only");
|
||||
m_runWithYul = true;
|
||||
m_runWithoutYul = false;
|
||||
}
|
||||
else if (choice == "false")
|
||||
{
|
||||
m_runWithYul = false;
|
||||
m_runWithoutYul = true;
|
||||
}
|
||||
else
|
||||
BOOST_THROW_EXCEPTION(runtime_error("Invalid compileViaYul value: " + choice + "."));
|
||||
|
||||
m_runWithABIEncoderV1Only = m_reader.boolSetting("ABIEncoderV1Only", false);
|
||||
if (m_runWithABIEncoderV1Only && solidity::test::CommonOptions::get().useABIEncoderV2)
|
||||
m_shouldRun = false;
|
||||
|
||||
if (m_settings.count("revertStrings"))
|
||||
{
|
||||
auto revertStrings = revertStringsFromString(m_settings["revertStrings"]);
|
||||
if (revertStrings)
|
||||
m_revertStrings = *revertStrings;
|
||||
m_validatedSettings["revertStrings"] = revertStringsToString(m_revertStrings);
|
||||
m_settings.erase("revertStrings");
|
||||
}
|
||||
auto revertStrings = revertStringsFromString(m_reader.stringSetting("revertStrings", "default"));
|
||||
soltestAssert(revertStrings, "Invalid revertStrings setting.");
|
||||
m_revertStrings = revertStrings.value();
|
||||
|
||||
if (m_settings.count("allowNonExistingFunctions"))
|
||||
{
|
||||
m_validatedSettings["allowNonExistingFunctions"] = true;
|
||||
m_settings.erase("allowNonExistingFunctions");
|
||||
}
|
||||
m_allowNonExistingFunctions = m_reader.boolSetting("allowNonExistingFunctions", false);
|
||||
|
||||
parseExpectations(file);
|
||||
parseExpectations(m_reader.stream());
|
||||
soltestAssert(!m_tests.empty(), "No tests specified in " + _filename);
|
||||
}
|
||||
|
||||
@ -152,7 +135,7 @@ TestCase::TestResult SemanticTest::run(ostream& _stream, string const& _linePref
|
||||
else
|
||||
{
|
||||
soltestAssert(
|
||||
m_validatedSettings.count("allowNonExistingFunctions") || m_compiler.methodIdentifiers(m_compiler.lastContractName()).isMember(test.call().signature),
|
||||
m_allowNonExistingFunctions || m_compiler.methodIdentifiers(m_compiler.lastContractName()).isMember(test.call().signature),
|
||||
"The function " + test.call().signature + " is not known to the compiler"
|
||||
);
|
||||
|
||||
|
@ -65,6 +65,7 @@ private:
|
||||
bool m_runWithYul = false;
|
||||
bool m_runWithoutYul = true;
|
||||
bool m_runWithABIEncoderV1Only = false;
|
||||
bool m_allowNonExistingFunctions = false;
|
||||
};
|
||||
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -21,6 +21,7 @@
|
||||
*/
|
||||
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <boost/test/framework.hpp>
|
||||
#include <test/libsolidity/SolidityExecutionFramework.h>
|
||||
|
||||
@ -60,6 +61,7 @@ bytes SolidityExecutionFramework::compileContract(
|
||||
formatter.printErrorInformation(*error);
|
||||
BOOST_ERROR("Compiling contract failed");
|
||||
}
|
||||
std::string contractName(_contractName.empty() ? m_compiler.lastContractName() : _contractName);
|
||||
evmasm::LinkerObject obj;
|
||||
if (m_compileViaYul)
|
||||
{
|
||||
@ -70,9 +72,7 @@ bytes SolidityExecutionFramework::compileContract(
|
||||
// get code that does not exhaust the stack.
|
||||
OptimiserSettings::full()
|
||||
);
|
||||
if (!asmStack.parseAndAnalyze("", m_compiler.yulIROptimized(
|
||||
_contractName.empty() ? m_compiler.lastContractName() : _contractName
|
||||
)))
|
||||
if (!asmStack.parseAndAnalyze("", m_compiler.yulIROptimized(contractName)))
|
||||
{
|
||||
langutil::SourceReferenceFormatter formatter(std::cerr);
|
||||
|
||||
@ -84,7 +84,9 @@ bytes SolidityExecutionFramework::compileContract(
|
||||
obj = std::move(*asmStack.assemble(yul::AssemblyStack::Machine::EVM).bytecode);
|
||||
}
|
||||
else
|
||||
obj = m_compiler.object(_contractName.empty() ? m_compiler.lastContractName() : _contractName);
|
||||
obj = m_compiler.object(contractName);
|
||||
BOOST_REQUIRE(obj.linkReferences.empty());
|
||||
if (m_showMetadata)
|
||||
cout << "metadata: " << m_compiler.metadata(contractName) << endl;
|
||||
return obj.bytecode;
|
||||
}
|
||||
|
@ -41,9 +41,9 @@ class SolidityExecutionFramework: public solidity::test::ExecutionFramework
|
||||
{
|
||||
|
||||
public:
|
||||
SolidityExecutionFramework() {}
|
||||
SolidityExecutionFramework(): m_showMetadata(solidity::test::CommonOptions::get().showMetadata) {}
|
||||
explicit SolidityExecutionFramework(langutil::EVMVersion _evmVersion):
|
||||
ExecutionFramework(_evmVersion)
|
||||
ExecutionFramework(_evmVersion), m_showMetadata(solidity::test::CommonOptions::get().showMetadata)
|
||||
{}
|
||||
|
||||
virtual bytes const& compileAndRunWithoutCheck(
|
||||
@ -68,6 +68,7 @@ public:
|
||||
protected:
|
||||
solidity::frontend::CompilerStack m_compiler;
|
||||
bool m_compileViaYul = false;
|
||||
bool m_showMetadata = false;
|
||||
RevertStrings m_revertStrings = RevertStrings::Default;
|
||||
|
||||
};
|
||||
|
@ -37,20 +37,7 @@ namespace fs = boost::filesystem;
|
||||
|
||||
SyntaxTest::SyntaxTest(string const& _filename, langutil::EVMVersion _evmVersion, bool _parserErrorRecovery): CommonSyntaxTest(_filename, _evmVersion)
|
||||
{
|
||||
if (m_settings.count("optimize-yul"))
|
||||
{
|
||||
if (m_settings["optimize-yul"] == "true")
|
||||
{
|
||||
m_validatedSettings["optimize-yul"] = "true";
|
||||
m_settings.erase("optimize-yul");
|
||||
}
|
||||
else if (m_settings["optimize-yul"] == "false")
|
||||
{
|
||||
m_validatedSettings["optimize-yul"] = "false";
|
||||
m_settings.erase("optimize-yul");
|
||||
m_optimiseYul = false;
|
||||
}
|
||||
}
|
||||
m_optimiseYul = m_reader.boolSetting("optimize-yul", true);
|
||||
m_parserErrorRecovery = _parserErrorRecovery;
|
||||
}
|
||||
|
||||
@ -60,7 +47,7 @@ TestCase::TestResult SyntaxTest::run(ostream& _stream, string const& _linePrefix
|
||||
parseAndAnalyze();
|
||||
filterObtainedErrors();
|
||||
|
||||
return printExpectationAndError(_stream, _linePrefix, _formatted) ? TestResult::Success : TestResult::Failure;
|
||||
return conclude(_stream, _linePrefix, _formatted);
|
||||
}
|
||||
|
||||
void SyntaxTest::setupCompiler()
|
||||
|
@ -14,9 +14,9 @@ contract C {
|
||||
}
|
||||
// ----
|
||||
// creation:
|
||||
// codeDepositCost: 1120000
|
||||
// executionCost: 1160
|
||||
// totalCost: 1121160
|
||||
// codeDepositCost: 1094400
|
||||
// executionCost: 1134
|
||||
// totalCost: 1095534
|
||||
// external:
|
||||
// a(): 1130
|
||||
// b(uint256): infinite
|
||||
|
@ -0,0 +1,8 @@
|
||||
contract C {
|
||||
function f(bytes calldata data) external pure returns (uint256[] memory) {
|
||||
return abi.decode(data, (uint256[]));
|
||||
}
|
||||
}
|
||||
|
||||
// ----
|
||||
// f(bytes): 0x20, 0xc0, 0x20, 0x4, 0x3, 0x4, 0x5, 0x6 -> 0x20, 0x4, 0x3, 0x4, 0x5, 0x6
|
@ -0,0 +1,12 @@
|
||||
contract C {
|
||||
function f(bytes calldata data)
|
||||
external
|
||||
pure
|
||||
returns (uint256[2][3] memory)
|
||||
{
|
||||
return abi.decode(data, (uint256[2][3]));
|
||||
}
|
||||
}
|
||||
|
||||
// ----
|
||||
// f(bytes): 0x20, 0xc0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6 -> 1, 2, 3, 4, 5, 6
|
@ -0,0 +1,15 @@
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
|
||||
contract C {
|
||||
function f(bytes calldata data)
|
||||
external
|
||||
pure
|
||||
returns (uint256[2][3] memory)
|
||||
{
|
||||
return abi.decode(data, (uint256[2][3]));
|
||||
}
|
||||
}
|
||||
|
||||
// ----
|
||||
// f(bytes): 0x20, 0xc0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6 -> 1, 2, 3, 4, 5, 6
|
@ -0,0 +1,8 @@
|
||||
contract C {
|
||||
function f(bytes memory data) public pure returns (uint256) {
|
||||
return abi.decode(data, (uint256));
|
||||
}
|
||||
}
|
||||
|
||||
// ----
|
||||
// f(bytes): 0x20, 0x20, 0x21 -> 33
|
@ -0,0 +1,22 @@
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
|
||||
contract C {
|
||||
struct S {
|
||||
uint256 a;
|
||||
uint256[] b;
|
||||
}
|
||||
|
||||
function f() public pure returns (S memory) {
|
||||
S memory s;
|
||||
s.a = 8;
|
||||
s.b = new uint256[](3);
|
||||
s.b[0] = 9;
|
||||
s.b[1] = 10;
|
||||
s.b[2] = 11;
|
||||
return abi.decode(abi.encode(s), (S));
|
||||
}
|
||||
}
|
||||
|
||||
// ----
|
||||
// f() -> 0x20, 0x8, 0x40, 0x3, 0x9, 0xa, 0xb
|
@ -0,0 +1,16 @@
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
|
||||
contract C {
|
||||
struct S {
|
||||
uint256 a;
|
||||
uint256[] b;
|
||||
}
|
||||
|
||||
function f(bytes calldata data) external pure returns (S memory) {
|
||||
return abi.decode(data, (S));
|
||||
}
|
||||
}
|
||||
|
||||
// ----
|
||||
// f(bytes): 0x20, 0xe0, 0x20, 0x21, 0x40, 0x3, 0xa, 0xb, 0xc -> 0x20, 0x21, 0x40, 0x3, 0xa, 0xb, 0xc
|
@ -0,0 +1,24 @@
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
|
||||
contract C {
|
||||
bytes data;
|
||||
struct S {
|
||||
uint256 a;
|
||||
uint256[] b;
|
||||
}
|
||||
|
||||
function f() public returns (S memory) {
|
||||
S memory s;
|
||||
s.a = 8;
|
||||
s.b = new uint256[](3);
|
||||
s.b[0] = 9;
|
||||
s.b[1] = 10;
|
||||
s.b[2] = 11;
|
||||
data = abi.encode(s);
|
||||
return abi.decode(data, (S));
|
||||
}
|
||||
}
|
||||
|
||||
// ----
|
||||
// f() -> 0x20, 0x8, 0x40, 0x3, 0x9, 0xa, 0xb
|
36
test/libsolidity/semanticTests/abiEncoderV1/abi_encode.sol
Normal file
36
test/libsolidity/semanticTests/abiEncoderV1/abi_encode.sol
Normal file
@ -0,0 +1,36 @@
|
||||
contract C {
|
||||
function f0() public returns (bytes memory) {
|
||||
return abi.encode();
|
||||
}
|
||||
|
||||
function f1() public returns (bytes memory) {
|
||||
return abi.encode(1, 2);
|
||||
}
|
||||
|
||||
function f2() public returns (bytes memory) {
|
||||
string memory x = "abc";
|
||||
return abi.encode(1, x, 2);
|
||||
}
|
||||
|
||||
function f3() public returns (bytes memory r) {
|
||||
// test that memory is properly allocated
|
||||
string memory x = "abc";
|
||||
r = abi.encode(1, x, 2);
|
||||
bytes memory y = "def";
|
||||
require(y[0] == "d");
|
||||
y[0] = "e";
|
||||
require(y[0] == "e");
|
||||
}
|
||||
|
||||
function f4() public returns (bytes memory) {
|
||||
bytes4 x = "abcd";
|
||||
return abi.encode(bytes2(x));
|
||||
}
|
||||
}
|
||||
|
||||
// ----
|
||||
// f0() -> 0x20, 0x0
|
||||
// f1() -> 0x20, 0x40, 0x1, 0x2
|
||||
// f2() -> 0x20, 0xa0, 0x1, 0x60, 0x2, 0x3, "abc"
|
||||
// f3() -> 0x20, 0xa0, 0x1, 0x60, 0x2, 0x3, "abc"
|
||||
// f4() -> 0x20, 0x20, "ab"
|
@ -0,0 +1,26 @@
|
||||
contract C {
|
||||
bool x;
|
||||
|
||||
function c(uint256 a, uint256[] memory b) public {
|
||||
require(a == 5);
|
||||
require(b.length == 2);
|
||||
require(b[0] == 6);
|
||||
require(b[1] == 7);
|
||||
x = true;
|
||||
}
|
||||
|
||||
function f() public returns (bool) {
|
||||
uint256 a = 5;
|
||||
uint256[] memory b = new uint256[](2);
|
||||
b[0] = 6;
|
||||
b[1] = 7;
|
||||
(bool success, ) = address(this).call(
|
||||
abi.encodeWithSignature("c(uint256,uint256[])", a, b)
|
||||
);
|
||||
require(success);
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
// ----
|
||||
// f() -> true
|
@ -0,0 +1,9 @@
|
||||
contract C {
|
||||
function f() public pure returns (uint256, bytes memory) {
|
||||
bytes memory arg = "abcdefg";
|
||||
return abi.decode(abi.encode(uint256(33), arg), (uint256, bytes));
|
||||
}
|
||||
}
|
||||
|
||||
// ----
|
||||
// f() -> 0x21, 0x40, 0x7, "abcdefg"
|
@ -0,0 +1,9 @@
|
||||
// Tests that rational numbers (even negative ones) are encoded properly.
|
||||
contract C {
|
||||
function f() public pure returns (bytes memory) {
|
||||
return abi.encode(1, -2);
|
||||
}
|
||||
}
|
||||
|
||||
// ----
|
||||
// f() -> 0x20, 0x40, 0x1, -2
|
@ -0,0 +1,13 @@
|
||||
// Tests that this will not end up using a "bytes0" type
|
||||
// (which would assert)
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
|
||||
contract C {
|
||||
function f() public pure returns (bytes memory, bytes memory) {
|
||||
return (abi.encode(""), abi.encodePacked(""));
|
||||
}
|
||||
}
|
||||
|
||||
// ----
|
||||
// f() -> 0x40, 0xa0, 0x40, 0x20, 0x0, 0x0
|
@ -0,0 +1,12 @@
|
||||
// Tests that rational numbers (even negative ones) are encoded properly.
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
|
||||
contract C {
|
||||
function f() public pure returns (bytes memory) {
|
||||
return abi.encode(1, -2);
|
||||
}
|
||||
}
|
||||
|
||||
// ----
|
||||
// f() -> 0x20, 0x40, 0x1, -2
|
@ -0,0 +1,53 @@
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
|
||||
contract C {
|
||||
struct S {
|
||||
uint256 a;
|
||||
uint256[] b;
|
||||
}
|
||||
|
||||
function f0() public pure returns (bytes memory) {
|
||||
return abi.encode();
|
||||
}
|
||||
|
||||
function f1() public pure returns (bytes memory) {
|
||||
return abi.encode(1, 2);
|
||||
}
|
||||
|
||||
function f2() public pure returns (bytes memory) {
|
||||
string memory x = "abc";
|
||||
return abi.encode(1, x, 2);
|
||||
}
|
||||
|
||||
function f3() public pure returns (bytes memory r) {
|
||||
// test that memory is properly allocated
|
||||
string memory x = "abc";
|
||||
r = abi.encode(1, x, 2);
|
||||
bytes memory y = "def";
|
||||
require(y[0] == "d");
|
||||
y[0] = "e";
|
||||
require(y[0] == "e");
|
||||
}
|
||||
|
||||
S s;
|
||||
|
||||
function f4() public returns (bytes memory r) {
|
||||
string memory x = "abc";
|
||||
s.a = 7;
|
||||
s.b.push(2);
|
||||
s.b.push(3);
|
||||
r = abi.encode(1, x, s, 2);
|
||||
bytes memory y = "def";
|
||||
require(y[0] == "d");
|
||||
y[0] = "e";
|
||||
require(y[0] == "e");
|
||||
}
|
||||
}
|
||||
|
||||
// ----
|
||||
// f0() -> 0x20, 0x0
|
||||
// f1() -> 0x20, 0x40, 0x1, 0x2
|
||||
// f2() -> 0x20, 0xa0, 0x1, 0x60, 0x2, 0x3, "abc"
|
||||
// f3() -> 0x20, 0xa0, 0x1, 0x60, 0x2, 0x3, "abc"
|
||||
// f4() -> 0x20, 0x160, 0x1, 0x80, 0xc0, 0x2, 0x3, "abc", 0x7, 0x40, 0x2, 0x2, 0x3
|
@ -0,0 +1,6 @@
|
||||
contract Lotto {
|
||||
uint256 public constant ticketPrice = 555;
|
||||
}
|
||||
|
||||
// ----
|
||||
// ticketPrice() -> 555
|
@ -0,0 +1,8 @@
|
||||
contract Lotto {
|
||||
uint256 public ticketPrice = 500;
|
||||
}
|
||||
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// ticketPrice() -> 500
|
12
test/libsolidity/semanticTests/arithmetics/addmod_mulmod.sol
Normal file
12
test/libsolidity/semanticTests/arithmetics/addmod_mulmod.sol
Normal file
@ -0,0 +1,12 @@
|
||||
contract C {
|
||||
function test() public returns (uint256) {
|
||||
// Note that this only works because computation on literals is done using
|
||||
// unbounded integers.
|
||||
if ((2**255 + 2**255) % 7 != addmod(2**255, 2**255, 7)) return 1;
|
||||
if ((2**255 + 2**255) % 7 != addmod(2**255, 2**255, 7)) return 2;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// ----
|
||||
// test() -> 0
|
@ -0,0 +1,24 @@
|
||||
contract C {
|
||||
function f(uint256 d) public pure returns (uint256) {
|
||||
addmod(1, 2, d);
|
||||
return 2;
|
||||
}
|
||||
|
||||
function g(uint256 d) public pure returns (uint256) {
|
||||
mulmod(1, 2, d);
|
||||
return 2;
|
||||
}
|
||||
|
||||
function h() public pure returns (uint256) {
|
||||
mulmod(0, 1, 2);
|
||||
mulmod(1, 0, 2);
|
||||
addmod(0, 1, 2);
|
||||
addmod(1, 0, 2);
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
// ----
|
||||
// f(uint256): 0 -> FAILURE
|
||||
// g(uint256): 0 -> FAILURE
|
||||
// h() -> 2
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user