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
6b3171c38b
@ -598,7 +598,7 @@ jobs:
|
|||||||
|
|
||||||
b_ems:
|
b_ems:
|
||||||
docker:
|
docker:
|
||||||
- image: ethereum/solidity-buildpack-deps:emsdk-1.39.15-1
|
- image: ethereum/solidity-buildpack-deps:emsdk-1.39.15-2
|
||||||
environment:
|
environment:
|
||||||
TERM: xterm
|
TERM: xterm
|
||||||
steps:
|
steps:
|
||||||
|
@ -26,11 +26,9 @@
|
|||||||
# contains a Makefile in the docker/ subdirectory that can be used to create the
|
# contains a Makefile in the docker/ subdirectory that can be used to create the
|
||||||
# required base image using:
|
# required base image using:
|
||||||
#
|
#
|
||||||
# make version=1.39.15-fastcomp build
|
# make version=1.39.15 build
|
||||||
#
|
#
|
||||||
# TODO: switch to the upstream backend by removing "-fastcomp".
|
FROM emscripten/emsdk:1.39.15 AS base
|
||||||
#
|
|
||||||
FROM emscripten/emsdk:1.39.15-fastcomp AS base
|
|
||||||
|
|
||||||
ADD emscripten.jam /usr/src
|
ADD emscripten.jam /usr/src
|
||||||
RUN set -ex; \
|
RUN set -ex; \
|
||||||
@ -47,6 +45,7 @@ RUN set -ex; \
|
|||||||
-DZ3_BUILD_TEST_EXECUTABLES=OFF \
|
-DZ3_BUILD_TEST_EXECUTABLES=OFF \
|
||||||
-DZ3_BUILD_EXECUTABLE=OFF \
|
-DZ3_BUILD_EXECUTABLE=OFF \
|
||||||
-DZ3_SINGLE_THREADED=ON \
|
-DZ3_SINGLE_THREADED=ON \
|
||||||
|
-DCMAKE_CXX_FLAGS="-s DISABLE_EXCEPTION_CATCHING=0" \
|
||||||
..; \
|
..; \
|
||||||
make; make install; \
|
make; make install; \
|
||||||
rm -r /usr/src/z3; \
|
rm -r /usr/src/z3; \
|
||||||
@ -61,6 +60,6 @@ RUN set -ex; \
|
|||||||
echo "using emscripten : : em++ ;" >> project-config.jam ; \
|
echo "using emscripten : : em++ ;" >> project-config.jam ; \
|
||||||
./b2 toolset=emscripten link=static variant=release threading=single runtime-link=static \
|
./b2 toolset=emscripten link=static variant=release threading=single runtime-link=static \
|
||||||
--with-system --with-filesystem --with-test --with-program_options \
|
--with-system --with-filesystem --with-test --with-program_options \
|
||||||
cxxflags="-Wno-unused-local-typedef -Wno-variadic-macros -Wno-c99-extensions -Wno-all" \
|
cxxflags="-s DISABLE_EXCEPTION_CATCHING=0 -Wno-unused-local-typedef -Wno-variadic-macros -Wno-c99-extensions -Wno-all" \
|
||||||
--prefix=/emsdk/emscripten/sdk/system install; \
|
--prefix=/emsdk/emscripten/sdk/system install; \
|
||||||
rm -r /usr/src/boost_1_73_0
|
rm -r /usr/src/boost_1_73_0
|
||||||
|
43
Changelog.md
43
Changelog.md
@ -19,39 +19,56 @@ Bugfixes:
|
|||||||
* NatSpec: Constructors and functions have consistent userdoc output.
|
* NatSpec: Constructors and functions have consistent userdoc output.
|
||||||
|
|
||||||
|
|
||||||
### 0.6.9 (unreleased)
|
### 0.6.10 (unreleased)
|
||||||
|
|
||||||
|
Language Features:
|
||||||
|
|
||||||
|
|
||||||
|
Compiler Features:
|
||||||
|
* Yul: Raise warning for switch statements that only have a default and no other cases.
|
||||||
|
|
||||||
|
|
||||||
|
Bugfixes:
|
||||||
|
* SMTChecker: Fix internal error when encoding tuples of tuples.
|
||||||
|
* SMTChecker: Fix aliasing soundness after pushing to an array pointer.
|
||||||
|
|
||||||
|
|
||||||
|
### 0.6.9 (2020-06-04)
|
||||||
|
|
||||||
Language Features:
|
Language Features:
|
||||||
* Permit calldata location for all variables.
|
* Permit calldata location for all variables.
|
||||||
|
* NatSpec: Support NatSpec comments on state variables.
|
||||||
* Yul: EVM instruction `pc()` is marked deprecated and will be removed in the next breaking release.
|
* Yul: EVM instruction `pc()` is marked deprecated and will be removed in the next breaking release.
|
||||||
|
|
||||||
|
|
||||||
Compiler Features:
|
Compiler Features:
|
||||||
* Code Generator: Do not introduce new source references for small internal routines.
|
|
||||||
* Build system: Update the soljson.js build to emscripten 1.39.15 and boost 1.73.0 and include Z3 for integrated SMTChecker support without the callback mechanism.
|
* Build system: Update the soljson.js build to emscripten 1.39.15 and boost 1.73.0 and include Z3 for integrated SMTChecker support without the callback mechanism.
|
||||||
* Commandline Interface: Adds new option ``--base-path PATH`` to use the given path as the root of the source tree instead of the root of the filesystem.
|
* Build system: Switch the emscripten build from the fastcomp backend to the upstream backend.
|
||||||
|
* Code Generator: Do not introduce new internal source references for small compiler routines.
|
||||||
|
* Commandline Interface: Adds new option ``--base-path PATH`` to use the given path as the root of the source tree (defaults to the root of the filesystem).
|
||||||
* SMTChecker: Support array ``length``.
|
* SMTChecker: Support array ``length``.
|
||||||
* SMTChecker: Support array ``push`` and ``pop``.
|
* SMTChecker: Support array ``push`` and ``pop``.
|
||||||
* SMTChecker: General support to BitVectors and the bitwise ``and`` operator.
|
* SMTChecker: General support to BitVectors and the bitwise ``and`` operator.
|
||||||
* Add support for natspec comments on state variables.
|
|
||||||
|
|
||||||
|
|
||||||
Bugfixes:
|
Bugfixes:
|
||||||
|
* Code Generator: Trigger proper unimplemented errors on certain array copy operations.
|
||||||
|
* Commandline Interface: Fix internal error when using ``--assemble`` or ``--yul`` options with ``--machine ewasm`` but without specifying ``--yul-dialect``.
|
||||||
|
* NatSpec: DocString block is terminated when encountering an empty line.
|
||||||
* Optimizer: Fixed a bug in BlockDeDuplicator.
|
* Optimizer: Fixed a bug in BlockDeDuplicator.
|
||||||
|
* Scanner: Fix bug when two empty NatSpec comments lead to scanning past EOL.
|
||||||
|
* SMTChecker: Fix internal error on try/catch clauses with parameters.
|
||||||
|
* SMTChecker: Fix internal error when applying arithmetic operators to fixed point variables.
|
||||||
|
* SMTChecker: Fix internal error when assigning to index access inside branches.
|
||||||
|
* SMTChecker: Fix internal error when short circuiting Boolean expressions with function calls in state variable initialization.
|
||||||
* Type Checker: Disallow assignments to storage variables of type ``mapping``.
|
* Type Checker: Disallow assignments to storage variables of type ``mapping``.
|
||||||
* Type Checker: Disallow inline arrays of non-nameable types.
|
* Type Checker: Disallow inline arrays of non-nameable types.
|
||||||
|
* Type Checker: Disallow usage of override with non-public state variables.
|
||||||
* Type Checker: Fix internal compiler error when accessing members of array slices.
|
* Type Checker: Fix internal compiler error when accessing members of array slices.
|
||||||
|
* Type Checker: Fix internal compiler error when forward referencing non-literal constants from inline assembly.
|
||||||
* Type Checker: Fix internal compiler error when trying to decode too large static arrays.
|
* Type Checker: Fix internal compiler error when trying to decode too large static arrays.
|
||||||
* Type Checker: Fix wrong compiler error when referencing an overridden function without calling it.
|
* Type Checker: Fix wrong compiler error when referencing an overridden function without calling it.
|
||||||
* Type Checker: Fix internal compiler error when forward referencing non-literal constants from inline assembly.
|
|
||||||
* Type Checker: Disallow usage of override with non-public state variables.
|
|
||||||
* NatSpec: DocString block is terminated when encountering an empty line.
|
|
||||||
* Scanner: Fix bug when two empty NatSpec comments lead to scanning past EOL.
|
|
||||||
* Code Generator: Trigger proper unimplemented errors on certain array copy operations.
|
|
||||||
* SMTChecker: Fix internal error when applying arithmetic operators to fixed point variables.
|
|
||||||
* SMTChecker: Fix internal error when short circuiting Boolean expressions with function calls in state variable initialization.
|
|
||||||
* SMTChecker: Fix internal error when assigning to index access inside branches.
|
|
||||||
* SMTChecker: Fix internal error on try/catch clauses with parameters.
|
|
||||||
|
|
||||||
### 0.6.8 (2020-05-14)
|
### 0.6.8 (2020-05-14)
|
||||||
|
|
||||||
|
@ -6,9 +6,8 @@
|
|||||||
- [ ] Readthedocs account, access to the Solidity project
|
- [ ] Readthedocs account, access to the Solidity project
|
||||||
- [ ] Write access to https://github.com/ethereum/homebrew-ethereum
|
- [ ] Write access to https://github.com/ethereum/homebrew-ethereum
|
||||||
|
|
||||||
### Pre-release
|
### Blog Post
|
||||||
- [ ] Ensure that a Github project exists for the release.
|
- [ ] Create a post on https://github.com/ethereum/solidity-blog and explain some of the new features or concepts.
|
||||||
- [ ] Check that all issues and pull requests from the Github project to be released are merged to ``develop``.
|
|
||||||
|
|
||||||
### Changelog
|
### Changelog
|
||||||
- [ ] Sort the changelog entries alphabetically and correct any errors you notice.
|
- [ ] Sort the changelog entries alphabetically and correct any errors you notice.
|
||||||
@ -33,8 +32,9 @@
|
|||||||
- [ ] Wait for the ``~ethereum/ubuntu/ethereum-static`` PPA build to be finished and published for *all platforms*. SERIOUSLY: DO NOT PROCEED EARLIER!!! *After* the static builds are *published*, copy the static package to the ``~ethereum/ubuntu/ethereum`` PPA for the destination series ``Trusty`` and ``Xenial`` while selecting ``Copy existing binaries``.
|
- [ ] Wait for the ``~ethereum/ubuntu/ethereum-static`` PPA build to be finished and published for *all platforms*. SERIOUSLY: DO NOT PROCEED EARLIER!!! *After* the static builds are *published*, copy the static package to the ``~ethereum/ubuntu/ethereum`` PPA for the destination series ``Trusty`` and ``Xenial`` while selecting ``Copy existing binaries``.
|
||||||
- [ ] Check that the Docker release was pushed to Docker Hub (this still seems to have problems, run ``./scripts/docker_deploy_manual.sh v0.x.x``).
|
- [ ] Check that the Docker release was pushed to Docker Hub (this still seems to have problems, run ``./scripts/docker_deploy_manual.sh v0.x.x``).
|
||||||
|
|
||||||
### Homebrew
|
### Homebrew and MacOS
|
||||||
- [ ] Update the version and the hash (``sha256sum solidity_x.x.x.tar.gz``) in https://github.com/ethereum/homebrew-ethereum/blob/master/solidity.rb
|
- [ ] Update the version and the hash (``sha256sum solidity_x.x.x.tar.gz``) in https://github.com/ethereum/homebrew-ethereum/blob/master/solidity.rb
|
||||||
|
- [ ] Take the binary from the ``b_osx`` run of the released commit in circle-ci and add it to the release page as ``solc-macos``.
|
||||||
|
|
||||||
### Documentation
|
### Documentation
|
||||||
- [ ] Build the new version on https://readthedocs.org/projects/solidity/ (select `latest` on the bottom of the page and click `BUILD`)
|
- [ ] Build the new version on https://readthedocs.org/projects/solidity/ (select `latest` on the bottom of the page and click `BUILD`)
|
||||||
|
@ -16,9 +16,11 @@
|
|||||||
|
|
||||||
include(EthCheckCXXCompilerFlag)
|
include(EthCheckCXXCompilerFlag)
|
||||||
|
|
||||||
eth_add_cxx_compiler_flag_if_supported(-fstack-protector-strong have_stack_protector_strong_support)
|
if(NOT EMSCRIPTEN)
|
||||||
if(NOT have_stack_protector_strong_support)
|
eth_add_cxx_compiler_flag_if_supported(-fstack-protector-strong have_stack_protector_strong_support)
|
||||||
eth_add_cxx_compiler_flag_if_supported(-fstack-protector)
|
if(NOT have_stack_protector_strong_support)
|
||||||
|
eth_add_cxx_compiler_flag_if_supported(-fstack-protector)
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
eth_add_cxx_compiler_flag_if_supported(-Wimplicit-fallthrough)
|
eth_add_cxx_compiler_flag_if_supported(-Wimplicit-fallthrough)
|
||||||
@ -109,15 +111,13 @@ if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MA
|
|||||||
# Re-enable exception catching (optimisations above -O1 disable it)
|
# Re-enable exception catching (optimisations above -O1 disable it)
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s DISABLE_EXCEPTION_CATCHING=0")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s DISABLE_EXCEPTION_CATCHING=0")
|
||||||
# Remove any code related to exit (such as atexit)
|
# Remove any code related to exit (such as atexit)
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s NO_EXIT_RUNTIME=1")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s EXIT_RUNTIME=0")
|
||||||
# Remove any code related to filesystem access
|
# Remove any code related to filesystem access
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s NO_FILESYSTEM=1")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s FILESYSTEM=0")
|
||||||
# Remove variables even if it needs to be duplicated (can improve speed at the cost of size)
|
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s AGGRESSIVE_VARIABLE_ELIMINATION=1")
|
|
||||||
# Allow memory growth, but disable some optimisations
|
# Allow memory growth, but disable some optimisations
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s ALLOW_MEMORY_GROWTH=1")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s ALLOW_MEMORY_GROWTH=1")
|
||||||
# Disable eval()
|
# Disable eval()
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s NO_DYNAMIC_EXECUTION=1")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s DYNAMIC_EXECUTION=0")
|
||||||
# Disable greedy exception catcher
|
# Disable greedy exception catcher
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s NODEJS_CATCH_EXIT=0")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s NODEJS_CATCH_EXIT=0")
|
||||||
# Abort if linking results in any undefined symbols
|
# Abort if linking results in any undefined symbols
|
||||||
|
@ -42,7 +42,8 @@ For most of the topics the compiler will provide suggestions.
|
|||||||
storage arrays.
|
storage arrays.
|
||||||
|
|
||||||
* The new keyword ``abstract`` can be used to mark contracts as abstract. It has to be used
|
* The new keyword ``abstract`` can be used to mark contracts as abstract. It has to be used
|
||||||
if a contract does not implement all its functions.
|
if a contract does not implement all its functions. Abstract contracts cannot be created using the ``new`` operator,
|
||||||
|
and it is not possible to generate bytecode for them during compilation.
|
||||||
|
|
||||||
* Libraries have to implement all their functions, not only the internal ones.
|
* Libraries have to implement all their functions, not only the internal ones.
|
||||||
|
|
||||||
|
@ -124,7 +124,11 @@ userDefinedTypeName
|
|||||||
: identifier ( '.' identifier )* ;
|
: identifier ( '.' identifier )* ;
|
||||||
|
|
||||||
mapping
|
mapping
|
||||||
: 'mapping' '(' (elementaryTypeName | userDefinedTypeName) '=>' typeName ')' ;
|
: 'mapping' '(' mappingKey '=>' typeName ')' ;
|
||||||
|
|
||||||
|
mappingKey
|
||||||
|
: elementaryTypeName
|
||||||
|
| userDefinedTypeName ;
|
||||||
|
|
||||||
functionTypeName
|
functionTypeName
|
||||||
: 'function' parameterList modifierList returnParameters? ;
|
: 'function' parameterList modifierList returnParameters? ;
|
||||||
@ -470,7 +474,7 @@ SingleQuotedStringCharacter
|
|||||||
: ~['\r\n\\] | ('\\' .) ;
|
: ~['\r\n\\] | ('\\' .) ;
|
||||||
|
|
||||||
VersionLiteral
|
VersionLiteral
|
||||||
: [0-9]+ '.' [0-9]+ ('.' [0-9]+)? ;
|
: [0-9]+ ( '.' [0-9]+ ('.' [0-9]+)? )? ;
|
||||||
|
|
||||||
WS
|
WS
|
||||||
: [ \t\r\n\u000C]+ -> skip ;
|
: [ \t\r\n\u000C]+ -> skip ;
|
||||||
|
@ -24,7 +24,7 @@ Function Selector
|
|||||||
=================
|
=================
|
||||||
|
|
||||||
The first four bytes of the call data for a function call specifies the function to be called. It is the
|
The first four bytes of the call data for a function call specifies the function to be called. It is the
|
||||||
first (left, high-order in big-endian) four bytes of the Keccak-256 (SHA-3) hash of the signature of
|
first (left, high-order in big-endian) four bytes of the Keccak-256 hash of the signature of
|
||||||
the function. The signature is defined as the canonical expression of the basic prototype without data
|
the function. The signature is defined as the canonical expression of the basic prototype without data
|
||||||
location specifier, i.e.
|
location specifier, i.e.
|
||||||
the function name with the parenthesised list of parameter types. Parameter types are split by a single
|
the function name with the parenthesised list of parameter types. Parameter types are split by a single
|
||||||
|
@ -1163,5 +1163,9 @@
|
|||||||
"0.6.8": {
|
"0.6.8": {
|
||||||
"bugs": [],
|
"bugs": [],
|
||||||
"released": "2020-05-14"
|
"released": "2020-05-14"
|
||||||
|
},
|
||||||
|
"0.6.9": {
|
||||||
|
"bugs": [],
|
||||||
|
"released": "2020-06-04"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -194,10 +194,10 @@ not known in the context of the class where it is used,
|
|||||||
although its type is known. This is similar for ordinary
|
although its type is known. This is similar for ordinary
|
||||||
virtual method lookup.
|
virtual method lookup.
|
||||||
|
|
||||||
.. _function-overriding:
|
|
||||||
|
|
||||||
.. index:: ! overriding;function
|
.. index:: ! overriding;function
|
||||||
|
|
||||||
|
.. _function-overriding:
|
||||||
|
|
||||||
Function Overriding
|
Function Overriding
|
||||||
===================
|
===================
|
||||||
|
|
||||||
@ -317,10 +317,10 @@ of the variable:
|
|||||||
While public state variables can override external functions, they themselves cannot
|
While public state variables can override external functions, they themselves cannot
|
||||||
be overridden.
|
be overridden.
|
||||||
|
|
||||||
.. _modifier-overriding:
|
|
||||||
|
|
||||||
.. index:: ! overriding;modifier
|
.. index:: ! overriding;modifier
|
||||||
|
|
||||||
|
.. _modifier-overriding:
|
||||||
|
|
||||||
Modifier Overriding
|
Modifier Overriding
|
||||||
===================
|
===================
|
||||||
|
|
||||||
|
@ -82,7 +82,7 @@ TypePointer const& TypeChecker::type(VariableDeclaration const& _variable) const
|
|||||||
|
|
||||||
bool TypeChecker::visit(ContractDefinition const& _contract)
|
bool TypeChecker::visit(ContractDefinition const& _contract)
|
||||||
{
|
{
|
||||||
m_scope = &_contract;
|
m_currentContract = &_contract;
|
||||||
|
|
||||||
ASTNode::listAccept(_contract.baseContracts(), *this);
|
ASTNode::listAccept(_contract.baseContracts(), *this);
|
||||||
|
|
||||||
@ -266,7 +266,7 @@ void TypeChecker::endVisit(InheritanceSpecifier const& _inheritance)
|
|||||||
auto base = dynamic_cast<ContractDefinition const*>(&dereference(_inheritance.name()));
|
auto base = dynamic_cast<ContractDefinition const*>(&dereference(_inheritance.name()));
|
||||||
solAssert(base, "Base contract not available.");
|
solAssert(base, "Base contract not available.");
|
||||||
|
|
||||||
if (m_scope->isInterface() && !base->isInterface())
|
if (m_currentContract->isInterface() && !base->isInterface())
|
||||||
m_errorReporter.typeError(6536_error, _inheritance.location(), "Interfaces can only inherit from other interfaces.");
|
m_errorReporter.typeError(6536_error, _inheritance.location(), "Interfaces can only inherit from other interfaces.");
|
||||||
|
|
||||||
if (base->isLibrary())
|
if (base->isLibrary())
|
||||||
@ -328,8 +328,6 @@ void TypeChecker::endVisit(ModifierDefinition const& _modifier)
|
|||||||
|
|
||||||
bool TypeChecker::visit(FunctionDefinition const& _function)
|
bool TypeChecker::visit(FunctionDefinition const& _function)
|
||||||
{
|
{
|
||||||
bool isLibraryFunction = _function.inContractKind() == ContractKind::Library;
|
|
||||||
|
|
||||||
if (_function.markedVirtual())
|
if (_function.markedVirtual())
|
||||||
{
|
{
|
||||||
if (_function.annotation().contract->isInterface())
|
if (_function.annotation().contract->isInterface())
|
||||||
@ -342,7 +340,7 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
|
|||||||
|
|
||||||
if (_function.isPayable())
|
if (_function.isPayable())
|
||||||
{
|
{
|
||||||
if (isLibraryFunction)
|
if (_function.libraryFunction())
|
||||||
m_errorReporter.typeError(7708_error, _function.location(), "Library functions cannot be payable.");
|
m_errorReporter.typeError(7708_error, _function.location(), "Library functions cannot be payable.");
|
||||||
if (_function.isOrdinary() && !_function.isPartOfExternalInterface())
|
if (_function.isOrdinary() && !_function.isPartOfExternalInterface())
|
||||||
m_errorReporter.typeError(5587_error, _function.location(), "Internal functions cannot be payable.");
|
m_errorReporter.typeError(5587_error, _function.location(), "Internal functions cannot be payable.");
|
||||||
@ -352,15 +350,13 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
|
|||||||
{
|
{
|
||||||
if (var.referenceLocation() != VariableDeclaration::Location::Storage)
|
if (var.referenceLocation() != VariableDeclaration::Location::Storage)
|
||||||
{
|
{
|
||||||
if (!isLibraryFunction && _function.isPublic())
|
if (!_function.libraryFunction() && _function.isPublic())
|
||||||
m_errorReporter.typeError(3442_error, var.location(), "Mapping types can only have a data location of \"storage\" and thus only be parameters or return variables for internal or library functions.");
|
m_errorReporter.typeError(3442_error, var.location(), "Mapping types can only have a data location of \"storage\" and thus only be parameters or return variables for internal or library functions.");
|
||||||
else
|
else
|
||||||
m_errorReporter.typeError(5380_error, var.location(), "Mapping types can only have a data location of \"storage\"." );
|
m_errorReporter.typeError(5380_error, var.location(), "Mapping types can only have a data location of \"storage\"." );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
solAssert(_function.libraryFunction() || !_function.isPublic(), "Mapping types for parameters or return variables can only be used in internal or library functions.");
|
||||||
solAssert(isLibraryFunction || !_function.isPublic(), "Mapping types for parameters or return variables can only be used in internal or library functions.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -368,7 +364,7 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
|
|||||||
m_errorReporter.typeError(3312_error, var.location(), "Type is required to live outside storage.");
|
m_errorReporter.typeError(3312_error, var.location(), "Type is required to live outside storage.");
|
||||||
if (_function.isPublic())
|
if (_function.isPublic())
|
||||||
{
|
{
|
||||||
auto iType = type(var)->interfaceType(isLibraryFunction);
|
auto iType = type(var)->interfaceType(_function.libraryFunction());
|
||||||
|
|
||||||
if (!iType)
|
if (!iType)
|
||||||
{
|
{
|
||||||
@ -380,7 +376,7 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
|
|||||||
if (
|
if (
|
||||||
_function.isPublic() &&
|
_function.isPublic() &&
|
||||||
!_function.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::ABIEncoderV2) &&
|
!_function.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::ABIEncoderV2) &&
|
||||||
!typeSupportedByOldABIEncoder(*type(var), isLibraryFunction)
|
!typeSupportedByOldABIEncoder(*type(var), _function.libraryFunction())
|
||||||
)
|
)
|
||||||
m_errorReporter.typeError(
|
m_errorReporter.typeError(
|
||||||
4957_error,
|
4957_error,
|
||||||
@ -419,7 +415,7 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
|
|||||||
else
|
else
|
||||||
modifiers.insert(decl);
|
modifiers.insert(decl);
|
||||||
}
|
}
|
||||||
if (m_scope->isInterface())
|
if (m_currentContract->isInterface())
|
||||||
{
|
{
|
||||||
if (_function.isImplemented())
|
if (_function.isImplemented())
|
||||||
m_errorReporter.typeError(4726_error, _function.location(), "Functions in interfaces cannot have an implementation.");
|
m_errorReporter.typeError(4726_error, _function.location(), "Functions in interfaces cannot have an implementation.");
|
||||||
@ -430,14 +426,14 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
|
|||||||
if (_function.isConstructor())
|
if (_function.isConstructor())
|
||||||
m_errorReporter.typeError(6482_error, _function.location(), "Constructor cannot be defined in interfaces.");
|
m_errorReporter.typeError(6482_error, _function.location(), "Constructor cannot be defined in interfaces.");
|
||||||
}
|
}
|
||||||
else if (m_scope->contractKind() == ContractKind::Library)
|
else if (m_currentContract->contractKind() == ContractKind::Library)
|
||||||
if (_function.isConstructor())
|
if (_function.isConstructor())
|
||||||
m_errorReporter.typeError(7634_error, _function.location(), "Constructor cannot be defined in libraries.");
|
m_errorReporter.typeError(7634_error, _function.location(), "Constructor cannot be defined in libraries.");
|
||||||
if (_function.isImplemented())
|
if (_function.isImplemented())
|
||||||
_function.body().accept(*this);
|
_function.body().accept(*this);
|
||||||
else if (_function.isConstructor())
|
else if (_function.isConstructor())
|
||||||
m_errorReporter.typeError(5700_error, _function.location(), "Constructor must be implemented if declared.");
|
m_errorReporter.typeError(5700_error, _function.location(), "Constructor must be implemented if declared.");
|
||||||
else if (isLibraryFunction)
|
else if (_function.libraryFunction())
|
||||||
m_errorReporter.typeError(9231_error, _function.location(), "Library functions must be implemented if declared.");
|
m_errorReporter.typeError(9231_error, _function.location(), "Library functions must be implemented if declared.");
|
||||||
else if (!_function.virtualSemantics())
|
else if (!_function.virtualSemantics())
|
||||||
m_errorReporter.typeError(5424_error, _function.location(), "Functions without implementation must be marked virtual.");
|
m_errorReporter.typeError(5424_error, _function.location(), "Functions without implementation must be marked virtual.");
|
||||||
@ -1796,7 +1792,7 @@ void TypeChecker::typeCheckFunctionCall(
|
|||||||
if (_functionType->kind() == FunctionType::Kind::Declaration)
|
if (_functionType->kind() == FunctionType::Kind::Declaration)
|
||||||
{
|
{
|
||||||
if (
|
if (
|
||||||
m_scope->derivesFrom(*_functionType->declaration().annotation().contract) &&
|
m_currentContract->derivesFrom(*_functionType->declaration().annotation().contract) &&
|
||||||
!dynamic_cast<FunctionDefinition const&>(_functionType->declaration()).isImplemented()
|
!dynamic_cast<FunctionDefinition const&>(_functionType->declaration()).isImplemented()
|
||||||
)
|
)
|
||||||
m_errorReporter.typeError(
|
m_errorReporter.typeError(
|
||||||
@ -1832,7 +1828,7 @@ void TypeChecker::typeCheckFallbackFunction(FunctionDefinition const& _function)
|
|||||||
{
|
{
|
||||||
solAssert(_function.isFallback(), "");
|
solAssert(_function.isFallback(), "");
|
||||||
|
|
||||||
if (_function.inContractKind() == ContractKind::Library)
|
if (_function.libraryFunction())
|
||||||
m_errorReporter.typeError(5982_error, _function.location(), "Libraries cannot have fallback functions.");
|
m_errorReporter.typeError(5982_error, _function.location(), "Libraries cannot have fallback functions.");
|
||||||
if (_function.stateMutability() != StateMutability::NonPayable && _function.stateMutability() != StateMutability::Payable)
|
if (_function.stateMutability() != StateMutability::NonPayable && _function.stateMutability() != StateMutability::Payable)
|
||||||
m_errorReporter.typeError(
|
m_errorReporter.typeError(
|
||||||
@ -1859,7 +1855,7 @@ void TypeChecker::typeCheckReceiveFunction(FunctionDefinition const& _function)
|
|||||||
{
|
{
|
||||||
solAssert(_function.isReceive(), "");
|
solAssert(_function.isReceive(), "");
|
||||||
|
|
||||||
if (_function.inContractKind() == ContractKind::Library)
|
if (_function.libraryFunction())
|
||||||
m_errorReporter.typeError(4549_error, _function.location(), "Libraries cannot have receive ether functions.");
|
m_errorReporter.typeError(4549_error, _function.location(), "Libraries cannot have receive ether functions.");
|
||||||
|
|
||||||
if (_function.stateMutability() != StateMutability::Payable)
|
if (_function.stateMutability() != StateMutability::Payable)
|
||||||
@ -1918,7 +1914,7 @@ void TypeChecker::typeCheckABIEncodeFunctions(
|
|||||||
bool const isPacked = _functionType->kind() == FunctionType::Kind::ABIEncodePacked;
|
bool const isPacked = _functionType->kind() == FunctionType::Kind::ABIEncodePacked;
|
||||||
solAssert(_functionType->padArguments() != isPacked, "ABI function with unexpected padding");
|
solAssert(_functionType->padArguments() != isPacked, "ABI function with unexpected padding");
|
||||||
|
|
||||||
bool const abiEncoderV2 = m_scope->sourceUnit().annotation().experimentalFeatures.count(
|
bool const abiEncoderV2 = m_currentContract->sourceUnit().annotation().experimentalFeatures.count(
|
||||||
ExperimentalFeature::ABIEncoderV2
|
ExperimentalFeature::ABIEncoderV2
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -2318,7 +2314,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
|
|||||||
case FunctionType::Kind::ABIDecode:
|
case FunctionType::Kind::ABIDecode:
|
||||||
{
|
{
|
||||||
bool const abiEncoderV2 =
|
bool const abiEncoderV2 =
|
||||||
m_scope->sourceUnit().annotation().experimentalFeatures.count(
|
m_currentContract->sourceUnit().annotation().experimentalFeatures.count(
|
||||||
ExperimentalFeature::ABIEncoderV2
|
ExperimentalFeature::ABIEncoderV2
|
||||||
);
|
);
|
||||||
returnTypes = typeCheckABIDecodeAndRetrieveReturnType(_functionCall, abiEncoderV2);
|
returnTypes = typeCheckABIDecodeAndRetrieveReturnType(_functionCall, abiEncoderV2);
|
||||||
@ -2514,13 +2510,13 @@ void TypeChecker::endVisit(NewExpression const& _newExpression)
|
|||||||
if (contract->abstract())
|
if (contract->abstract())
|
||||||
m_errorReporter.typeError(4614_error, _newExpression.location(), "Cannot instantiate an abstract contract.");
|
m_errorReporter.typeError(4614_error, _newExpression.location(), "Cannot instantiate an abstract contract.");
|
||||||
|
|
||||||
solAssert(!!m_scope, "");
|
solAssert(!!m_currentContract, "");
|
||||||
m_scope->annotation().contractDependencies.insert(contract);
|
m_currentContract->annotation().contractDependencies.insert(contract);
|
||||||
solAssert(
|
solAssert(
|
||||||
!contract->annotation().linearizedBaseContracts.empty(),
|
!contract->annotation().linearizedBaseContracts.empty(),
|
||||||
"Linearized base contracts not yet available."
|
"Linearized base contracts not yet available."
|
||||||
);
|
);
|
||||||
if (contractDependenciesAreCyclic(*m_scope))
|
if (contractDependenciesAreCyclic(*m_currentContract))
|
||||||
m_errorReporter.typeError(
|
m_errorReporter.typeError(
|
||||||
4579_error,
|
4579_error,
|
||||||
_newExpression.location(),
|
_newExpression.location(),
|
||||||
@ -2567,7 +2563,7 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
|
|||||||
|
|
||||||
// Retrieve the types of the arguments if this is used to call a function.
|
// Retrieve the types of the arguments if this is used to call a function.
|
||||||
auto const& arguments = _memberAccess.annotation().arguments;
|
auto const& arguments = _memberAccess.annotation().arguments;
|
||||||
MemberList::MemberMap possibleMembers = exprType->members(m_scope).membersByName(memberName);
|
MemberList::MemberMap possibleMembers = exprType->members(m_currentContract).membersByName(memberName);
|
||||||
size_t const initialMemberCount = possibleMembers.size();
|
size_t const initialMemberCount = possibleMembers.size();
|
||||||
if (initialMemberCount > 1 && arguments)
|
if (initialMemberCount > 1 && arguments)
|
||||||
{
|
{
|
||||||
@ -2593,7 +2589,7 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
|
|||||||
DataLocation::Storage,
|
DataLocation::Storage,
|
||||||
exprType
|
exprType
|
||||||
);
|
);
|
||||||
if (!storageType->members(m_scope).membersByName(memberName).empty())
|
if (!storageType->members(m_currentContract).membersByName(memberName).empty())
|
||||||
m_errorReporter.fatalTypeError(
|
m_errorReporter.fatalTypeError(
|
||||||
4994_error,
|
4994_error,
|
||||||
_memberAccess.location(),
|
_memberAccess.location(),
|
||||||
@ -2749,7 +2745,7 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
|
|||||||
{
|
{
|
||||||
annotation.isPure = true;
|
annotation.isPure = true;
|
||||||
ContractType const& accessedContractType = dynamic_cast<ContractType const&>(*magicType->typeArgument());
|
ContractType const& accessedContractType = dynamic_cast<ContractType const&>(*magicType->typeArgument());
|
||||||
m_scope->annotation().contractDependencies.insert(&accessedContractType.contractDefinition());
|
m_currentContract->annotation().contractDependencies.insert(&accessedContractType.contractDefinition());
|
||||||
|
|
||||||
if (
|
if (
|
||||||
memberName == "runtimeCode" &&
|
memberName == "runtimeCode" &&
|
||||||
@ -2761,7 +2757,7 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
|
|||||||
"\"runtimeCode\" is not available for contracts containing immutable variables."
|
"\"runtimeCode\" is not available for contracts containing immutable variables."
|
||||||
);
|
);
|
||||||
|
|
||||||
if (contractDependenciesAreCyclic(*m_scope))
|
if (contractDependenciesAreCyclic(*m_currentContract))
|
||||||
m_errorReporter.typeError(
|
m_errorReporter.typeError(
|
||||||
4224_error,
|
4224_error,
|
||||||
_memberAccess.location(),
|
_memberAccess.location(),
|
||||||
|
@ -165,7 +165,7 @@ private:
|
|||||||
/// Runs type checks on @a _expression to infer its type and then checks that it is an LValue.
|
/// Runs type checks on @a _expression to infer its type and then checks that it is an LValue.
|
||||||
void requireLValue(Expression const& _expression, bool _ordinaryAssignment);
|
void requireLValue(Expression const& _expression, bool _ordinaryAssignment);
|
||||||
|
|
||||||
ContractDefinition const* m_scope = nullptr;
|
ContractDefinition const* m_currentContract = nullptr;
|
||||||
|
|
||||||
langutil::EVMVersion m_evmVersion;
|
langutil::EVMVersion m_evmVersion;
|
||||||
|
|
||||||
|
@ -276,7 +276,7 @@ void ViewPureChecker::reportMutability(
|
|||||||
{
|
{
|
||||||
// We do not warn for library functions because they cannot be payable anyway.
|
// We do not warn for library functions because they cannot be payable anyway.
|
||||||
// Also internal functions should be allowed to use `msg.value`.
|
// Also internal functions should be allowed to use `msg.value`.
|
||||||
if (m_currentFunction->isPublic() && m_currentFunction->inContractKind() != ContractKind::Library)
|
if (m_currentFunction->isPublic() && !m_currentFunction->libraryFunction())
|
||||||
{
|
{
|
||||||
if (_nestedLocation)
|
if (_nestedLocation)
|
||||||
m_errorReporter.typeError(
|
m_errorReporter.typeError(
|
||||||
|
@ -287,11 +287,11 @@ TypeDeclarationAnnotation& EnumDefinition::annotation() const
|
|||||||
return initAnnotation<TypeDeclarationAnnotation>();
|
return initAnnotation<TypeDeclarationAnnotation>();
|
||||||
}
|
}
|
||||||
|
|
||||||
ContractKind FunctionDefinition::inContractKind() const
|
bool FunctionDefinition::libraryFunction() const
|
||||||
{
|
{
|
||||||
auto contractDef = dynamic_cast<ContractDefinition const*>(scope());
|
if (auto const* contractDef = dynamic_cast<ContractDefinition const*>(scope()))
|
||||||
solAssert(contractDef, "Enclosing Scope of FunctionDefinition was not set.");
|
return contractDef->isLibrary();
|
||||||
return contractDef->contractKind();
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
FunctionTypePointer FunctionDefinition::functionType(bool _internal) const
|
FunctionTypePointer FunctionDefinition::functionType(bool _internal) const
|
||||||
|
@ -799,6 +799,7 @@ public:
|
|||||||
void accept(ASTConstVisitor& _visitor) const override;
|
void accept(ASTConstVisitor& _visitor) const override;
|
||||||
|
|
||||||
StateMutability stateMutability() const { return m_stateMutability; }
|
StateMutability stateMutability() const { return m_stateMutability; }
|
||||||
|
bool libraryFunction() const;
|
||||||
bool isOrdinary() const { return m_kind == Token::Function; }
|
bool isOrdinary() const { return m_kind == Token::Function; }
|
||||||
bool isConstructor() const { return m_kind == Token::Constructor; }
|
bool isConstructor() const { return m_kind == Token::Constructor; }
|
||||||
bool isFallback() const { return m_kind == Token::Fallback; }
|
bool isFallback() const { return m_kind == Token::Fallback; }
|
||||||
@ -825,8 +826,6 @@ public:
|
|||||||
/// @returns the external identifier of this function (the hash of the signature) as a hex string.
|
/// @returns the external identifier of this function (the hash of the signature) as a hex string.
|
||||||
std::string externalIdentifierHex() const;
|
std::string externalIdentifierHex() const;
|
||||||
|
|
||||||
ContractKind inContractKind() const;
|
|
||||||
|
|
||||||
TypePointer type() const override;
|
TypePointer type() const override;
|
||||||
TypePointer typeViaContractName() const override;
|
TypePointer typeViaContractName() const override;
|
||||||
|
|
||||||
|
@ -1730,7 +1730,7 @@ void IRGeneratorForStatements::endVisit(IndexAccess const& _indexAccess)
|
|||||||
|
|
||||||
setLValue(_indexAccess, IRLValue{
|
setLValue(_indexAccess, IRLValue{
|
||||||
*arrayType.baseType(),
|
*arrayType.baseType(),
|
||||||
IRLValue::Memory{memAddress}
|
IRLValue::Memory{memAddress, arrayType.isByteArray()}
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1763,7 +1763,23 @@ void IRGeneratorForStatements::endVisit(IndexAccess const& _indexAccess)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (baseType.category() == Type::Category::FixedBytes)
|
else if (baseType.category() == Type::Category::FixedBytes)
|
||||||
solUnimplementedAssert(false, "");
|
{
|
||||||
|
auto const& fixedBytesType = dynamic_cast<FixedBytesType const&>(baseType);
|
||||||
|
solAssert(_indexAccess.indexExpression(), "Index expression expected.");
|
||||||
|
|
||||||
|
IRVariable index{m_context.newYulVariable(), *TypeProvider::uint256()};
|
||||||
|
define(index, *_indexAccess.indexExpression());
|
||||||
|
m_code << Whiskers(R"(
|
||||||
|
if iszero(lt(<index>, <length>)) { invalid() }
|
||||||
|
let <result> := <shl248>(byte(<index>, <array>))
|
||||||
|
)")
|
||||||
|
("index", index.name())
|
||||||
|
("length", to_string(fixedBytesType.numBytes()))
|
||||||
|
("array", IRVariable(_indexAccess.baseExpression()).name())
|
||||||
|
("shl248", m_utils.shiftLeftFunction(256 - 8))
|
||||||
|
("result", IRVariable(_indexAccess).name())
|
||||||
|
.render();
|
||||||
|
}
|
||||||
else if (baseType.category() == Type::Category::TypeType)
|
else if (baseType.category() == Type::Category::TypeType)
|
||||||
{
|
{
|
||||||
solAssert(baseType.sizeOnStack() == 0, "");
|
solAssert(baseType.sizeOnStack() == 0, "");
|
||||||
|
@ -604,7 +604,11 @@ void BMC::checkUnderflow(BMCVerificationTarget& _target, smtutil::Expression con
|
|||||||
_target.type == VerificationTarget::Type::UnderOverflow,
|
_target.type == VerificationTarget::Type::UnderOverflow,
|
||||||
""
|
""
|
||||||
);
|
);
|
||||||
auto intType = dynamic_cast<IntegerType const*>(_target.expression->annotation().type);
|
IntegerType const* intType = nullptr;
|
||||||
|
if (auto const* type = dynamic_cast<IntegerType const*>(_target.expression->annotation().type))
|
||||||
|
intType = type;
|
||||||
|
else
|
||||||
|
intType = TypeProvider::uint256();
|
||||||
solAssert(intType, "");
|
solAssert(intType, "");
|
||||||
checkCondition(
|
checkCondition(
|
||||||
_target.constraints && _constraints && _target.value < smt::minValue(*intType),
|
_target.constraints && _constraints && _target.value < smt::minValue(*intType),
|
||||||
@ -626,7 +630,12 @@ void BMC::checkOverflow(BMCVerificationTarget& _target, smtutil::Expression cons
|
|||||||
_target.type == VerificationTarget::Type::UnderOverflow,
|
_target.type == VerificationTarget::Type::UnderOverflow,
|
||||||
""
|
""
|
||||||
);
|
);
|
||||||
auto intType = dynamic_cast<IntegerType const*>(_target.expression->annotation().type);
|
IntegerType const* intType = nullptr;
|
||||||
|
if (auto const* type = dynamic_cast<IntegerType const*>(_target.expression->annotation().type))
|
||||||
|
intType = type;
|
||||||
|
else
|
||||||
|
intType = TypeProvider::uint256();
|
||||||
|
|
||||||
solAssert(intType, "");
|
solAssert(intType, "");
|
||||||
checkCondition(
|
checkCondition(
|
||||||
_target.constraints && _constraints && _target.value > smt::maxValue(*intType),
|
_target.constraints && _constraints && _target.value > smt::maxValue(*intType),
|
||||||
|
@ -385,44 +385,22 @@ void SMTEncoder::endVisit(Assignment const& _assignment)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
auto const& type = _assignment.annotation().type;
|
if (dynamic_cast<TupleType const*>(_assignment.rightHandSide().annotation().type))
|
||||||
vector<smtutil::Expression> rightArguments;
|
tupleAssignment(_assignment.leftHandSide(), _assignment.rightHandSide());
|
||||||
if (auto const* tupleTypeRight = dynamic_cast<TupleType const*>(_assignment.rightHandSide().annotation().type))
|
|
||||||
{
|
|
||||||
auto symbTupleLeft = dynamic_pointer_cast<smt::SymbolicTupleVariable>(m_context.expression(_assignment.leftHandSide()));
|
|
||||||
solAssert(symbTupleLeft, "");
|
|
||||||
auto symbTupleRight = dynamic_pointer_cast<smt::SymbolicTupleVariable>(m_context.expression(_assignment.rightHandSide()));
|
|
||||||
solAssert(symbTupleRight, "");
|
|
||||||
|
|
||||||
auto const& leftComponents = symbTupleLeft->components();
|
|
||||||
auto const& rightComponents = symbTupleRight->components();
|
|
||||||
solAssert(leftComponents.size() == rightComponents.size(), "");
|
|
||||||
|
|
||||||
auto tupleTypeLeft = dynamic_cast<TupleType const*>(_assignment.leftHandSide().annotation().type);
|
|
||||||
solAssert(tupleTypeLeft, "");
|
|
||||||
solAssert(tupleTypeLeft->components().size() == leftComponents.size(), "");
|
|
||||||
auto const& typesLeft = tupleTypeLeft->components();
|
|
||||||
|
|
||||||
solAssert(tupleTypeRight->components().size() == rightComponents.size(), "");
|
|
||||||
auto const& typesRight = tupleTypeRight->components();
|
|
||||||
|
|
||||||
for (unsigned i = 0; i < rightComponents.size(); ++i)
|
|
||||||
rightArguments.push_back(symbTupleRight->component(i, typesRight.at(i), typesLeft.at(i)));
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
auto const& type = _assignment.annotation().type;
|
||||||
auto rightHandSide = compoundOps.count(op) ?
|
auto rightHandSide = compoundOps.count(op) ?
|
||||||
compoundAssignment(_assignment) :
|
compoundAssignment(_assignment) :
|
||||||
expr(_assignment.rightHandSide(), type);
|
expr(_assignment.rightHandSide(), type);
|
||||||
defineExpr(_assignment, rightHandSide);
|
defineExpr(_assignment, rightHandSide);
|
||||||
rightArguments.push_back(expr(_assignment, type));
|
assignment(
|
||||||
|
_assignment.leftHandSide(),
|
||||||
|
expr(_assignment, type),
|
||||||
|
type,
|
||||||
|
_assignment.location()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
assignment(
|
|
||||||
_assignment.leftHandSide(),
|
|
||||||
rightArguments,
|
|
||||||
type,
|
|
||||||
_assignment.location()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1023,38 +1001,7 @@ void SMTEncoder::arrayIndexAssignment(Expression const& _expr, smtutil::Expressi
|
|||||||
solAssert(varDecl, "");
|
solAssert(varDecl, "");
|
||||||
|
|
||||||
if (varDecl->hasReferenceOrMappingType())
|
if (varDecl->hasReferenceOrMappingType())
|
||||||
m_context.resetVariables([&](VariableDeclaration const& _var) {
|
resetReferences(*varDecl);
|
||||||
if (_var == *varDecl)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// If both are state variables no need to clear knowledge.
|
|
||||||
if (_var.isStateVariable() && varDecl->isStateVariable())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
TypePointer prefix = _var.type();
|
|
||||||
TypePointer originalType = typeWithoutPointer(varDecl->type());
|
|
||||||
while (
|
|
||||||
prefix->category() == Type::Category::Mapping ||
|
|
||||||
prefix->category() == Type::Category::Array
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (*originalType == *typeWithoutPointer(prefix))
|
|
||||||
return true;
|
|
||||||
if (prefix->category() == Type::Category::Mapping)
|
|
||||||
{
|
|
||||||
auto mapPrefix = dynamic_cast<MappingType const*>(prefix);
|
|
||||||
solAssert(mapPrefix, "");
|
|
||||||
prefix = mapPrefix->valueType();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
auto arrayPrefix = dynamic_cast<ArrayType const*>(prefix);
|
|
||||||
solAssert(arrayPrefix, "");
|
|
||||||
prefix = arrayPrefix->baseType();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
|
|
||||||
auto symbArray = dynamic_pointer_cast<smt::SymbolicArrayVariable>(m_context.variable(*varDecl));
|
auto symbArray = dynamic_pointer_cast<smt::SymbolicArrayVariable>(m_context.variable(*varDecl));
|
||||||
smtutil::Expression store = smtutil::Expression::store(
|
smtutil::Expression store = smtutil::Expression::store(
|
||||||
@ -1160,6 +1107,8 @@ void SMTEncoder::arrayPushPopAssign(Expression const& _expr, smtutil::Expression
|
|||||||
{
|
{
|
||||||
auto varDecl = identifierToVariable(*id);
|
auto varDecl = identifierToVariable(*id);
|
||||||
solAssert(varDecl, "");
|
solAssert(varDecl, "");
|
||||||
|
if (varDecl->hasReferenceOrMappingType())
|
||||||
|
resetReferences(*varDecl);
|
||||||
m_context.addAssertion(m_context.newValue(*varDecl) == _array);
|
m_context.addAssertion(m_context.newValue(*varDecl) == _array);
|
||||||
}
|
}
|
||||||
else if (auto const* indexAccess = dynamic_cast<IndexAccess const*>(&_expr))
|
else if (auto const* indexAccess = dynamic_cast<IndexAccess const*>(&_expr))
|
||||||
@ -1213,7 +1162,7 @@ void SMTEncoder::arithmeticOperation(BinaryOperation const& _op)
|
|||||||
{
|
{
|
||||||
auto type = _op.annotation().commonType;
|
auto type = _op.annotation().commonType;
|
||||||
solAssert(type, "");
|
solAssert(type, "");
|
||||||
if (type->category() == Type::Category::Integer)
|
if (type->category() == Type::Category::Integer || type->category() == Type::Category::FixedPoint)
|
||||||
{
|
{
|
||||||
switch (_op.getOperator())
|
switch (_op.getOperator())
|
||||||
{
|
{
|
||||||
@ -1266,13 +1215,21 @@ pair<smtutil::Expression, smtutil::Expression> SMTEncoder::arithmeticOperation(
|
|||||||
};
|
};
|
||||||
solAssert(validOperators.count(_op), "");
|
solAssert(validOperators.count(_op), "");
|
||||||
solAssert(_commonType, "");
|
solAssert(_commonType, "");
|
||||||
solAssert(_commonType->category() == Type::Category::Integer, "");
|
solAssert(
|
||||||
|
_commonType->category() == Type::Category::Integer || _commonType->category() == Type::Category::FixedPoint,
|
||||||
|
""
|
||||||
|
);
|
||||||
|
|
||||||
|
IntegerType const* intType = nullptr;
|
||||||
|
if (auto type = dynamic_cast<IntegerType const*>(_commonType))
|
||||||
|
intType = type;
|
||||||
|
else
|
||||||
|
intType = TypeProvider::uint256();
|
||||||
|
|
||||||
auto const& intType = dynamic_cast<IntegerType const&>(*_commonType);
|
|
||||||
smtutil::Expression valueNoMod(
|
smtutil::Expression valueNoMod(
|
||||||
_op == Token::Add ? _left + _right :
|
_op == Token::Add ? _left + _right :
|
||||||
_op == Token::Sub ? _left - _right :
|
_op == Token::Sub ? _left - _right :
|
||||||
_op == Token::Div ? division(_left, _right, intType) :
|
_op == Token::Div ? division(_left, _right, *intType) :
|
||||||
_op == Token::Mul ? _left * _right :
|
_op == Token::Mul ? _left * _right :
|
||||||
/*_op == Token::Mod*/ _left % _right
|
/*_op == Token::Mod*/ _left % _right
|
||||||
);
|
);
|
||||||
@ -1280,20 +1237,23 @@ pair<smtutil::Expression, smtutil::Expression> SMTEncoder::arithmeticOperation(
|
|||||||
if (_op == Token::Div || _op == Token::Mod)
|
if (_op == Token::Div || _op == Token::Mod)
|
||||||
m_context.addAssertion(_right != 0);
|
m_context.addAssertion(_right != 0);
|
||||||
|
|
||||||
smtutil::Expression intValueRange = (0 - smt::minValue(intType)) + smt::maxValue(intType) + 1;
|
auto symbMin = smt::minValue(*intType);
|
||||||
|
auto symbMax = smt::maxValue(*intType);
|
||||||
|
|
||||||
|
smtutil::Expression intValueRange = (0 - symbMin) + symbMax + 1;
|
||||||
auto value = smtutil::Expression::ite(
|
auto value = smtutil::Expression::ite(
|
||||||
valueNoMod > smt::maxValue(intType),
|
valueNoMod > symbMax,
|
||||||
valueNoMod % intValueRange,
|
valueNoMod % intValueRange,
|
||||||
smtutil::Expression::ite(
|
smtutil::Expression::ite(
|
||||||
valueNoMod < smt::minValue(intType),
|
valueNoMod < symbMin,
|
||||||
valueNoMod % intValueRange,
|
valueNoMod % intValueRange,
|
||||||
valueNoMod
|
valueNoMod
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (intType.isSigned())
|
if (intType->isSigned())
|
||||||
value = smtutil::Expression::ite(
|
value = smtutil::Expression::ite(
|
||||||
value > smt::maxValue(intType),
|
value > symbMax,
|
||||||
value - intValueRange,
|
value - intValueRange,
|
||||||
value
|
value
|
||||||
);
|
);
|
||||||
@ -1422,11 +1382,16 @@ smtutil::Expression SMTEncoder::division(smtutil::Expression _left, smtutil::Exp
|
|||||||
|
|
||||||
void SMTEncoder::assignment(
|
void SMTEncoder::assignment(
|
||||||
Expression const& _left,
|
Expression const& _left,
|
||||||
vector<smtutil::Expression> const& _right,
|
smtutil::Expression const& _right,
|
||||||
TypePointer const& _type,
|
TypePointer const& _type,
|
||||||
langutil::SourceLocation const& _location
|
langutil::SourceLocation const& _location
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
solAssert(
|
||||||
|
_left.annotation().type->category() != Type::Category::Tuple,
|
||||||
|
"Tuple assignments should be handled by tupleAssignment."
|
||||||
|
);
|
||||||
|
|
||||||
if (!smt::isSupportedType(_type->category()))
|
if (!smt::isSupportedType(_type->category()))
|
||||||
{
|
{
|
||||||
// Give it a new index anyway to keep the SSA scheme sound.
|
// Give it a new index anyway to keep the SSA scheme sound.
|
||||||
@ -1440,26 +1405,9 @@ void SMTEncoder::assignment(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
else if (auto varDecl = identifierToVariable(_left))
|
else if (auto varDecl = identifierToVariable(_left))
|
||||||
{
|
assignment(*varDecl, _right);
|
||||||
solAssert(_right.size() == 1, "");
|
|
||||||
assignment(*varDecl, _right.front());
|
|
||||||
}
|
|
||||||
else if (dynamic_cast<IndexAccess const*>(&_left))
|
else if (dynamic_cast<IndexAccess const*>(&_left))
|
||||||
{
|
arrayIndexAssignment(_left, _right);
|
||||||
solAssert(_right.size() == 1, "");
|
|
||||||
arrayIndexAssignment(_left, _right.front());
|
|
||||||
}
|
|
||||||
else if (auto tuple = dynamic_cast<TupleExpression const*>(&_left))
|
|
||||||
{
|
|
||||||
auto const& components = tuple->components();
|
|
||||||
if (!_right.empty())
|
|
||||||
{
|
|
||||||
solAssert(_right.size() == components.size(), "");
|
|
||||||
for (unsigned i = 0; i < _right.size(); ++i)
|
|
||||||
if (auto component = components.at(i))
|
|
||||||
assignment(*component, {_right.at(i)}, component->annotation().type, component->location());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
m_errorReporter.warning(
|
m_errorReporter.warning(
|
||||||
8182_error,
|
8182_error,
|
||||||
@ -1468,6 +1416,52 @@ void SMTEncoder::assignment(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SMTEncoder::tupleAssignment(Expression const& _left, Expression const& _right)
|
||||||
|
{
|
||||||
|
auto lTuple = dynamic_cast<TupleExpression const*>(&_left);
|
||||||
|
solAssert(lTuple, "");
|
||||||
|
|
||||||
|
auto const& lComponents = lTuple->components();
|
||||||
|
|
||||||
|
// If both sides are tuple expressions, we individually and potentially
|
||||||
|
// recursively assign each pair of components.
|
||||||
|
// This is because of potential type conversion.
|
||||||
|
if (auto rTuple = dynamic_cast<TupleExpression const*>(&_right))
|
||||||
|
{
|
||||||
|
auto const& rComponents = rTuple->components();
|
||||||
|
solAssert(lComponents.size() == rComponents.size(), "");
|
||||||
|
for (unsigned i = 0; i < lComponents.size(); ++i)
|
||||||
|
{
|
||||||
|
if (!lComponents.at(i) || !rComponents.at(i))
|
||||||
|
continue;
|
||||||
|
auto const& lExpr = *lComponents.at(i);
|
||||||
|
auto const& rExpr = *rComponents.at(i);
|
||||||
|
if (lExpr.annotation().type->category() == Type::Category::Tuple)
|
||||||
|
tupleAssignment(lExpr, rExpr);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto type = lExpr.annotation().type;
|
||||||
|
assignment(lExpr, expr(rExpr, type), type, lExpr.location());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto rType = dynamic_cast<TupleType const*>(_right.annotation().type);
|
||||||
|
solAssert(rType, "");
|
||||||
|
|
||||||
|
auto const& rComponents = rType->components();
|
||||||
|
solAssert(lComponents.size() == rComponents.size(), "");
|
||||||
|
|
||||||
|
auto symbRight = expr(_right);
|
||||||
|
solAssert(symbRight.sort->kind == smtutil::Kind::Tuple, "");
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < lComponents.size(); ++i)
|
||||||
|
if (auto component = lComponents.at(i); component && rComponents.at(i))
|
||||||
|
assignment(*component, smtutil::Expression::tuple_get(symbRight, i), component->annotation().type, component->location());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
smtutil::Expression SMTEncoder::compoundAssignment(Assignment const& _assignment)
|
smtutil::Expression SMTEncoder::compoundAssignment(Assignment const& _assignment)
|
||||||
{
|
{
|
||||||
static map<Token, Token> const compoundToArithmetic{
|
static map<Token, Token> const compoundToArithmetic{
|
||||||
@ -1618,6 +1612,42 @@ void SMTEncoder::resetStateVariables()
|
|||||||
m_context.resetVariables([&](VariableDeclaration const& _variable) { return _variable.isStateVariable(); });
|
m_context.resetVariables([&](VariableDeclaration const& _variable) { return _variable.isStateVariable(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SMTEncoder::resetReferences(VariableDeclaration const& _varDecl)
|
||||||
|
{
|
||||||
|
m_context.resetVariables([&](VariableDeclaration const& _var) {
|
||||||
|
if (_var == _varDecl)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// If both are state variables no need to clear knowledge.
|
||||||
|
if (_var.isStateVariable() && _varDecl.isStateVariable())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
TypePointer prefix = _var.type();
|
||||||
|
TypePointer originalType = typeWithoutPointer(_varDecl.type());
|
||||||
|
while (
|
||||||
|
prefix->category() == Type::Category::Mapping ||
|
||||||
|
prefix->category() == Type::Category::Array
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (*originalType == *typeWithoutPointer(prefix))
|
||||||
|
return true;
|
||||||
|
if (prefix->category() == Type::Category::Mapping)
|
||||||
|
{
|
||||||
|
auto mapPrefix = dynamic_cast<MappingType const*>(prefix);
|
||||||
|
solAssert(mapPrefix, "");
|
||||||
|
prefix = mapPrefix->valueType();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto arrayPrefix = dynamic_cast<ArrayType const*>(prefix);
|
||||||
|
solAssert(arrayPrefix, "");
|
||||||
|
prefix = arrayPrefix->baseType();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
TypePointer SMTEncoder::typeWithoutPointer(TypePointer const& _type)
|
TypePointer SMTEncoder::typeWithoutPointer(TypePointer const& _type)
|
||||||
{
|
{
|
||||||
if (auto refType = dynamic_cast<ReferenceType const*>(_type))
|
if (auto refType = dynamic_cast<ReferenceType const*>(_type))
|
||||||
|
@ -157,10 +157,12 @@ protected:
|
|||||||
/// Will also be used for assignments of tuple components.
|
/// Will also be used for assignments of tuple components.
|
||||||
void assignment(
|
void assignment(
|
||||||
Expression const& _left,
|
Expression const& _left,
|
||||||
std::vector<smtutil::Expression> const& _right,
|
smtutil::Expression const& _right,
|
||||||
TypePointer const& _type,
|
TypePointer const& _type,
|
||||||
langutil::SourceLocation const& _location
|
langutil::SourceLocation const& _location
|
||||||
);
|
);
|
||||||
|
/// Handle assignments between tuples.
|
||||||
|
void tupleAssignment(Expression const& _left, Expression const& _right);
|
||||||
/// Computes the right hand side of a compound assignment.
|
/// Computes the right hand side of a compound assignment.
|
||||||
smtutil::Expression compoundAssignment(Assignment const& _assignment);
|
smtutil::Expression compoundAssignment(Assignment const& _assignment);
|
||||||
|
|
||||||
@ -181,6 +183,9 @@ protected:
|
|||||||
void initializeLocalVariables(FunctionDefinition const& _function);
|
void initializeLocalVariables(FunctionDefinition const& _function);
|
||||||
void initializeFunctionCallParameters(CallableDeclaration const& _function, std::vector<smtutil::Expression> const& _callArgs);
|
void initializeFunctionCallParameters(CallableDeclaration const& _function, std::vector<smtutil::Expression> const& _callArgs);
|
||||||
void resetStateVariables();
|
void resetStateVariables();
|
||||||
|
/// Resets all references/pointers that have the same type or have
|
||||||
|
/// a subexpression of the same type as _varDecl.
|
||||||
|
void resetReferences(VariableDeclaration const& _varDecl);
|
||||||
/// @returns the type without storage pointer information if it has it.
|
/// @returns the type without storage pointer information if it has it.
|
||||||
TypePointer typeWithoutPointer(TypePointer const& _type);
|
TypePointer typeWithoutPointer(TypePointer const& _type);
|
||||||
|
|
||||||
|
@ -53,9 +53,6 @@ string solidity::util::toHex(uint8_t _data, HexCase _case)
|
|||||||
|
|
||||||
string solidity::util::toHex(bytes const& _data, HexPrefix _prefix, HexCase _case)
|
string solidity::util::toHex(bytes const& _data, HexPrefix _prefix, HexCase _case)
|
||||||
{
|
{
|
||||||
if (_data.empty())
|
|
||||||
return {};
|
|
||||||
|
|
||||||
std::string ret(_data.size() * 2 + (_prefix == HexPrefix::Add ? 2 : 0), 0);
|
std::string ret(_data.size() * 2 + (_prefix == HexPrefix::Add ? 2 : 0), 0);
|
||||||
|
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
|
@ -359,6 +359,13 @@ void AsmAnalyzer::operator()(Switch const& _switch)
|
|||||||
{
|
{
|
||||||
yulAssert(_switch.expression, "");
|
yulAssert(_switch.expression, "");
|
||||||
|
|
||||||
|
if (_switch.cases.size() == 1 && !_switch.cases[0].value)
|
||||||
|
m_errorReporter.warning(
|
||||||
|
9592_error,
|
||||||
|
_switch.location,
|
||||||
|
"\"switch\" statement with only a default case."
|
||||||
|
);
|
||||||
|
|
||||||
YulString valueType = expectExpression(*_switch.expression);
|
YulString valueType = expectExpression(*_switch.expression);
|
||||||
|
|
||||||
set<u256> cases;
|
set<u256> cases;
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
#include <libsolutil/CommonData.h>
|
#include <libsolutil/CommonData.h>
|
||||||
|
|
||||||
#include <boost/range/adaptor/reversed.hpp>
|
#include <boost/range/adaptor/reversed.hpp>
|
||||||
|
#include <boost/range/adaptor/map.hpp>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace solidity;
|
using namespace solidity;
|
||||||
@ -130,7 +131,7 @@ bytes toBytes(Opcode _o)
|
|||||||
return toBytes(uint8_t(_o));
|
return toBytes(uint8_t(_o));
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::map<string, uint8_t> const builtins = {
|
static map<string, uint8_t> const builtins = {
|
||||||
{"i32.load", 0x28},
|
{"i32.load", 0x28},
|
||||||
{"i64.load", 0x29},
|
{"i64.load", 0x29},
|
||||||
{"i32.load8_s", 0x2c},
|
{"i32.load8_s", 0x2c},
|
||||||
@ -240,46 +241,56 @@ bytes lebEncodeSigned(int64_t _n)
|
|||||||
bytes prefixSize(bytes _data)
|
bytes prefixSize(bytes _data)
|
||||||
{
|
{
|
||||||
size_t size = _data.size();
|
size_t size = _data.size();
|
||||||
return lebEncode(size) + std::move(_data);
|
return lebEncode(size) + move(_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
bytes makeSection(Section _section, bytes _data)
|
bytes makeSection(Section _section, bytes _data)
|
||||||
{
|
{
|
||||||
return toBytes(_section) + prefixSize(std::move(_data));
|
return toBytes(_section) + prefixSize(move(_data));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bytes BinaryTransform::run(Module const& _module)
|
bytes BinaryTransform::run(Module const& _module)
|
||||||
{
|
{
|
||||||
BinaryTransform bt;
|
map<Type, vector<string>> const types = typeToFunctionMap(_module.imports, _module.functions);
|
||||||
|
|
||||||
for (size_t i = 0; i < _module.globals.size(); ++i)
|
map<string, size_t> const globalIDs = enumerateGlobals(_module);
|
||||||
bt.m_globals[_module.globals[i].variableName] = i;
|
map<string, size_t> const functionIDs = enumerateFunctions(_module);
|
||||||
|
map<string, size_t> const functionTypes = enumerateFunctionTypes(types);
|
||||||
|
|
||||||
size_t funID = 0;
|
yulAssert(globalIDs.size() == _module.globals.size(), "");
|
||||||
for (FunctionImport const& fun: _module.imports)
|
yulAssert(functionIDs.size() == _module.imports.size() + _module.functions.size(), "");
|
||||||
bt.m_functions[fun.internalName] = funID++;
|
yulAssert(functionTypes.size() == functionIDs.size(), "");
|
||||||
for (FunctionDefinition const& fun: _module.functions)
|
yulAssert(functionTypes.size() >= types.size(), "");
|
||||||
bt.m_functions[fun.name] = funID++;
|
|
||||||
|
|
||||||
bytes ret{0, 'a', 's', 'm'};
|
bytes ret{0, 'a', 's', 'm'};
|
||||||
// version
|
// version
|
||||||
ret += bytes{1, 0, 0, 0};
|
ret += bytes{1, 0, 0, 0};
|
||||||
ret += bt.typeSection(_module.imports, _module.functions);
|
ret += typeSection(types);
|
||||||
ret += bt.importSection(_module.imports);
|
ret += importSection(_module.imports, functionTypes);
|
||||||
ret += bt.functionSection(_module.functions);
|
ret += functionSection(_module.functions, functionTypes);
|
||||||
ret += bt.memorySection();
|
ret += memorySection();
|
||||||
ret += bt.globalSection();
|
ret += globalSection(_module.globals);
|
||||||
ret += bt.exportSection();
|
ret += exportSection(functionIDs);
|
||||||
|
|
||||||
|
map<string, pair<size_t, size_t>> subModulePosAndSize;
|
||||||
for (auto const& sub: _module.subModules)
|
for (auto const& sub: _module.subModules)
|
||||||
{
|
{
|
||||||
// TODO should we prefix and / or shorten the name?
|
// TODO should we prefix and / or shorten the name?
|
||||||
bytes data = BinaryTransform::run(sub.second);
|
bytes data = BinaryTransform::run(sub.second);
|
||||||
size_t length = data.size();
|
size_t length = data.size();
|
||||||
ret += bt.customSection(sub.first, std::move(data));
|
ret += customSection(sub.first, move(data));
|
||||||
bt.m_subModulePosAndSize[sub.first] = {ret.size() - length, length};
|
subModulePosAndSize[sub.first] = {ret.size() - length, length};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BinaryTransform bt(
|
||||||
|
move(globalIDs),
|
||||||
|
move(functionIDs),
|
||||||
|
move(functionTypes),
|
||||||
|
move(subModulePosAndSize)
|
||||||
|
);
|
||||||
|
|
||||||
ret += bt.codeSection(_module.functions);
|
ret += bt.codeSection(_module.functions);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -302,7 +313,7 @@ bytes BinaryTransform::operator()(LocalVariable const& _variable)
|
|||||||
|
|
||||||
bytes BinaryTransform::operator()(GlobalVariable const& _variable)
|
bytes BinaryTransform::operator()(GlobalVariable const& _variable)
|
||||||
{
|
{
|
||||||
return toBytes(Opcode::GlobalGet) + lebEncode(m_globals.at(_variable.name));
|
return toBytes(Opcode::GlobalGet) + lebEncode(m_globalIDs.at(_variable.name));
|
||||||
}
|
}
|
||||||
|
|
||||||
bytes BinaryTransform::operator()(BuiltinCall const& _call)
|
bytes BinaryTransform::operator()(BuiltinCall const& _call)
|
||||||
@ -311,12 +322,12 @@ bytes BinaryTransform::operator()(BuiltinCall const& _call)
|
|||||||
// they are references to object names that should not end up in the code.
|
// they are references to object names that should not end up in the code.
|
||||||
if (_call.functionName == "dataoffset")
|
if (_call.functionName == "dataoffset")
|
||||||
{
|
{
|
||||||
string name = std::get<StringLiteral>(_call.arguments.at(0)).value;
|
string name = get<StringLiteral>(_call.arguments.at(0)).value;
|
||||||
return toBytes(Opcode::I64Const) + lebEncodeSigned(m_subModulePosAndSize.at(name).first);
|
return toBytes(Opcode::I64Const) + lebEncodeSigned(m_subModulePosAndSize.at(name).first);
|
||||||
}
|
}
|
||||||
else if (_call.functionName == "datasize")
|
else if (_call.functionName == "datasize")
|
||||||
{
|
{
|
||||||
string name = std::get<StringLiteral>(_call.arguments.at(0)).value;
|
string name = get<StringLiteral>(_call.arguments.at(0)).value;
|
||||||
return toBytes(Opcode::I64Const) + lebEncodeSigned(m_subModulePosAndSize.at(name).second);
|
return toBytes(Opcode::I64Const) + lebEncodeSigned(m_subModulePosAndSize.at(name).second);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -331,20 +342,24 @@ bytes BinaryTransform::operator()(BuiltinCall const& _call)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
yulAssert(builtins.count(_call.functionName), "Builtin " + _call.functionName + " not found");
|
yulAssert(builtins.count(_call.functionName), "Builtin " + _call.functionName + " not found");
|
||||||
bytes ret = std::move(args) + toBytes(builtins.at(_call.functionName));
|
bytes ret = move(args) + toBytes(builtins.at(_call.functionName));
|
||||||
if (
|
if (
|
||||||
_call.functionName.find(".load") != string::npos ||
|
_call.functionName.find(".load") != string::npos ||
|
||||||
_call.functionName.find(".store") != string::npos
|
_call.functionName.find(".store") != string::npos
|
||||||
)
|
)
|
||||||
// alignment and offset
|
// Alignment hint and offset. Interpreters ignore the alignment. JITs/AOTs can take it
|
||||||
ret += bytes{{3, 0}};
|
// into account to generate more efficient code but if the hint is invalid it could
|
||||||
|
// actually be more expensive. It's best to hint at 1-byte alignment if we don't plan
|
||||||
|
// to control the memory layout accordingly.
|
||||||
|
ret += bytes{{0, 0}}; // 2^0 == 1-byte alignment
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bytes BinaryTransform::operator()(FunctionCall const& _call)
|
bytes BinaryTransform::operator()(FunctionCall const& _call)
|
||||||
{
|
{
|
||||||
return visit(_call.arguments) + toBytes(Opcode::Call) + lebEncode(m_functions.at(_call.functionName));
|
return visit(_call.arguments) + toBytes(Opcode::Call) + lebEncode(m_functionIDs.at(_call.functionName));
|
||||||
}
|
}
|
||||||
|
|
||||||
bytes BinaryTransform::operator()(LocalAssignment const& _assignment)
|
bytes BinaryTransform::operator()(LocalAssignment const& _assignment)
|
||||||
@ -360,7 +375,7 @@ bytes BinaryTransform::operator()(GlobalAssignment const& _assignment)
|
|||||||
return
|
return
|
||||||
std::visit(*this, *_assignment.value) +
|
std::visit(*this, *_assignment.value) +
|
||||||
toBytes(Opcode::GlobalSet) +
|
toBytes(Opcode::GlobalSet) +
|
||||||
lebEncode(m_globals.at(_assignment.variableName));
|
lebEncode(m_globalIDs.at(_assignment.variableName));
|
||||||
}
|
}
|
||||||
|
|
||||||
bytes BinaryTransform::operator()(If const& _if)
|
bytes BinaryTransform::operator()(If const& _if)
|
||||||
@ -430,9 +445,14 @@ bytes BinaryTransform::operator()(FunctionDefinition const& _function)
|
|||||||
|
|
||||||
// This is a kind of run-length-encoding of local types. Has to be adapted once
|
// This is a kind of run-length-encoding of local types. Has to be adapted once
|
||||||
// we have locals of different types.
|
// we have locals of different types.
|
||||||
ret += lebEncode(1); // number of locals groups
|
if (_function.locals.size() == 0)
|
||||||
ret += lebEncode(_function.locals.size());
|
ret += lebEncode(0); // number of locals groups
|
||||||
ret += toBytes(ValueType::I64);
|
else
|
||||||
|
{
|
||||||
|
ret += lebEncode(1); // number of locals groups
|
||||||
|
ret += lebEncode(_function.locals.size());
|
||||||
|
ret += toBytes(ValueType::I64);
|
||||||
|
}
|
||||||
|
|
||||||
m_locals.clear();
|
m_locals.clear();
|
||||||
size_t varIdx = 0;
|
size_t varIdx = 0;
|
||||||
@ -448,7 +468,7 @@ bytes BinaryTransform::operator()(FunctionDefinition const& _function)
|
|||||||
|
|
||||||
yulAssert(m_labels.empty(), "Stray labels.");
|
yulAssert(m_labels.empty(), "Stray labels.");
|
||||||
|
|
||||||
return prefixSize(std::move(ret));
|
return prefixSize(move(ret));
|
||||||
}
|
}
|
||||||
|
|
||||||
BinaryTransform::Type BinaryTransform::typeOf(FunctionImport const& _import)
|
BinaryTransform::Type BinaryTransform::typeOf(FunctionImport const& _import)
|
||||||
@ -487,9 +507,9 @@ vector<uint8_t> BinaryTransform::encodeTypes(vector<string> const& _typeNames)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bytes BinaryTransform::typeSection(
|
map<BinaryTransform::Type, vector<string>> BinaryTransform::typeToFunctionMap(
|
||||||
vector<FunctionImport> const& _imports,
|
vector<wasm::FunctionImport> const& _imports,
|
||||||
vector<FunctionDefinition> const& _functions
|
vector<wasm::FunctionDefinition> const& _functions
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
map<Type, vector<string>> types;
|
map<Type, vector<string>> types;
|
||||||
@ -498,12 +518,50 @@ bytes BinaryTransform::typeSection(
|
|||||||
for (auto const& fun: _functions)
|
for (auto const& fun: _functions)
|
||||||
types[typeOf(fun)].emplace_back(fun.name);
|
types[typeOf(fun)].emplace_back(fun.name);
|
||||||
|
|
||||||
bytes result;
|
return types;
|
||||||
size_t index = 0;
|
}
|
||||||
for (auto const& [type, funNames]: types)
|
|
||||||
|
map<string, size_t> BinaryTransform::enumerateGlobals(Module const& _module)
|
||||||
|
{
|
||||||
|
map<string, size_t> globals;
|
||||||
|
for (size_t i = 0; i < _module.globals.size(); ++i)
|
||||||
|
globals[_module.globals[i].variableName] = i;
|
||||||
|
|
||||||
|
return globals;
|
||||||
|
}
|
||||||
|
|
||||||
|
map<string, size_t> BinaryTransform::enumerateFunctions(Module const& _module)
|
||||||
|
{
|
||||||
|
map<string, size_t> functions;
|
||||||
|
size_t funID = 0;
|
||||||
|
for (FunctionImport const& fun: _module.imports)
|
||||||
|
functions[fun.internalName] = funID++;
|
||||||
|
for (FunctionDefinition const& fun: _module.functions)
|
||||||
|
functions[fun.name] = funID++;
|
||||||
|
|
||||||
|
return functions;
|
||||||
|
}
|
||||||
|
|
||||||
|
map<string, size_t> BinaryTransform::enumerateFunctionTypes(map<Type, vector<string>> const& _typeToFunctionMap)
|
||||||
|
{
|
||||||
|
map<string, size_t> functionTypes;
|
||||||
|
size_t typeID = 0;
|
||||||
|
for (vector<string> const& funNames: _typeToFunctionMap | boost::adaptors::map_values)
|
||||||
{
|
{
|
||||||
for (string const& name: funNames)
|
for (string const& name: funNames)
|
||||||
m_functionTypes[name] = index;
|
functionTypes[name] = typeID;
|
||||||
|
++typeID;
|
||||||
|
}
|
||||||
|
|
||||||
|
return functionTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes BinaryTransform::typeSection(map<BinaryTransform::Type, vector<string>> const& _typeToFunctionMap)
|
||||||
|
{
|
||||||
|
bytes result;
|
||||||
|
size_t index = 0;
|
||||||
|
for (Type const& type: _typeToFunctionMap | boost::adaptors::map_keys)
|
||||||
|
{
|
||||||
result += toBytes(ValueType::Function);
|
result += toBytes(ValueType::Function);
|
||||||
result += lebEncode(type.first.size()) + type.first;
|
result += lebEncode(type.first.size()) + type.first;
|
||||||
result += lebEncode(type.second.size()) + type.second;
|
result += lebEncode(type.second.size()) + type.second;
|
||||||
@ -511,11 +569,12 @@ bytes BinaryTransform::typeSection(
|
|||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return makeSection(Section::TYPE, lebEncode(index) + std::move(result));
|
return makeSection(Section::TYPE, lebEncode(index) + move(result));
|
||||||
}
|
}
|
||||||
|
|
||||||
bytes BinaryTransform::importSection(
|
bytes BinaryTransform::importSection(
|
||||||
vector<FunctionImport> const& _imports
|
vector<FunctionImport> const& _imports,
|
||||||
|
map<string, size_t> const& _functionTypes
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
bytes result = lebEncode(_imports.size());
|
bytes result = lebEncode(_imports.size());
|
||||||
@ -526,17 +585,20 @@ bytes BinaryTransform::importSection(
|
|||||||
encodeName(import.module) +
|
encodeName(import.module) +
|
||||||
encodeName(import.externalName) +
|
encodeName(import.externalName) +
|
||||||
toBytes(importKind) +
|
toBytes(importKind) +
|
||||||
lebEncode(m_functionTypes[import.internalName]);
|
lebEncode(_functionTypes.at(import.internalName));
|
||||||
}
|
}
|
||||||
return makeSection(Section::IMPORT, std::move(result));
|
return makeSection(Section::IMPORT, move(result));
|
||||||
}
|
}
|
||||||
|
|
||||||
bytes BinaryTransform::functionSection(vector<FunctionDefinition> const& _functions)
|
bytes BinaryTransform::functionSection(
|
||||||
|
vector<FunctionDefinition> const& _functions,
|
||||||
|
map<string, size_t> const& _functionTypes
|
||||||
|
)
|
||||||
{
|
{
|
||||||
bytes result = lebEncode(_functions.size());
|
bytes result = lebEncode(_functions.size());
|
||||||
for (auto const& fun: _functions)
|
for (auto const& fun: _functions)
|
||||||
result += lebEncode(m_functionTypes.at(fun.name));
|
result += lebEncode(_functionTypes.at(fun.name));
|
||||||
return makeSection(Section::FUNCTION, std::move(result));
|
return makeSection(Section::FUNCTION, move(result));
|
||||||
}
|
}
|
||||||
|
|
||||||
bytes BinaryTransform::memorySection()
|
bytes BinaryTransform::memorySection()
|
||||||
@ -544,13 +606,13 @@ bytes BinaryTransform::memorySection()
|
|||||||
bytes result = lebEncode(1);
|
bytes result = lebEncode(1);
|
||||||
result.push_back(static_cast<uint8_t>(LimitsKind::Min));
|
result.push_back(static_cast<uint8_t>(LimitsKind::Min));
|
||||||
result.push_back(1); // initial length
|
result.push_back(1); // initial length
|
||||||
return makeSection(Section::MEMORY, std::move(result));
|
return makeSection(Section::MEMORY, move(result));
|
||||||
}
|
}
|
||||||
|
|
||||||
bytes BinaryTransform::globalSection()
|
bytes BinaryTransform::globalSection(vector<wasm::GlobalVariableDeclaration> const& _globals)
|
||||||
{
|
{
|
||||||
bytes result = lebEncode(m_globals.size());
|
bytes result = lebEncode(_globals.size());
|
||||||
for (size_t i = 0; i < m_globals.size(); ++i)
|
for (size_t i = 0; i < _globals.size(); ++i)
|
||||||
result +=
|
result +=
|
||||||
toBytes(ValueType::I64) +
|
toBytes(ValueType::I64) +
|
||||||
lebEncode(static_cast<uint8_t>(Mutability::Var)) +
|
lebEncode(static_cast<uint8_t>(Mutability::Var)) +
|
||||||
@ -558,21 +620,21 @@ bytes BinaryTransform::globalSection()
|
|||||||
lebEncodeSigned(0) +
|
lebEncodeSigned(0) +
|
||||||
toBytes(Opcode::End);
|
toBytes(Opcode::End);
|
||||||
|
|
||||||
return makeSection(Section::GLOBAL, std::move(result));
|
return makeSection(Section::GLOBAL, move(result));
|
||||||
}
|
}
|
||||||
|
|
||||||
bytes BinaryTransform::exportSection()
|
bytes BinaryTransform::exportSection(map<string, size_t> const& _functionIDs)
|
||||||
{
|
{
|
||||||
bytes result = lebEncode(2);
|
bytes result = lebEncode(2);
|
||||||
result += encodeName("memory") + toBytes(Export::Memory) + lebEncode(0);
|
result += encodeName("memory") + toBytes(Export::Memory) + lebEncode(0);
|
||||||
result += encodeName("main") + toBytes(Export::Function) + lebEncode(m_functions.at("main"));
|
result += encodeName("main") + toBytes(Export::Function) + lebEncode(_functionIDs.at("main"));
|
||||||
return makeSection(Section::EXPORT, std::move(result));
|
return makeSection(Section::EXPORT, move(result));
|
||||||
}
|
}
|
||||||
|
|
||||||
bytes BinaryTransform::customSection(string const& _name, bytes _data)
|
bytes BinaryTransform::customSection(string const& _name, bytes _data)
|
||||||
{
|
{
|
||||||
bytes result = encodeName(_name) + std::move(_data);
|
bytes result = encodeName(_name) + move(_data);
|
||||||
return makeSection(Section::CUSTOM, std::move(result));
|
return makeSection(Section::CUSTOM, move(result));
|
||||||
}
|
}
|
||||||
|
|
||||||
bytes BinaryTransform::codeSection(vector<wasm::FunctionDefinition> const& _functions)
|
bytes BinaryTransform::codeSection(vector<wasm::FunctionDefinition> const& _functions)
|
||||||
@ -580,7 +642,7 @@ bytes BinaryTransform::codeSection(vector<wasm::FunctionDefinition> const& _func
|
|||||||
bytes result = lebEncode(_functions.size());
|
bytes result = lebEncode(_functions.size());
|
||||||
for (FunctionDefinition const& fun: _functions)
|
for (FunctionDefinition const& fun: _functions)
|
||||||
result += (*this)(fun);
|
result += (*this)(fun);
|
||||||
return makeSection(Section::CODE, std::move(result));
|
return makeSection(Section::CODE, move(result));
|
||||||
}
|
}
|
||||||
|
|
||||||
bytes BinaryTransform::visit(vector<Expression> const& _expressions)
|
bytes BinaryTransform::visit(vector<Expression> const& _expressions)
|
||||||
@ -611,7 +673,7 @@ bytes BinaryTransform::encodeLabelIdx(string const& _label) const
|
|||||||
yulAssert(false, "Label not found.");
|
yulAssert(false, "Label not found.");
|
||||||
}
|
}
|
||||||
|
|
||||||
bytes BinaryTransform::encodeName(std::string const& _name)
|
bytes BinaryTransform::encodeName(string const& _name)
|
||||||
{
|
{
|
||||||
// UTF-8 is allowed here by the Wasm spec, but since all names here should stem from
|
// UTF-8 is allowed here by the Wasm spec, but since all names here should stem from
|
||||||
// Solidity or Yul identifiers or similar, non-ascii characters ending up here
|
// Solidity or Yul identifiers or similar, non-ascii characters ending up here
|
||||||
|
@ -55,23 +55,49 @@ public:
|
|||||||
bytes operator()(wasm::FunctionDefinition const& _function);
|
bytes operator()(wasm::FunctionDefinition const& _function);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
BinaryTransform(
|
||||||
|
std::map<std::string, size_t> _globalIDs,
|
||||||
|
std::map<std::string, size_t> _functionIDs,
|
||||||
|
std::map<std::string, size_t> _functionTypes,
|
||||||
|
std::map<std::string, std::pair<size_t, size_t>> _subModulePosAndSize
|
||||||
|
):
|
||||||
|
m_globalIDs(std::move(_globalIDs)),
|
||||||
|
m_functionIDs(std::move(_functionIDs)),
|
||||||
|
m_functionTypes(std::move(_functionTypes)),
|
||||||
|
m_subModulePosAndSize(std::move(_subModulePosAndSize))
|
||||||
|
{}
|
||||||
|
|
||||||
using Type = std::pair<std::vector<std::uint8_t>, std::vector<std::uint8_t>>;
|
using Type = std::pair<std::vector<std::uint8_t>, std::vector<std::uint8_t>>;
|
||||||
static Type typeOf(wasm::FunctionImport const& _import);
|
static Type typeOf(wasm::FunctionImport const& _import);
|
||||||
static Type typeOf(wasm::FunctionDefinition const& _funDef);
|
static Type typeOf(wasm::FunctionDefinition const& _funDef);
|
||||||
|
|
||||||
static uint8_t encodeType(std::string const& _typeName);
|
static uint8_t encodeType(std::string const& _typeName);
|
||||||
static std::vector<uint8_t> encodeTypes(std::vector<std::string> const& _typeNames);
|
static std::vector<uint8_t> encodeTypes(std::vector<std::string> const& _typeNames);
|
||||||
bytes typeSection(
|
|
||||||
|
static std::map<Type, std::vector<std::string>> typeToFunctionMap(
|
||||||
std::vector<wasm::FunctionImport> const& _imports,
|
std::vector<wasm::FunctionImport> const& _imports,
|
||||||
std::vector<wasm::FunctionDefinition> const& _functions
|
std::vector<wasm::FunctionDefinition> const& _functions
|
||||||
);
|
);
|
||||||
|
|
||||||
bytes importSection(std::vector<wasm::FunctionImport> const& _imports);
|
static std::map<std::string, size_t> enumerateGlobals(Module const& _module);
|
||||||
bytes functionSection(std::vector<wasm::FunctionDefinition> const& _functions);
|
static std::map<std::string, size_t> enumerateFunctions(Module const& _module);
|
||||||
bytes memorySection();
|
static std::map<std::string, size_t> enumerateFunctionTypes(
|
||||||
bytes globalSection();
|
std::map<Type, std::vector<std::string>> const& _typeToFunctionMap
|
||||||
bytes exportSection();
|
);
|
||||||
bytes customSection(std::string const& _name, bytes _data);
|
|
||||||
|
static bytes typeSection(std::map<Type, std::vector<std::string>> const& _typeToFunctionMap);
|
||||||
|
static bytes importSection(
|
||||||
|
std::vector<wasm::FunctionImport> const& _imports,
|
||||||
|
std::map<std::string, size_t> const& _functionTypes
|
||||||
|
);
|
||||||
|
static bytes functionSection(
|
||||||
|
std::vector<wasm::FunctionDefinition> const& _functions,
|
||||||
|
std::map<std::string, size_t> const& _functionTypes
|
||||||
|
);
|
||||||
|
static bytes memorySection();
|
||||||
|
static bytes globalSection(std::vector<wasm::GlobalVariableDeclaration> const& _globals);
|
||||||
|
static bytes exportSection(std::map<std::string, size_t> const& _functionIDs);
|
||||||
|
static bytes customSection(std::string const& _name, bytes _data);
|
||||||
bytes codeSection(std::vector<wasm::FunctionDefinition> const& _functions);
|
bytes codeSection(std::vector<wasm::FunctionDefinition> const& _functions);
|
||||||
|
|
||||||
bytes visit(std::vector<wasm::Expression> const& _expressions);
|
bytes visit(std::vector<wasm::Expression> const& _expressions);
|
||||||
@ -81,12 +107,13 @@ private:
|
|||||||
|
|
||||||
static bytes encodeName(std::string const& _name);
|
static bytes encodeName(std::string const& _name);
|
||||||
|
|
||||||
|
std::map<std::string, size_t> const m_globalIDs;
|
||||||
|
std::map<std::string, size_t> const m_functionIDs;
|
||||||
|
std::map<std::string, size_t> const m_functionTypes;
|
||||||
|
std::map<std::string, std::pair<size_t, size_t>> const m_subModulePosAndSize;
|
||||||
|
|
||||||
std::map<std::string, size_t> m_locals;
|
std::map<std::string, size_t> m_locals;
|
||||||
std::map<std::string, size_t> m_globals;
|
|
||||||
std::map<std::string, size_t> m_functions;
|
|
||||||
std::map<std::string, size_t> m_functionTypes;
|
|
||||||
std::vector<std::string> m_labels;
|
std::vector<std::string> m_labels;
|
||||||
std::map<std::string, std::pair<size_t, size_t>> m_subModulePosAndSize;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -33,16 +33,16 @@ using namespace solidity::yul;
|
|||||||
VarNameCleaner::VarNameCleaner(
|
VarNameCleaner::VarNameCleaner(
|
||||||
Block const& _ast,
|
Block const& _ast,
|
||||||
Dialect const& _dialect,
|
Dialect const& _dialect,
|
||||||
set<YulString> _blacklist
|
set<YulString> _namesToKeep
|
||||||
):
|
):
|
||||||
m_dialect{_dialect},
|
m_dialect{_dialect},
|
||||||
m_blacklist{std::move(_blacklist)},
|
m_namesToKeep{std::move(_namesToKeep)},
|
||||||
m_translatedNames{}
|
m_translatedNames{}
|
||||||
{
|
{
|
||||||
for (auto const& statement: _ast.statements)
|
for (auto const& statement: _ast.statements)
|
||||||
if (holds_alternative<FunctionDefinition>(statement))
|
if (holds_alternative<FunctionDefinition>(statement))
|
||||||
m_blacklist.insert(std::get<FunctionDefinition>(statement).name);
|
m_namesToKeep.insert(std::get<FunctionDefinition>(statement).name);
|
||||||
m_usedNames = m_blacklist;
|
m_usedNames = m_namesToKeep;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VarNameCleaner::operator()(FunctionDefinition& _funDef)
|
void VarNameCleaner::operator()(FunctionDefinition& _funDef)
|
||||||
@ -51,7 +51,7 @@ void VarNameCleaner::operator()(FunctionDefinition& _funDef)
|
|||||||
m_insideFunction = true;
|
m_insideFunction = true;
|
||||||
|
|
||||||
set<YulString> globalUsedNames = std::move(m_usedNames);
|
set<YulString> globalUsedNames = std::move(m_usedNames);
|
||||||
m_usedNames = m_blacklist;
|
m_usedNames = m_namesToKeep;
|
||||||
map<YulString, YulString> globalTranslatedNames;
|
map<YulString, YulString> globalTranslatedNames;
|
||||||
swap(globalTranslatedNames, m_translatedNames);
|
swap(globalTranslatedNames, m_translatedNames);
|
||||||
|
|
||||||
|
@ -63,7 +63,7 @@ private:
|
|||||||
VarNameCleaner(
|
VarNameCleaner(
|
||||||
Block const& _ast,
|
Block const& _ast,
|
||||||
Dialect const& _dialect,
|
Dialect const& _dialect,
|
||||||
std::set<YulString> _blacklist = {}
|
std::set<YulString> _namesToKeep = {}
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Tries to rename a list of variables.
|
/// Tries to rename a list of variables.
|
||||||
@ -77,11 +77,13 @@ private:
|
|||||||
YulString findCleanName(YulString const& name) const;
|
YulString findCleanName(YulString const& name) const;
|
||||||
|
|
||||||
/// Tests whether a given name was already used within this pass
|
/// Tests whether a given name was already used within this pass
|
||||||
/// or is on the blacklist.
|
/// or was set to be kept.
|
||||||
bool isUsedName(YulString const& _name) const;
|
bool isUsedName(YulString const& _name) const;
|
||||||
|
|
||||||
Dialect const& m_dialect;
|
Dialect const& m_dialect;
|
||||||
std::set<YulString> m_blacklist;
|
|
||||||
|
/// These names will not be modified.
|
||||||
|
std::set<YulString> m_namesToKeep;
|
||||||
|
|
||||||
/// Set of names that are in use.
|
/// Set of names that are in use.
|
||||||
std::set<YulString> m_usedNames;
|
std::set<YulString> m_usedNames;
|
||||||
|
@ -54,6 +54,10 @@ cmake \
|
|||||||
-DTESTS=0 \
|
-DTESTS=0 \
|
||||||
..
|
..
|
||||||
make -j 4 soljson
|
make -j 4 soljson
|
||||||
|
# Patch soljson.js for backwards compatibility.
|
||||||
|
# TODO: remove this with 0.7.
|
||||||
|
# "viiiii" encodes the signature of the callback function.
|
||||||
|
sed -i -e 's/addFunction(func,sig){/addFunction(func,sig){sig=sig||"viiiii";/' libsolc/soljson.js
|
||||||
|
|
||||||
cd ..
|
cd ..
|
||||||
mkdir -p upload
|
mkdir -p upload
|
||||||
|
@ -740,7 +740,7 @@ remap paths using the context:prefix=path syntax.
|
|||||||
Example:
|
Example:
|
||||||
solc --)" + g_argBinary + R"( -o /tmp/solcoutput dapp-bin=/usr/local/lib/dapp-bin contract.sol
|
solc --)" + g_argBinary + R"( -o /tmp/solcoutput dapp-bin=/usr/local/lib/dapp-bin contract.sol
|
||||||
|
|
||||||
Allowed options)").c_str(),
|
General Information)").c_str(),
|
||||||
po::options_description::m_default_line_length,
|
po::options_description::m_default_line_length,
|
||||||
po::options_description::m_default_line_length - 23
|
po::options_description::m_default_line_length - 23
|
||||||
);
|
);
|
||||||
@ -748,47 +748,67 @@ Allowed options)").c_str(),
|
|||||||
(g_argHelp.c_str(), "Show help message and exit.")
|
(g_argHelp.c_str(), "Show help message and exit.")
|
||||||
(g_argVersion.c_str(), "Show version and exit.")
|
(g_argVersion.c_str(), "Show version and exit.")
|
||||||
(g_strLicense.c_str(), "Show licensing information and exit.")
|
(g_strLicense.c_str(), "Show licensing information and exit.")
|
||||||
|
;
|
||||||
|
|
||||||
|
po::options_description inputOptions("Input Options");
|
||||||
|
inputOptions.add_options()
|
||||||
|
(
|
||||||
|
g_argBasePath.c_str(),
|
||||||
|
po::value<string>()->value_name("path"),
|
||||||
|
"Use the given path as the root of the source tree instead of the root of the filesystem."
|
||||||
|
)
|
||||||
|
(
|
||||||
|
g_argAllowPaths.c_str(),
|
||||||
|
po::value<string>()->value_name("path(s)"),
|
||||||
|
"Allow a given path for imports. A list of paths can be supplied by separating them with a comma."
|
||||||
|
)
|
||||||
|
(
|
||||||
|
g_argIgnoreMissingFiles.c_str(),
|
||||||
|
"Ignore missing files."
|
||||||
|
)
|
||||||
|
(
|
||||||
|
g_argErrorRecovery.c_str(),
|
||||||
|
"Enables additional parser error recovery."
|
||||||
|
)
|
||||||
|
;
|
||||||
|
desc.add(inputOptions);
|
||||||
|
|
||||||
|
po::options_description outputOptions("Output Options");
|
||||||
|
outputOptions.add_options()
|
||||||
|
(
|
||||||
|
(g_argOutputDir + ",o").c_str(),
|
||||||
|
po::value<string>()->value_name("path"),
|
||||||
|
"If given, creates one file per component and contract/file at the specified directory."
|
||||||
|
)
|
||||||
|
(
|
||||||
|
g_strOverwrite.c_str(),
|
||||||
|
"Overwrite existing files (used together with -o)."
|
||||||
|
)
|
||||||
(
|
(
|
||||||
g_strEVMVersion.c_str(),
|
g_strEVMVersion.c_str(),
|
||||||
po::value<string>()->value_name("version"),
|
po::value<string>()->value_name("version"),
|
||||||
"Select desired EVM version. Either homestead, tangerineWhistle, spuriousDragon, "
|
"Select desired EVM version. Either homestead, tangerineWhistle, spuriousDragon, "
|
||||||
"byzantium, constantinople, petersburg, istanbul (default) or berlin."
|
"byzantium, constantinople, petersburg, istanbul (default) or berlin."
|
||||||
)
|
)
|
||||||
(g_argPrettyJson.c_str(), "Output JSON in pretty format. Currently it only works with the combined JSON output.")
|
|
||||||
(
|
|
||||||
g_argLibraries.c_str(),
|
|
||||||
po::value<vector<string>>()->value_name("libs"),
|
|
||||||
"Direct string or file containing library addresses. Syntax: "
|
|
||||||
"<libraryName>:<address> [, or whitespace] ...\n"
|
|
||||||
"Address is interpreted as a hex string optionally prefixed by 0x."
|
|
||||||
)
|
|
||||||
(
|
(
|
||||||
g_strRevertStrings.c_str(),
|
g_strRevertStrings.c_str(),
|
||||||
po::value<string>()->value_name(boost::join(g_revertStringsArgs, ",")),
|
po::value<string>()->value_name(boost::join(g_revertStringsArgs, ",")),
|
||||||
"Strip revert (and require) reason strings or add additional debugging information."
|
"Strip revert (and require) reason strings or add additional debugging information."
|
||||||
)
|
)
|
||||||
(
|
;
|
||||||
(g_argOutputDir + ",o").c_str(),
|
desc.add(outputOptions);
|
||||||
po::value<string>()->value_name("path"),
|
|
||||||
"If given, creates one file per component and contract/file at the specified directory."
|
po::options_description alternativeInputModes("Alternative Input Modes");
|
||||||
)
|
alternativeInputModes.add_options()
|
||||||
(g_strOverwrite.c_str(), "Overwrite existing files (used together with -o).")
|
|
||||||
(
|
|
||||||
g_argCombinedJson.c_str(),
|
|
||||||
po::value<string>()->value_name(boost::join(g_combinedJsonArgs, ",")),
|
|
||||||
"Output a single json document containing the specified information."
|
|
||||||
)
|
|
||||||
(g_argGas.c_str(), "Print an estimate of the maximal gas usage for each function.")
|
|
||||||
(
|
(
|
||||||
g_argStandardJSON.c_str(),
|
g_argStandardJSON.c_str(),
|
||||||
"Switch to Standard JSON input / output mode, ignoring all options. "
|
"Switch to Standard JSON input / output mode, ignoring all options. "
|
||||||
"It reads from standard input, if no input file was given, otherwise it reads from the provided input file. The result will be written to standard output."
|
"It reads from standard input, if no input file was given, otherwise it reads from the provided input file. The result will be written to standard output."
|
||||||
)
|
)
|
||||||
(
|
(
|
||||||
g_argImportAst.c_str(),
|
g_argLink.c_str(),
|
||||||
("Import ASTs to be compiled, assumes input holds the AST in compact JSON format. "
|
("Switch to linker mode, ignoring all options apart from --" + g_argLibraries + " "
|
||||||
"Supported Inputs is the output of the --" + g_argStandardJSON + " or the one produced by "
|
"and modify binaries in place.").c_str()
|
||||||
"--" + g_argCombinedJson + " " + g_strAst + "," + g_strCompactJSON).c_str()
|
|
||||||
)
|
)
|
||||||
(
|
(
|
||||||
g_argAssemble.c_str(),
|
g_argAssemble.c_str(),
|
||||||
@ -809,58 +829,62 @@ Allowed options)").c_str(),
|
|||||||
"and assumes input is strict assembly.").c_str()
|
"and assumes input is strict assembly.").c_str()
|
||||||
)
|
)
|
||||||
(
|
(
|
||||||
g_strYulDialect.c_str(),
|
g_argImportAst.c_str(),
|
||||||
po::value<string>()->value_name(boost::join(g_yulDialectArgs, ",")),
|
("Import ASTs to be compiled, assumes input holds the AST in compact JSON format. "
|
||||||
"Input dialect to use in assembly or yul mode."
|
"Supported Inputs is the output of the --" + g_argStandardJSON + " or the one produced by "
|
||||||
|
"--" + g_argCombinedJson + " " + g_strAst + "," + g_strCompactJSON).c_str()
|
||||||
)
|
)
|
||||||
|
;
|
||||||
|
desc.add(alternativeInputModes);
|
||||||
|
|
||||||
|
po::options_description assemblyModeOptions("Assembly Mode Options");
|
||||||
|
assemblyModeOptions.add_options()
|
||||||
(
|
(
|
||||||
g_argMachine.c_str(),
|
g_argMachine.c_str(),
|
||||||
po::value<string>()->value_name(boost::join(g_machineArgs, ",")),
|
po::value<string>()->value_name(boost::join(g_machineArgs, ",")),
|
||||||
"Target machine in assembly or Yul mode."
|
"Target machine in assembly or Yul mode."
|
||||||
)
|
)
|
||||||
(
|
(
|
||||||
g_argLink.c_str(),
|
g_strYulDialect.c_str(),
|
||||||
("Switch to linker mode, ignoring all options apart from --" + g_argLibraries + " "
|
po::value<string>()->value_name(boost::join(g_yulDialectArgs, ",")),
|
||||||
"and modify binaries in place.").c_str()
|
"Input dialect to use in assembly or yul mode."
|
||||||
|
)
|
||||||
|
;
|
||||||
|
desc.add(assemblyModeOptions);
|
||||||
|
|
||||||
|
po::options_description linkerModeOptions("Linker Mode Options");
|
||||||
|
linkerModeOptions.add_options()
|
||||||
|
(
|
||||||
|
g_argLibraries.c_str(),
|
||||||
|
po::value<vector<string>>()->value_name("libs"),
|
||||||
|
"Direct string or file containing library addresses. Syntax: "
|
||||||
|
"<libraryName>:<address> [, or whitespace] ...\n"
|
||||||
|
"Address is interpreted as a hex string optionally prefixed by 0x."
|
||||||
|
)
|
||||||
|
;
|
||||||
|
desc.add(linkerModeOptions);
|
||||||
|
|
||||||
|
po::options_description outputFormatting("Output Formatting");
|
||||||
|
outputFormatting.add_options()
|
||||||
|
(
|
||||||
|
g_argPrettyJson.c_str(),
|
||||||
|
"Output JSON in pretty format. Currently it only works with the combined JSON output."
|
||||||
)
|
)
|
||||||
(
|
(
|
||||||
g_argMetadataHash.c_str(),
|
g_argColor.c_str(),
|
||||||
po::value<string>()->value_name(boost::join(g_metadataHashArgs, ",")),
|
"Force colored output."
|
||||||
"Choose hash method for the bytecode metadata or disable it."
|
|
||||||
)
|
|
||||||
(g_argMetadataLiteral.c_str(), "Store referenced sources as literal data in the metadata output.")
|
|
||||||
(
|
|
||||||
g_argAllowPaths.c_str(),
|
|
||||||
po::value<string>()->value_name("path(s)"),
|
|
||||||
"Allow a given path for imports. A list of paths can be supplied by separating them with a comma."
|
|
||||||
)
|
)
|
||||||
(
|
(
|
||||||
g_argBasePath.c_str(),
|
g_argNoColor.c_str(),
|
||||||
po::value<string>()->value_name("path"),
|
"Explicitly disable colored output, disabling terminal auto-detection."
|
||||||
"Use the given path as the root of the source tree instead of the root of the filesystem."
|
|
||||||
)
|
)
|
||||||
(g_argColor.c_str(), "Force colored output.")
|
|
||||||
(g_argNoColor.c_str(), "Explicitly disable colored output, disabling terminal auto-detection.")
|
|
||||||
(g_argOldReporter.c_str(), "Enables old diagnostics reporter.")
|
|
||||||
(g_argErrorRecovery.c_str(), "Enables additional parser error recovery.")
|
|
||||||
(g_argIgnoreMissingFiles.c_str(), "Ignore missing files.");
|
|
||||||
po::options_description optimizerOptions("Optimizer options");
|
|
||||||
optimizerOptions.add_options()
|
|
||||||
(g_argOptimize.c_str(), "Enable bytecode optimizer.")
|
|
||||||
(
|
(
|
||||||
g_argOptimizeRuns.c_str(),
|
g_argOldReporter.c_str(),
|
||||||
po::value<unsigned>()->value_name("n")->default_value(200),
|
"Enables old diagnostics reporter (legacy option, will be removed)."
|
||||||
"Set for how many contract runs to optimize. "
|
|
||||||
"Lower values will optimize more for initial deployment cost, higher values will optimize more for high-frequency usage."
|
|
||||||
)
|
)
|
||||||
(g_strOptimizeYul.c_str(), ("Legacy option, ignored. Use the general --" + g_argOptimize + " to enable Yul optimizer.").c_str())
|
;
|
||||||
(g_strNoOptimizeYul.c_str(), "Disable Yul optimizer in Solidity.")
|
desc.add(outputFormatting);
|
||||||
(
|
|
||||||
g_strYulOptimizations.c_str(),
|
|
||||||
po::value<string>()->value_name("steps"),
|
|
||||||
"Forces yul optimizer to use the specified sequence of optimization steps instead of the built-in one."
|
|
||||||
);
|
|
||||||
desc.add(optimizerOptions);
|
|
||||||
po::options_description outputComponents("Output Components");
|
po::options_description outputComponents("Output Components");
|
||||||
outputComponents.add_options()
|
outputComponents.add_options()
|
||||||
(g_argAstJson.c_str(), "AST of all source files in JSON format.")
|
(g_argAstJson.c_str(), "AST of all source files in JSON format.")
|
||||||
@ -878,9 +902,66 @@ Allowed options)").c_str(),
|
|||||||
(g_argNatspecUser.c_str(), "Natspec user documentation of all contracts.")
|
(g_argNatspecUser.c_str(), "Natspec user documentation of all contracts.")
|
||||||
(g_argNatspecDev.c_str(), "Natspec developer documentation of all contracts.")
|
(g_argNatspecDev.c_str(), "Natspec developer documentation of all contracts.")
|
||||||
(g_argMetadata.c_str(), "Combined Metadata JSON whose Swarm hash is stored on-chain.")
|
(g_argMetadata.c_str(), "Combined Metadata JSON whose Swarm hash is stored on-chain.")
|
||||||
(g_argStorageLayout.c_str(), "Slots, offsets and types of the contract's state variables.");
|
(g_argStorageLayout.c_str(), "Slots, offsets and types of the contract's state variables.")
|
||||||
|
;
|
||||||
desc.add(outputComponents);
|
desc.add(outputComponents);
|
||||||
|
|
||||||
|
po::options_description extraOutput("Extra Output");
|
||||||
|
extraOutput.add_options()
|
||||||
|
(
|
||||||
|
g_argGas.c_str(),
|
||||||
|
"Print an estimate of the maximal gas usage for each function."
|
||||||
|
)
|
||||||
|
(
|
||||||
|
g_argCombinedJson.c_str(),
|
||||||
|
po::value<string>()->value_name(boost::join(g_combinedJsonArgs, ",")),
|
||||||
|
"Output a single json document containing the specified information."
|
||||||
|
)
|
||||||
|
;
|
||||||
|
desc.add(extraOutput);
|
||||||
|
|
||||||
|
po::options_description metadataOptions("Metadata Options");
|
||||||
|
metadataOptions.add_options()
|
||||||
|
(
|
||||||
|
g_argMetadataHash.c_str(),
|
||||||
|
po::value<string>()->value_name(boost::join(g_metadataHashArgs, ",")),
|
||||||
|
"Choose hash method for the bytecode metadata or disable it."
|
||||||
|
)
|
||||||
|
(
|
||||||
|
g_argMetadataLiteral.c_str(),
|
||||||
|
"Store referenced sources as literal data in the metadata output."
|
||||||
|
)
|
||||||
|
;
|
||||||
|
desc.add(metadataOptions);
|
||||||
|
|
||||||
|
po::options_description optimizerOptions("Optimizer Options");
|
||||||
|
optimizerOptions.add_options()
|
||||||
|
(
|
||||||
|
g_argOptimize.c_str(),
|
||||||
|
"Enable bytecode optimizer."
|
||||||
|
)
|
||||||
|
(
|
||||||
|
g_argOptimizeRuns.c_str(),
|
||||||
|
po::value<unsigned>()->value_name("n")->default_value(200),
|
||||||
|
"Set for how many contract runs to optimize. "
|
||||||
|
"Lower values will optimize more for initial deployment cost, higher values will optimize more for high-frequency usage."
|
||||||
|
)
|
||||||
|
(
|
||||||
|
g_strOptimizeYul.c_str(),
|
||||||
|
("Legacy option, ignored. Use the general --" + g_argOptimize + " to enable Yul optimizer.").c_str()
|
||||||
|
)
|
||||||
|
(
|
||||||
|
g_strNoOptimizeYul.c_str(),
|
||||||
|
"Disable Yul optimizer in Solidity."
|
||||||
|
)
|
||||||
|
(
|
||||||
|
g_strYulOptimizations.c_str(),
|
||||||
|
po::value<string>()->value_name("steps"),
|
||||||
|
"Forces yul optimizer to use the specified sequence of optimization steps instead of the built-in one."
|
||||||
|
)
|
||||||
|
;
|
||||||
|
desc.add(optimizerOptions);
|
||||||
|
|
||||||
po::options_description allOptions = desc;
|
po::options_description allOptions = desc;
|
||||||
allOptions.add_options()(g_argInputFile.c_str(), po::value<vector<string>>(), "input file");
|
allOptions.add_options()(g_argInputFile.c_str(), po::value<vector<string>>(), "input file");
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ object "object" {
|
|||||||
|
|
||||||
|
|
||||||
Binary representation:
|
Binary representation:
|
||||||
0061736d0100000001160460000060017e017e60057e7e7e7e7e0060027f7f0002190108657468657265756d0c73746f7261676553746f7265000303060500010101020503010001060100071102066d656d6f72790200046d61696e00010acd01052b01017e0240420021004200200020002000200010054220200020002000420110054200a74220a710000b0b1f01017e024020004208864280fe0383200042088842ff01838421010b20010b1e01027e02402000100242108621022002200042108810028421010b20010b1e01027e02402000100342208621022002200042208810038421010b20010b4101007e02402000a7200110043703002000a74208a76aada7200210043703002000a74210a76aada7200310043703002000a74218a76aada7200410043703000b0b
|
0061736d0100000001160460000060017e017e60057e7e7e7e7e0060027f7f0002190108657468657265756d0c73746f7261676553746f7265000303060500010101020503010001060100071102066d656d6f72790200046d61696e00010acb01052b01017e0240420021004200200020002000200010054220200020002000420110054200a74220a710000b0b1f01017e024020004208864280fe0383200042088842ff01838421010b20010b1e01027e02402000100242108621022002200042108810028421010b20010b1e01027e02402000100342208621022002200042208810038421010b20010b3f0002402000a7200110043700002000a74208a76aada7200210043700002000a74210a76aada7200310043700002000a74218a76aada7200410043700000b0b
|
||||||
|
|
||||||
Text representation:
|
Text representation:
|
||||||
(module
|
(module
|
||||||
|
@ -154,7 +154,7 @@ object "object" {
|
|||||||
|
|
||||||
|
|
||||||
Binary representation:
|
Binary representation:
|
||||||
0061736d0100000001480a60000060017e017e60027e7e017e60037e7e7e017e60047e7e7e7e017e60057e7e7e7e7e0060087e7e7e7e7e7e7e7e0060087e7e7e7e7e7e7e7e017e60027f7f0060037f7f7f0002310208657468657265756d0c73746f7261676553746f7265000808657468657265756d0c63616c6c44617461436f70790009030e0d0003070407020704010101050605030100010610037e0142000b7e0142000b7e0142000b071102066d656d6f72790200046d61696e00020aa9090df302011f7e02404200210002402000200020002000100921012300210223012103230221040b2001210520022106200321072004210842012109200020008420002009848450ada745ada745ad210a02400340200aa745ad500d01024002402005200620072008200020002000420a1008210b2300210c2301210d2302210e0b0240200b200c200d200e1005210f2300211023012111230221120b200f20108420112012848450ada745ad42005204400c030b024020052006200720082000200020004202100621132300211423012115230221160b201320148420152016848450ada745ad42005204400c030b0240200520062007200820002000200042041006211723002118230121192302211a0b20172018842019201a848450ada745ad42005204400c010b0b0240200520062007200820002000200020091004211b2300211c2301211d2302211e0b201b2105201c2106201d2107201e21080c000b0b20002000200020002005200620072008100e0b0b2f01037e0240200020017c2105200520027c21032005200054ada72003200554ada772ada7ad21040b2004240020030b72010b7e0240200320077c210c200c42007c210b024020022006200c200354ada7200b200c54ada772ada7ad1003210d2300210e0b200d210a024020012005200e1003210f230021100b200f2109024020002004201010032111230021120b201121080b20092400200a2401200b240220080b2601047e0240200020018420022003848450ada7ad21070b20052400200624012007240220040b4901047e02402000200451ad42005204402001200551ad42005204402002200651ad42005204402003200751ad42005204404201210b0b0b0b0b0b20092400200a2401200b240220080b2d01027e024002402000200154ad21032003420151044042ffffffff0f2102052000200152ad21020b0b0b20020b960101087e02404200210c0240200020041007210d200d42005104400240200120051007210e200e42005104400240200220061007210f200f42005104402003200754ad210c05200f42015104404200210c054201210c0b0b0b05200e42015104404200210c054201210c0b0b0b05200d42015104404200210c054201210c0b0b0b200ca7ad210b0b20092400200a2401200b240220080b8f0101087e02404200200020018420028452ad4200520440000b4200200342208852ad4200520440000b4200a72003a7ada74220a710014200a7290300100c21084200a74208a76aada7290300100c21094200a74210a76aada7290300100c210a4200a74218a76aada7290300100c210b2008210420092105200a2106200b21070b20052400200624012007240220040b1f01017e024020004208864280fe0383200042088842ff01838421010b20010b1e01027e02402000100a421086210220022000421088100a8421010b20010b1e01027e02402000100b422086210220022000422088100b8421010b20010b4101007e02402000a72001100c3703002000a74208a76aada72002100c3703002000a74210a76aada72003100c3703002000a74218a76aada72004100c3703000b0b2701007e024042002000200120022003100d42202004200520062007100d4200a74220a710000b0b
|
0061736d0100000001480a60000060017e017e60027e7e017e60037e7e7e017e60047e7e7e7e017e60057e7e7e7e7e0060087e7e7e7e7e7e7e7e0060087e7e7e7e7e7e7e7e017e60027f7f0060037f7f7f0002310208657468657265756d0c73746f7261676553746f7265000808657468657265756d0c63616c6c44617461436f70790009030e0d0003070407020704010101050605030100010610037e0142000b7e0142000b7e0142000b071102066d656d6f72790200046d61696e00020aa5090df302011f7e02404200210002402000200020002000100921012300210223012103230221040b2001210520022106200321072004210842012109200020008420002009848450ada745ada745ad210a02400340200aa745ad500d01024002402005200620072008200020002000420a1008210b2300210c2301210d2302210e0b0240200b200c200d200e1005210f2300211023012111230221120b200f20108420112012848450ada745ad42005204400c030b024020052006200720082000200020004202100621132300211423012115230221160b201320148420152016848450ada745ad42005204400c030b0240200520062007200820002000200042041006211723002118230121192302211a0b20172018842019201a848450ada745ad42005204400c010b0b0240200520062007200820002000200020091004211b2300211c2301211d2302211e0b201b2105201c2106201d2107201e21080c000b0b20002000200020002005200620072008100e0b0b2f01037e0240200020017c2105200520027c21032005200054ada72003200554ada772ada7ad21040b2004240020030b72010b7e0240200320077c210c200c42007c210b024020022006200c200354ada7200b200c54ada772ada7ad1003210d2300210e0b200d210a024020012005200e1003210f230021100b200f2109024020002004201010032111230021120b201121080b20092400200a2401200b240220080b2601047e0240200020018420022003848450ada7ad21070b20052400200624012007240220040b4901047e02402000200451ad42005204402001200551ad42005204402002200651ad42005204402003200751ad42005204404201210b0b0b0b0b0b20092400200a2401200b240220080b2d01027e024002402000200154ad21032003420151044042ffffffff0f2102052000200152ad21020b0b0b20020b960101087e02404200210c0240200020041007210d200d42005104400240200120051007210e200e42005104400240200220061007210f200f42005104402003200754ad210c05200f42015104404200210c054201210c0b0b0b05200e42015104404200210c054201210c0b0b0b05200d42015104404200210c054201210c0b0b0b200ca7ad210b0b20092400200a2401200b240220080b8f0101087e02404200200020018420028452ad4200520440000b4200200342208852ad4200520440000b4200a72003a7ada74220a710014200a7290000100c21084200a74208a76aada7290000100c21094200a74210a76aada7290000100c210a4200a74218a76aada7290000100c210b2008210420092105200a2106200b21070b20052400200624012007240220040b1f01017e024020004208864280fe0383200042088842ff01838421010b20010b1e01027e02402000100a421086210220022000421088100a8421010b20010b1e01027e02402000100b422086210220022000422088100b8421010b20010b3f0002402000a72001100c3700002000a74208a76aada72002100c3700002000a74210a76aada72003100c3700002000a74218a76aada72004100c3700000b0b2500024042002000200120022003100d42202004200520062007100d4200a74220a710000b0b
|
||||||
|
|
||||||
Text representation:
|
Text representation:
|
||||||
(module
|
(module
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
--yul --yul-dialect ewasm --machine ewasm
|
@ -0,0 +1 @@
|
|||||||
|
Warning: Yul is still experimental. Please use the output with care.
|
@ -0,0 +1,8 @@
|
|||||||
|
object "object" {
|
||||||
|
code {
|
||||||
|
function main()
|
||||||
|
{
|
||||||
|
i64.store8(0x01:i32, 42:i64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
|
||||||
|
======= wasm_to_wasm_memory_instructions_alignment/input.yul (Ewasm) =======
|
||||||
|
|
||||||
|
Pretty printed source:
|
||||||
|
object "object" {
|
||||||
|
code {
|
||||||
|
function main()
|
||||||
|
{ i64.store8(0x01:i32, 42) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Binary representation:
|
||||||
|
0061736d01000000010401600000020100030201000503010001060100071102066d656d6f72790200046d61696e00000a0f010d0002404201a7422a3c00000b0b
|
||||||
|
|
||||||
|
Text representation:
|
||||||
|
(module
|
||||||
|
(memory $memory (export "memory") 1)
|
||||||
|
(export "main" (func $main))
|
||||||
|
|
||||||
|
(func $main
|
||||||
|
(block $label_
|
||||||
|
(i64.store8 (i32.wrap_i64 (i64.const 1)) (i64.const 42))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
)
|
@ -31,14 +31,14 @@
|
|||||||
{
|
{
|
||||||
"id": 4,
|
"id": 4,
|
||||||
"nodeType": "Block",
|
"nodeType": "Block",
|
||||||
"src": "42:48:1",
|
"src": "42:58:1",
|
||||||
"statements":
|
"statements":
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"AST":
|
"AST":
|
||||||
{
|
{
|
||||||
"nodeType": "YulBlock",
|
"nodeType": "YulBlock",
|
||||||
"src": "61:23:1",
|
"src": "61:33:1",
|
||||||
"statements":
|
"statements":
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
@ -48,11 +48,29 @@
|
|||||||
"body":
|
"body":
|
||||||
{
|
{
|
||||||
"nodeType": "YulBlock",
|
"nodeType": "YulBlock",
|
||||||
"src": "80:2:1",
|
"src": "79:2:1",
|
||||||
"statements": []
|
"statements": []
|
||||||
},
|
},
|
||||||
"nodeType": "YulCase",
|
"nodeType": "YulCase",
|
||||||
"src": "72:10:1",
|
"src": "72:9:1",
|
||||||
|
"value":
|
||||||
|
{
|
||||||
|
"kind": "number",
|
||||||
|
"nodeType": "YulLiteral",
|
||||||
|
"src": "77:1:1",
|
||||||
|
"type": "",
|
||||||
|
"value": "0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"body":
|
||||||
|
{
|
||||||
|
"nodeType": "YulBlock",
|
||||||
|
"src": "90:2:1",
|
||||||
|
"statements": []
|
||||||
|
},
|
||||||
|
"nodeType": "YulCase",
|
||||||
|
"src": "82:10:1",
|
||||||
"value": "default"
|
"value": "default"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@ -65,7 +83,7 @@
|
|||||||
"value": "0"
|
"value": "0"
|
||||||
},
|
},
|
||||||
"nodeType": "YulSwitch",
|
"nodeType": "YulSwitch",
|
||||||
"src": "63:19:1"
|
"src": "63:29:1"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -73,7 +91,7 @@
|
|||||||
"externalReferences": [],
|
"externalReferences": [],
|
||||||
"id": 3,
|
"id": 3,
|
||||||
"nodeType": "InlineAssembly",
|
"nodeType": "InlineAssembly",
|
||||||
"src": "52:32:1"
|
"src": "52:42:1"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -99,15 +117,15 @@
|
|||||||
"src": "42:0:1"
|
"src": "42:0:1"
|
||||||
},
|
},
|
||||||
"scope": 6,
|
"scope": 6,
|
||||||
"src": "17:73:1",
|
"src": "17:83:1",
|
||||||
"stateMutability": "view",
|
"stateMutability": "view",
|
||||||
"virtual": false,
|
"virtual": false,
|
||||||
"visibility": "public"
|
"visibility": "public"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"scope": 7,
|
"scope": 7,
|
||||||
"src": "0:92:1"
|
"src": "0:102:1"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"src": "0:93:1"
|
"src": "0:103:1"
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
contract C {
|
contract C {
|
||||||
function g() view public {
|
function g() view public {
|
||||||
assembly { switch 0 default {} }
|
assembly { switch 0 case 0 {} default {} }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,30 +91,30 @@
|
|||||||
[
|
[
|
||||||
null
|
null
|
||||||
],
|
],
|
||||||
"operations": "{\n switch 0\n default { }\n}"
|
"operations": "{\n switch 0\n case 0 { }\n default { }\n}"
|
||||||
},
|
},
|
||||||
"children": [],
|
"children": [],
|
||||||
"id": 3,
|
"id": 3,
|
||||||
"name": "InlineAssembly",
|
"name": "InlineAssembly",
|
||||||
"src": "52:32:1"
|
"src": "52:42:1"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"id": 4,
|
"id": 4,
|
||||||
"name": "Block",
|
"name": "Block",
|
||||||
"src": "42:48:1"
|
"src": "42:58:1"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"id": 5,
|
"id": 5,
|
||||||
"name": "FunctionDefinition",
|
"name": "FunctionDefinition",
|
||||||
"src": "17:73:1"
|
"src": "17:83:1"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"id": 6,
|
"id": 6,
|
||||||
"name": "ContractDefinition",
|
"name": "ContractDefinition",
|
||||||
"src": "0:92:1"
|
"src": "0:102:1"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"id": 7,
|
"id": 7,
|
||||||
"name": "SourceUnit",
|
"name": "SourceUnit",
|
||||||
"src": "0:93:1"
|
"src": "0:103:1"
|
||||||
}
|
}
|
||||||
|
@ -314,9 +314,17 @@ BOOST_AUTO_TEST_CASE(switch_duplicate_case)
|
|||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(switch_invalid_expression)
|
BOOST_AUTO_TEST_CASE(switch_invalid_expression)
|
||||||
{
|
{
|
||||||
CHECK_PARSE_ERROR("{ switch {} default {} }", ParserError, "Literal or identifier expected.");
|
CHECK_PARSE_ERROR("{ switch {} case 1 {} default {} }", ParserError, "Literal or identifier expected.");
|
||||||
CHECK_PARSE_ERROR("{ switch mload default {} }", ParserError, "Expected '(' but got reserved keyword 'default'");
|
CHECK_PARSE_ERROR(
|
||||||
CHECK_PARSE_ERROR("{ switch mstore(1, 1) default {} }", TypeError, "Expected expression to evaluate to one value, but got 0 values instead.");
|
"{ switch mload case 1 {} default {} }",
|
||||||
|
ParserError,
|
||||||
|
"Expected '(' but got reserved keyword 'case'"
|
||||||
|
);
|
||||||
|
CHECK_PARSE_ERROR(
|
||||||
|
"{ switch mstore(1, 1) case 1 {} default {} }",
|
||||||
|
TypeError,
|
||||||
|
"Expected expression to evaluate to one value, but got 0 values instead."
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(switch_default_before_case)
|
BOOST_AUTO_TEST_CASE(switch_default_before_case)
|
||||||
|
20
test/libsolidity/semanticTests/array/index_access.sol
Normal file
20
test/libsolidity/semanticTests/array/index_access.sol
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
contract C {
|
||||||
|
function to_little_endian_64(uint64 value) public pure returns (bytes memory ret) {
|
||||||
|
ret = new bytes(8);
|
||||||
|
bytes8 bytesValue = bytes8(value);
|
||||||
|
// Byteswapping during copying to bytes.
|
||||||
|
ret[0] = bytesValue[7];
|
||||||
|
ret[1] = bytesValue[6];
|
||||||
|
ret[2] = bytesValue[5];
|
||||||
|
ret[3] = bytesValue[4];
|
||||||
|
ret[4] = bytesValue[3];
|
||||||
|
ret[5] = bytesValue[2];
|
||||||
|
ret[6] = bytesValue[1];
|
||||||
|
ret[7] = bytesValue[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// to_little_endian_64(uint64): 0 -> 0x20, 8, 0x00
|
||||||
|
// to_little_endian_64(uint64): 0x0102030405060708 -> 0x20, 8, 0x0807060504030201000000000000000000000000000000000000000000000000
|
@ -0,0 +1,29 @@
|
|||||||
|
contract C {
|
||||||
|
struct Y {
|
||||||
|
uint a;
|
||||||
|
uint b;
|
||||||
|
}
|
||||||
|
mapping(uint256 => Y)[] public m;
|
||||||
|
mapping(uint256 => Y)[3] public n;
|
||||||
|
constructor() public {
|
||||||
|
m.push();
|
||||||
|
m.push();
|
||||||
|
m[1][0].a = 1;
|
||||||
|
m[1][0].b = 2;
|
||||||
|
m[1][1].a = 3;
|
||||||
|
m[1][1].b = 4;
|
||||||
|
n[1][0].a = 7;
|
||||||
|
n[1][0].b = 8;
|
||||||
|
n[1][1].a = 9;
|
||||||
|
n[1][1].b = 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// m(uint256,uint256): 0, 0 -> 0x00, 0x00
|
||||||
|
// m(uint256,uint256): 1, 0 -> 1, 2
|
||||||
|
// m(uint256,uint256): 1, 1 -> 3, 4
|
||||||
|
// m(uint256,uint256): 1, 2 -> 0x00, 0x00
|
||||||
|
// n(uint256,uint256): 0, 0 -> 0x00, 0x00
|
||||||
|
// n(uint256,uint256): 1, 0 -> 7, 8
|
||||||
|
// n(uint256,uint256): 1, 1 -> 9, 0x0a
|
||||||
|
// n(uint256,uint256): 1, 2 -> 0x00, 0x00
|
@ -0,0 +1,27 @@
|
|||||||
|
contract C {
|
||||||
|
struct Y {
|
||||||
|
uint a;
|
||||||
|
uint b;
|
||||||
|
}
|
||||||
|
mapping(uint256 => Y[]) public m;
|
||||||
|
mapping(uint256 => Y[3]) public n;
|
||||||
|
constructor() public {
|
||||||
|
m[1].push().a = 1;
|
||||||
|
m[1][0].b = 2;
|
||||||
|
m[1].push().a = 3;
|
||||||
|
m[1][1].b = 4;
|
||||||
|
n[1][0].a = 7;
|
||||||
|
n[1][0].b = 8;
|
||||||
|
n[1][1].a = 9;
|
||||||
|
n[1][1].b = 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// m(uint256,uint256): 0, 0 -> FAILURE
|
||||||
|
// m(uint256,uint256): 1, 0 -> 1, 2
|
||||||
|
// m(uint256,uint256): 1, 1 -> 3, 4
|
||||||
|
// m(uint256,uint256): 1, 2 -> FAILURE
|
||||||
|
// n(uint256,uint256): 0, 0 -> 0x00, 0x00
|
||||||
|
// n(uint256,uint256): 1, 0 -> 7, 8
|
||||||
|
// n(uint256,uint256): 1, 1 -> 9, 0x0a
|
||||||
|
// n(uint256,uint256): 1, 2 -> 0x00, 0x00
|
18
test/libsolidity/semanticTests/getters/mapping_of_string.sol
Normal file
18
test/libsolidity/semanticTests/getters/mapping_of_string.sol
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
contract C {
|
||||||
|
mapping(string => uint8[3]) public x;
|
||||||
|
constructor() public {
|
||||||
|
x["abc"][0] = 1;
|
||||||
|
x["abc"][2] = 3;
|
||||||
|
x["abc"][1] = 2;
|
||||||
|
x["def"][1] = 9;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// x(string,uint256): 0x40, 0, 3, "abc" -> 1
|
||||||
|
// x(string,uint256): 0x40, 1, 3, "abc" -> 2
|
||||||
|
// x(string,uint256): 0x40, 2, 3, "abc" -> 3
|
||||||
|
// x(string,uint256): 0x40, 0, 3, "def" -> 0x00
|
||||||
|
// x(string,uint256): 0x40, 1, 3, "def" -> 9
|
||||||
|
// x(string,uint256): 0x40, 2, 3, "def" -> 0x00
|
18
test/libsolidity/semanticTests/getters/struct_with_bytes.sol
Normal file
18
test/libsolidity/semanticTests/getters/struct_with_bytes.sol
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
contract C {
|
||||||
|
struct S {
|
||||||
|
uint a;
|
||||||
|
bytes b;
|
||||||
|
mapping(uint => uint) c;
|
||||||
|
uint[] d;
|
||||||
|
}
|
||||||
|
uint shifter;
|
||||||
|
S public s;
|
||||||
|
constructor() public {
|
||||||
|
s.a = 7;
|
||||||
|
s.b = "abc";
|
||||||
|
s.c[0] = 9;
|
||||||
|
s.d.push(10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// s() -> 7, 0x40, 3, 0x6162630000000000000000000000000000000000000000000000000000000000
|
@ -0,0 +1,23 @@
|
|||||||
|
pragma experimental SMTChecker;
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
uint[][] a;
|
||||||
|
uint[][][] c;
|
||||||
|
uint[] d;
|
||||||
|
function f() public {
|
||||||
|
a.push();
|
||||||
|
uint[] storage b = a[0];
|
||||||
|
c[0][0][0] = 12;
|
||||||
|
d[5] = 7;
|
||||||
|
b.push(8);
|
||||||
|
assert(a[0].length == 0);
|
||||||
|
// Safe but knowledge about `c` is erased because `b` could be pointing to `c[x][y]`.
|
||||||
|
assert(c[0][0][0] == 12);
|
||||||
|
// Safe but knowledge about `d` is erased because `b` could be pointing to `d`.
|
||||||
|
assert(d[5] == 7);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// Warning: (193-217): Assertion violation happens here
|
||||||
|
// Warning: (309-333): Assertion violation happens here
|
||||||
|
// Warning: (419-436): Assertion violation happens here
|
@ -0,0 +1,12 @@
|
|||||||
|
pragma experimental SMTChecker;
|
||||||
|
contract test {
|
||||||
|
function f() internal pure {
|
||||||
|
ufixed a = uint64(1) + ufixed(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// Warning: (80-88): Unused local variable.
|
||||||
|
// Warning: (91-100): Type conversion is not yet fully supported and might yield false positives.
|
||||||
|
// Warning: (103-112): Type conversion is not yet fully supported and might yield false positives.
|
||||||
|
// Warning: (91-112): Underflow (resulting value less than 0) happens here
|
||||||
|
// Warning: (91-112): Overflow (resulting value larger than 2**256 - 1) happens here
|
@ -0,0 +1,8 @@
|
|||||||
|
pragma experimental SMTChecker;
|
||||||
|
contract C {
|
||||||
|
fixed[] b;
|
||||||
|
function f() internal { b[0] += 1; }
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// Warning: (84-93): Underflow (resulting value less than 0) happens here
|
||||||
|
// Warning: (84-93): Overflow (resulting value larger than 2**256 - 1) happens here
|
6
test/libsolidity/smtCheckerTests/types/tuple_tuple.sol
Normal file
6
test/libsolidity/smtCheckerTests/types/tuple_tuple.sol
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
pragma experimental SMTChecker;
|
||||||
|
contract C {
|
||||||
|
function f3() public pure {
|
||||||
|
((, ), ) = ((7, 8), 9);
|
||||||
|
}
|
||||||
|
}
|
@ -1,15 +1,7 @@
|
|||||||
contract C {
|
contract C {
|
||||||
struct S { bool f; }
|
struct S { bool f; }
|
||||||
S s;
|
S s;
|
||||||
function f(uint256 a) internal pure {
|
function f(bool flag) internal pure {
|
||||||
S storage c;
|
|
||||||
assembly {
|
|
||||||
switch a
|
|
||||||
default { c_slot := s_slot }
|
|
||||||
}
|
|
||||||
c;
|
|
||||||
}
|
|
||||||
function g(bool flag) internal pure {
|
|
||||||
S storage c;
|
S storage c;
|
||||||
assembly {
|
assembly {
|
||||||
switch flag
|
switch flag
|
||||||
@ -18,7 +10,7 @@ contract C {
|
|||||||
}
|
}
|
||||||
c;
|
c;
|
||||||
}
|
}
|
||||||
function h(uint256 a) internal pure {
|
function g(uint256 a) internal pure {
|
||||||
S storage c;
|
S storage c;
|
||||||
assembly {
|
assembly {
|
||||||
switch a
|
switch a
|
||||||
|
@ -1,20 +1,14 @@
|
|||||||
contract C {
|
contract C {
|
||||||
struct S { bool f; }
|
struct S { bool f; }
|
||||||
S s;
|
S s;
|
||||||
function f(uint256 a) internal pure returns (S storage c) {
|
function f(bool flag) internal pure returns (S storage c) {
|
||||||
assembly {
|
|
||||||
switch a
|
|
||||||
default { c_slot := s_slot }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function g(bool flag) internal pure returns (S storage c) {
|
|
||||||
assembly {
|
assembly {
|
||||||
switch flag
|
switch flag
|
||||||
case 0 { c_slot := s_slot }
|
case 0 { c_slot := s_slot }
|
||||||
default { c_slot := s_slot }
|
default { c_slot := s_slot }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function h(uint256 a) internal pure returns (S storage c) {
|
function g(uint256 a) internal pure returns (S storage c) {
|
||||||
assembly {
|
assembly {
|
||||||
switch a
|
switch a
|
||||||
case 0 { revert(0, 0) }
|
case 0 { revert(0, 0) }
|
||||||
|
@ -0,0 +1,12 @@
|
|||||||
|
contract C {
|
||||||
|
struct S { bool f; }
|
||||||
|
S s;
|
||||||
|
function f(uint256 a) internal pure returns (S storage c) {
|
||||||
|
assembly {
|
||||||
|
switch a
|
||||||
|
default { c_slot := s_slot }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// Warning: (142-195): "switch" statement with only a default case.
|
@ -87,12 +87,12 @@ pair<shared_ptr<Block>, shared_ptr<yul::AsmAnalysisInfo>> yul::test::parse(
|
|||||||
shared_ptr<Object> parserResult = yul::ObjectParser(errorReporter, _dialect).parse(scanner, false);
|
shared_ptr<Object> parserResult = yul::ObjectParser(errorReporter, _dialect).parse(scanner, false);
|
||||||
if (!parserResult)
|
if (!parserResult)
|
||||||
return {};
|
return {};
|
||||||
if (!parserResult->code || !errorReporter.errors().empty())
|
if (!parserResult->code || errorReporter.hasErrors())
|
||||||
return {};
|
return {};
|
||||||
shared_ptr<AsmAnalysisInfo> analysisInfo = make_shared<AsmAnalysisInfo>();
|
shared_ptr<AsmAnalysisInfo> analysisInfo = make_shared<AsmAnalysisInfo>();
|
||||||
AsmAnalyzer analyzer(*analysisInfo, errorReporter, _dialect, {}, parserResult->dataNames());
|
AsmAnalyzer analyzer(*analysisInfo, errorReporter, _dialect, {}, parserResult->dataNames());
|
||||||
// TODO this should be done recursively.
|
// TODO this should be done recursively.
|
||||||
if (!analyzer.analyze(*parserResult->code) || !errorReporter.errors().empty())
|
if (!analyzer.analyze(*parserResult->code) || errorReporter.hasErrors())
|
||||||
return {};
|
return {};
|
||||||
return {std::move(parserResult->code), std::move(analysisInfo)};
|
return {std::move(parserResult->code), std::move(analysisInfo)};
|
||||||
}
|
}
|
||||||
|
@ -332,7 +332,7 @@ BOOST_FIXTURE_TEST_CASE(if_statement_custom_weights, CustomWeightFixture)
|
|||||||
BOOST_AUTO_TEST_CASE(switch_statement_tiny)
|
BOOST_AUTO_TEST_CASE(switch_statement_tiny)
|
||||||
{
|
{
|
||||||
BOOST_CHECK_EQUAL(codeSize(
|
BOOST_CHECK_EQUAL(codeSize(
|
||||||
"{ switch calldatasize() default {} }"
|
"{ switch calldatasize() case 0 {} }"
|
||||||
), 4);
|
), 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -362,7 +362,7 @@ bool YulOptimizerTest::parse(ostream& _stream, string const& _linePrefix, bool c
|
|||||||
ErrorList errors;
|
ErrorList errors;
|
||||||
soltestAssert(m_dialect, "");
|
soltestAssert(m_dialect, "");
|
||||||
std::tie(m_ast, m_analysisInfo) = yul::test::parse(m_source, *m_dialect, errors);
|
std::tie(m_ast, m_analysisInfo) = yul::test::parse(m_source, *m_dialect, errors);
|
||||||
if (!m_ast || !m_analysisInfo || !errors.empty())
|
if (!m_ast || !m_analysisInfo || !Error::containsOnlyWarnings(errors))
|
||||||
{
|
{
|
||||||
AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Error parsing source." << endl;
|
AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Error parsing source." << endl;
|
||||||
printErrors(_stream, errors);
|
printErrors(_stream, errors);
|
||||||
|
@ -118,7 +118,7 @@ BOOST_AUTO_TEST_CASE(geneAddition_should_be_able_to_insert_before_first_position
|
|||||||
BOOST_TEST(mutatedChromosome.length() > chromosome.length());
|
BOOST_TEST(mutatedChromosome.length() > chromosome.length());
|
||||||
|
|
||||||
vector<string> suffix(
|
vector<string> suffix(
|
||||||
mutatedChromosome.optimisationSteps().end() - chromosome.length(),
|
mutatedChromosome.optimisationSteps().end() - static_cast<ptrdiff_t>(chromosome.length()),
|
||||||
mutatedChromosome.optimisationSteps().end()
|
mutatedChromosome.optimisationSteps().end()
|
||||||
);
|
);
|
||||||
BOOST_TEST(suffix == chromosome.optimisationSteps());
|
BOOST_TEST(suffix == chromosome.optimisationSteps());
|
||||||
@ -135,7 +135,7 @@ BOOST_AUTO_TEST_CASE(geneAddition_should_be_able_to_insert_after_last_position)
|
|||||||
|
|
||||||
vector<string> prefix(
|
vector<string> prefix(
|
||||||
mutatedChromosome.optimisationSteps().begin(),
|
mutatedChromosome.optimisationSteps().begin(),
|
||||||
mutatedChromosome.optimisationSteps().begin() + chromosome.length()
|
mutatedChromosome.optimisationSteps().begin() + static_cast<ptrdiff_t>(chromosome.length())
|
||||||
);
|
);
|
||||||
BOOST_TEST(prefix == chromosome.optimisationSteps());
|
BOOST_TEST(prefix == chromosome.optimisationSteps());
|
||||||
}
|
}
|
||||||
@ -179,8 +179,8 @@ BOOST_AUTO_TEST_CASE(alternativeMutations_should_choose_between_mutations_with_g
|
|||||||
for (size_t i = 0; i < 10; ++i)
|
for (size_t i = 0; i < 10; ++i)
|
||||||
{
|
{
|
||||||
Chromosome mutatedChromosome = mutation(chromosome);
|
Chromosome mutatedChromosome = mutation(chromosome);
|
||||||
cCount += static_cast<int>(mutatedChromosome == Chromosome("c"));
|
cCount += (mutatedChromosome == Chromosome("c") ? 1 : 0);
|
||||||
fCount += static_cast<int>(mutatedChromosome == Chromosome("f"));
|
fCount += (mutatedChromosome == Chromosome("f") ? 1 : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This particular seed results in 7 "c"s out of 10 which looks plausible given the 80% chance.
|
// This particular seed results in 7 "c"s out of 10 which looks plausible given the 80% chance.
|
||||||
|
@ -135,7 +135,7 @@ BOOST_FIXTURE_TEST_CASE(makeRandom_should_get_chromosome_lengths_from_specified_
|
|||||||
size_t maxLength = 5;
|
size_t maxLength = 5;
|
||||||
assert(chromosomeCount % maxLength == 0);
|
assert(chromosomeCount % maxLength == 0);
|
||||||
|
|
||||||
auto nextLength = [counter = 0, maxLength]() mutable { return counter++ % maxLength; };
|
auto nextLength = [counter = 0ul, maxLength]() mutable { return counter++ % maxLength; };
|
||||||
auto population = Population::makeRandom(m_fitnessMetric, chromosomeCount, nextLength);
|
auto population = Population::makeRandom(m_fitnessMetric, chromosomeCount, nextLength);
|
||||||
|
|
||||||
// We can't rely on the order since the population sorts its chromosomes immediately but
|
// We can't rely on the order since the population sorts its chromosomes immediately but
|
||||||
|
@ -72,9 +72,13 @@ size_t phaser::test::countDifferences(Chromosome const& _chromosome1, Chromosome
|
|||||||
{
|
{
|
||||||
size_t count = 0;
|
size_t count = 0;
|
||||||
for (size_t i = 0; i < min(_chromosome1.length(), _chromosome2.length()); ++i)
|
for (size_t i = 0; i < min(_chromosome1.length(), _chromosome2.length()); ++i)
|
||||||
count += static_cast<int>(_chromosome1.optimisationSteps()[i] != _chromosome2.optimisationSteps()[i]);
|
if (_chromosome1.optimisationSteps()[i] != _chromosome2.optimisationSteps()[i])
|
||||||
|
++count;
|
||||||
|
|
||||||
return count + abs(static_cast<int>(_chromosome1.length() - _chromosome2.length()));
|
return count + static_cast<size_t>(abs(
|
||||||
|
static_cast<long>(_chromosome1.length()) -
|
||||||
|
static_cast<long>(_chromosome2.length())
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
TemporaryDirectory::TemporaryDirectory(std::string const& _prefix):
|
TemporaryDirectory::TemporaryDirectory(std::string const& _prefix):
|
||||||
|
@ -155,7 +155,7 @@ Population ClassicGeneticAlgorithm::select(Population _population, size_t _selec
|
|||||||
vector<Individual> selectedIndividuals;
|
vector<Individual> selectedIndividuals;
|
||||||
for (size_t i = 0; i < _selectionSize; ++i)
|
for (size_t i = 0; i < _selectionSize; ++i)
|
||||||
{
|
{
|
||||||
uint32_t ball = SimulationRNG::uniformInt(0, rouletteRange - 1);
|
size_t ball = SimulationRNG::uniformInt(0, rouletteRange - 1);
|
||||||
|
|
||||||
size_t cumulativeFitness = 0;
|
size_t cumulativeFitness = 0;
|
||||||
for (auto const& individual: _population.individuals())
|
for (auto const& individual: _population.individuals())
|
||||||
|
@ -126,12 +126,12 @@ ChromosomePair fixedPointSwap(
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
Chromosome(
|
Chromosome(
|
||||||
vector<string>(begin1, begin1 + _crossoverPoint) +
|
vector<string>(begin1, begin1 + static_cast<ptrdiff_t>(_crossoverPoint)) +
|
||||||
vector<string>(begin2 + _crossoverPoint, end2)
|
vector<string>(begin2 + static_cast<ptrdiff_t>(_crossoverPoint), end2)
|
||||||
),
|
),
|
||||||
Chromosome(
|
Chromosome(
|
||||||
vector<string>(begin2, begin2 + _crossoverPoint) +
|
vector<string>(begin2, begin2 + static_cast<ptrdiff_t>(_crossoverPoint)) +
|
||||||
vector<string>(begin1 + _crossoverPoint, end1)
|
vector<string>(begin1 + static_cast<ptrdiff_t>(_crossoverPoint), end1)
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -196,8 +196,8 @@ ChromosomePair fixedTwoPointSwap(
|
|||||||
assert(_crossoverPoint2 <= _chromosome1.length());
|
assert(_crossoverPoint2 <= _chromosome1.length());
|
||||||
assert(_crossoverPoint2 <= _chromosome2.length());
|
assert(_crossoverPoint2 <= _chromosome2.length());
|
||||||
|
|
||||||
size_t lowPoint = min(_crossoverPoint1, _crossoverPoint2);
|
auto lowPoint = static_cast<ptrdiff_t>(min(_crossoverPoint1, _crossoverPoint2));
|
||||||
size_t highPoint = max(_crossoverPoint1, _crossoverPoint2);
|
auto highPoint = static_cast<ptrdiff_t>(max(_crossoverPoint1, _crossoverPoint2));
|
||||||
|
|
||||||
auto begin1 = _chromosome1.optimisationSteps().begin();
|
auto begin1 = _chromosome1.optimisationSteps().begin();
|
||||||
auto begin2 = _chromosome2.optimisationSteps().begin();
|
auto begin2 = _chromosome2.optimisationSteps().begin();
|
||||||
@ -282,17 +282,17 @@ ChromosomePair uniformSwap(Chromosome const& _chromosome1, Chromosome const& _ch
|
|||||||
if (_chromosome1.length() > minLength)
|
if (_chromosome1.length() > minLength)
|
||||||
{
|
{
|
||||||
if (swapTail)
|
if (swapTail)
|
||||||
steps2.insert(steps2.end(), begin1 + minLength, end1);
|
steps2.insert(steps2.end(), begin1 + static_cast<ptrdiff_t>(minLength), end1);
|
||||||
else
|
else
|
||||||
steps1.insert(steps1.end(), begin1 + minLength, end1);
|
steps1.insert(steps1.end(), begin1 + static_cast<ptrdiff_t>(minLength), end1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_chromosome2.length() > minLength)
|
if (_chromosome2.length() > minLength)
|
||||||
{
|
{
|
||||||
if (swapTail)
|
if (swapTail)
|
||||||
steps1.insert(steps1.end(), begin2 + minLength, end2);
|
steps1.insert(steps1.end(), begin2 + static_cast<ptrdiff_t>(minLength), end2);
|
||||||
else
|
else
|
||||||
steps2.insert(steps2.end(), begin2 + minLength, end2);
|
steps2.insert(steps2.end(), begin2 + static_cast<ptrdiff_t>(minLength), end2);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {Chromosome(steps1), Chromosome(steps2)};
|
return {Chromosome(steps1), Chromosome(steps2)};
|
||||||
|
@ -30,7 +30,7 @@ vector<tuple<size_t, size_t>> RandomPairSelection::materialise(size_t _poolSize)
|
|||||||
if (_poolSize < 2)
|
if (_poolSize < 2)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
size_t count = static_cast<size_t>(round(_poolSize * m_selectionSize));
|
auto count = static_cast<size_t>(round(_poolSize * m_selectionSize));
|
||||||
|
|
||||||
vector<tuple<size_t, size_t>> selection;
|
vector<tuple<size_t, size_t>> selection;
|
||||||
for (size_t i = 0; i < count; ++i)
|
for (size_t i = 0; i < count; ++i)
|
||||||
@ -64,7 +64,10 @@ vector<tuple<size_t, size_t>> PairsFromRandomSubset::materialise(size_t _poolSiz
|
|||||||
} while (selectedIndices.size() % 2 != 0);
|
} while (selectedIndices.size() % 2 != 0);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
selectedIndices.erase(selectedIndices.begin() + SimulationRNG::uniformInt(0, selectedIndices.size() - 1));
|
selectedIndices.erase(
|
||||||
|
selectedIndices.begin() +
|
||||||
|
static_cast<ptrdiff_t>(SimulationRNG::uniformInt(0, selectedIndices.size() - 1))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
assert(selectedIndices.size() % 2 == 0);
|
assert(selectedIndices.size() % 2 == 0);
|
||||||
|
|
||||||
@ -73,14 +76,14 @@ vector<tuple<size_t, size_t>> PairsFromRandomSubset::materialise(size_t _poolSiz
|
|||||||
{
|
{
|
||||||
size_t position1 = SimulationRNG::uniformInt(0, selectedIndices.size() - 1);
|
size_t position1 = SimulationRNG::uniformInt(0, selectedIndices.size() - 1);
|
||||||
size_t value1 = selectedIndices[position1];
|
size_t value1 = selectedIndices[position1];
|
||||||
selectedIndices.erase(selectedIndices.begin() + position1);
|
selectedIndices.erase(selectedIndices.begin() + static_cast<ptrdiff_t>(position1));
|
||||||
size_t position2 = SimulationRNG::uniformInt(0, selectedIndices.size() - 1);
|
size_t position2 = SimulationRNG::uniformInt(0, selectedIndices.size() - 1);
|
||||||
size_t value2 = selectedIndices[position2];
|
size_t value2 = selectedIndices[position2];
|
||||||
selectedIndices.erase(selectedIndices.begin() + position2);
|
selectedIndices.erase(selectedIndices.begin() + static_cast<ptrdiff_t>(position2));
|
||||||
|
|
||||||
selectedPairs.push_back({value1, value2});
|
selectedPairs.emplace_back(value1, value2);
|
||||||
}
|
}
|
||||||
assert(selectedIndices.size() == 0);
|
assert(selectedIndices.empty());
|
||||||
|
|
||||||
return selectedPairs;
|
return selectedPairs;
|
||||||
}
|
}
|
||||||
|
@ -17,12 +17,17 @@
|
|||||||
|
|
||||||
#include <tools/yulPhaser/SimulationRNG.h>
|
#include <tools/yulPhaser/SimulationRNG.h>
|
||||||
|
|
||||||
|
// NOTE: The code would work with std::random but the results for a given seed would not be reproducible
|
||||||
|
// across different STL implementations. Boost does not guarantee this either but at least it has only one
|
||||||
|
// implementation. Reproducibility is not a hard requirement for yul-phaser but it's nice to have.
|
||||||
#include <boost/random/bernoulli_distribution.hpp>
|
#include <boost/random/bernoulli_distribution.hpp>
|
||||||
#include <boost/random/binomial_distribution.hpp>
|
#include <boost/random/binomial_distribution.hpp>
|
||||||
#include <boost/random/uniform_int_distribution.hpp>
|
#include <boost/random/uniform_int_distribution.hpp>
|
||||||
|
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
using namespace solidity;
|
using namespace solidity;
|
||||||
using namespace solidity::phaser;
|
using namespace solidity::phaser;
|
||||||
|
|
||||||
@ -30,23 +35,27 @@ thread_local boost::random::mt19937 SimulationRNG::s_generator(SimulationRNG::ge
|
|||||||
|
|
||||||
bool SimulationRNG::bernoulliTrial(double _successProbability)
|
bool SimulationRNG::bernoulliTrial(double _successProbability)
|
||||||
{
|
{
|
||||||
boost::random::bernoulli_distribution<> distribution(_successProbability);
|
boost::random::bernoulli_distribution<double> distribution(_successProbability);
|
||||||
|
|
||||||
return static_cast<bool>(distribution(s_generator));
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t SimulationRNG::uniformInt(uint32_t _min, uint32_t _max)
|
|
||||||
{
|
|
||||||
boost::random::uniform_int_distribution<> distribution(_min, _max);
|
|
||||||
return distribution(s_generator);
|
return distribution(s_generator);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t SimulationRNG::binomialInt(uint32_t _numTrials, double _successProbability)
|
size_t SimulationRNG::uniformInt(size_t _min, size_t _max)
|
||||||
{
|
{
|
||||||
boost::random::binomial_distribution<> distribution(_numTrials, _successProbability);
|
boost::random::uniform_int_distribution<size_t> distribution(_min, _max);
|
||||||
return distribution(s_generator);
|
return distribution(s_generator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t SimulationRNG::binomialInt(size_t _numTrials, double _successProbability)
|
||||||
|
{
|
||||||
|
// NOTE: binomial_distribution<size_t> would not work because it internally tries to use abs()
|
||||||
|
// and fails to compile due to ambiguous conversion.
|
||||||
|
assert(_numTrials <= static_cast<size_t>(numeric_limits<long>::max()));
|
||||||
|
|
||||||
|
boost::random::binomial_distribution<long> distribution(static_cast<long>(_numTrials), _successProbability);
|
||||||
|
return static_cast<size_t>(distribution(s_generator));
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t SimulationRNG::generateSeed()
|
uint32_t SimulationRNG::generateSeed()
|
||||||
{
|
{
|
||||||
// This is not a secure way to seed the generator but it's good enough for simulation purposes.
|
// This is not a secure way to seed the generator but it's good enough for simulation purposes.
|
||||||
|
@ -38,8 +38,8 @@ class SimulationRNG
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static bool bernoulliTrial(double _successProbability);
|
static bool bernoulliTrial(double _successProbability);
|
||||||
static uint32_t uniformInt(uint32_t _min, uint32_t _max);
|
static size_t uniformInt(size_t _min, size_t _max);
|
||||||
static uint32_t binomialInt(uint32_t _numTrials, double _successProbability);
|
static size_t binomialInt(size_t _numTrials, double _successProbability);
|
||||||
|
|
||||||
/// Resets generator to a known state given by the @a seed. Given the same seed, a fixed
|
/// Resets generator to a known state given by the @a seed. Given the same seed, a fixed
|
||||||
/// sequence of calls to the members generating random values is guaranteed to produce the
|
/// sequence of calls to the members generating random values is guaranteed to produce the
|
||||||
|
Loading…
Reference in New Issue
Block a user