mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #8983 from ethereum/develop
Merge develop into breaking.
This commit is contained in:
commit
d422a406ba
@ -173,6 +173,18 @@ defaults:
|
|||||||
- store_test_results: *store_test_results
|
- store_test_results: *store_test_results
|
||||||
- store_artifacts: *artifacts_test_results
|
- store_artifacts: *artifacts_test_results
|
||||||
|
|
||||||
|
- test_asan_clang: &test_asan_clang
|
||||||
|
<<: *test_ubuntu2004_clang
|
||||||
|
steps:
|
||||||
|
- checkout
|
||||||
|
- attach_workspace:
|
||||||
|
at: build
|
||||||
|
- run:
|
||||||
|
<<: *run_soltest
|
||||||
|
no_output_timeout: 30m
|
||||||
|
- store_test_results: *store_test_results
|
||||||
|
- store_artifacts: *artifacts_test_results
|
||||||
|
|
||||||
# --------------------------------------------------------------------------
|
# --------------------------------------------------------------------------
|
||||||
# Workflow Templates
|
# Workflow Templates
|
||||||
|
|
||||||
@ -216,6 +228,11 @@ defaults:
|
|||||||
requires:
|
requires:
|
||||||
- b_ubu_asan
|
- b_ubu_asan
|
||||||
|
|
||||||
|
- workflow_ubuntu2004_asan_clang: &workflow_ubuntu2004_asan_clang
|
||||||
|
<<: *workflow_trigger_on_tags
|
||||||
|
requires:
|
||||||
|
- b_ubu_asan_clang
|
||||||
|
|
||||||
- workflow_emscripten: &workflow_emscripten
|
- workflow_emscripten: &workflow_emscripten
|
||||||
<<: *workflow_trigger_on_tags
|
<<: *workflow_trigger_on_tags
|
||||||
requires:
|
requires:
|
||||||
@ -380,6 +397,20 @@ jobs:
|
|||||||
- store_artifacts: *artifacts_solc
|
- store_artifacts: *artifacts_solc
|
||||||
- persist_to_workspace: *artifacts_executables
|
- persist_to_workspace: *artifacts_executables
|
||||||
|
|
||||||
|
|
||||||
|
b_ubu_asan_clang: &build_ubuntu2004_clang
|
||||||
|
docker:
|
||||||
|
- image: ethereum/solidity-buildpack-deps:ubuntu2004-clang-<< pipeline.parameters.ubuntu-2004-clang-docker-image-rev >>
|
||||||
|
environment:
|
||||||
|
CC: clang
|
||||||
|
CXX: clang++
|
||||||
|
CMAKE_OPTIONS: -DSANITIZE=address
|
||||||
|
steps:
|
||||||
|
- checkout
|
||||||
|
- run: *run_build
|
||||||
|
- store_artifacts: *artifacts_solc
|
||||||
|
- persist_to_workspace: *artifacts_executables
|
||||||
|
|
||||||
b_ubu: &build_ubuntu2004
|
b_ubu: &build_ubuntu2004
|
||||||
docker:
|
docker:
|
||||||
- image: ethereum/solidity-buildpack-deps:ubuntu2004-<< pipeline.parameters.ubuntu-2004-docker-image-rev >>
|
- image: ethereum/solidity-buildpack-deps:ubuntu2004-<< pipeline.parameters.ubuntu-2004-docker-image-rev >>
|
||||||
@ -558,27 +589,15 @@ jobs:
|
|||||||
|
|
||||||
b_ems:
|
b_ems:
|
||||||
docker:
|
docker:
|
||||||
- image: trzeci/emscripten:sdk-tag-1.39.3-64bit
|
- image: ethereum/solidity-buildpack-deps:emsdk-1.39.15-1
|
||||||
environment:
|
environment:
|
||||||
TERM: xterm
|
TERM: xterm
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- restore_cache:
|
|
||||||
name: Restore Boost build
|
|
||||||
key: &boost-cache-key emscripten-boost-{{ checksum "scripts/travis-emscripten/install_deps.sh" }}{{ checksum "scripts/build_emscripten.sh" }}{{ checksum "scripts/travis-emscripten/build_emscripten.sh" }}
|
|
||||||
- run:
|
|
||||||
name: Bootstrap Boost
|
|
||||||
command: |
|
|
||||||
scripts/travis-emscripten/install_deps.sh
|
|
||||||
- run:
|
- run:
|
||||||
name: Build
|
name: Build
|
||||||
command: |
|
command: |
|
||||||
scripts/travis-emscripten/build_emscripten.sh
|
scripts/travis-emscripten/build_emscripten.sh
|
||||||
- save_cache:
|
|
||||||
name: Save Boost build
|
|
||||||
key: *boost-cache-key
|
|
||||||
paths:
|
|
||||||
- boost_1_70_0_install
|
|
||||||
- store_artifacts:
|
- store_artifacts:
|
||||||
path: emscripten_build/libsolc/soljson.js
|
path: emscripten_build/libsolc/soljson.js
|
||||||
destination: soljson.js
|
destination: soljson.js
|
||||||
@ -684,6 +703,14 @@ jobs:
|
|||||||
SOLTEST_FLAGS: --no-smt
|
SOLTEST_FLAGS: --no-smt
|
||||||
ASAN_OPTIONS: check_initialization_order=true:detect_stack_use_after_return=true:strict_init_order=true:strict_string_checks=true:detect_invalid_pointer_pairs=2
|
ASAN_OPTIONS: check_initialization_order=true:detect_stack_use_after_return=true:strict_init_order=true:strict_string_checks=true:detect_invalid_pointer_pairs=2
|
||||||
|
|
||||||
|
t_ubu_asan_constantinople_clang:
|
||||||
|
<<: *test_asan_clang
|
||||||
|
environment:
|
||||||
|
EVM: constantinople
|
||||||
|
OPTIMIZE: 0
|
||||||
|
SOLTEST_FLAGS: --no-smt
|
||||||
|
ASAN_OPTIONS: check_initialization_order=true:detect_stack_use_after_return=true:strict_init_order=true:strict_string_checks=true:detect_invalid_pointer_pairs=2
|
||||||
|
|
||||||
t_ems_solcjs:
|
t_ems_solcjs:
|
||||||
docker:
|
docker:
|
||||||
- image: buildpack-deps:latest
|
- image: buildpack-deps:latest
|
||||||
@ -848,7 +875,9 @@ workflows:
|
|||||||
|
|
||||||
# ASan build and tests
|
# ASan build and tests
|
||||||
- b_ubu_asan: *workflow_trigger_on_tags
|
- b_ubu_asan: *workflow_trigger_on_tags
|
||||||
|
- b_ubu_asan_clang: *workflow_trigger_on_tags
|
||||||
- t_ubu_asan_constantinople: *workflow_ubuntu2004_asan
|
- t_ubu_asan_constantinople: *workflow_ubuntu2004_asan
|
||||||
|
- t_ubu_asan_constantinople_clang: *workflow_ubuntu2004_asan_clang
|
||||||
- t_ubu_asan_cli: *workflow_ubuntu2004_asan
|
- t_ubu_asan_cli: *workflow_ubuntu2004_asan
|
||||||
|
|
||||||
# Emscripten build and selected tests
|
# Emscripten build and selected tests
|
||||||
|
66
.circleci/docker/Dockerfile.emscripten
Normal file
66
.circleci/docker/Dockerfile.emscripten
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
# vim:syntax=dockerfile
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# Dockerfile for building and testing Solidity Compiler on CI
|
||||||
|
# Target: Emscripten
|
||||||
|
# URL: https://hub.docker.com/r/ethereum/solidity-buildpack-deps
|
||||||
|
#
|
||||||
|
# This file is part of solidity.
|
||||||
|
#
|
||||||
|
# solidity is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# solidity is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with solidity. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
#
|
||||||
|
# (c) 2016-2019 solidity contributors.
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# The Emscripten SDK at https://github.com/emscripten-core/emsdk/
|
||||||
|
# contains a Makefile in the docker/ subdirectory that can be used to create the
|
||||||
|
# required base image using:
|
||||||
|
#
|
||||||
|
# make version=1.39.15-fastcomp build
|
||||||
|
#
|
||||||
|
# TODO: switch to the upstream backend by removing "-fastcomp".
|
||||||
|
#
|
||||||
|
FROM emscripten/emsdk:1.39.15-fastcomp AS base
|
||||||
|
|
||||||
|
ADD emscripten.jam /usr/src
|
||||||
|
RUN set -ex; \
|
||||||
|
cd /usr/src; \
|
||||||
|
git clone https://github.com/Z3Prover/z3.git -b z3-4.8.8 --depth 1 ; \
|
||||||
|
cd z3; \
|
||||||
|
mkdir build; \
|
||||||
|
cd build; \
|
||||||
|
emcmake cmake \
|
||||||
|
-DCMAKE_BUILD_TYPE=MinSizeRel \
|
||||||
|
-DCMAKE_INSTALL_PREFIX=/emsdk/emscripten/sdk/system/ \
|
||||||
|
-DZ3_BUILD_LIBZ3_SHARED=OFF \
|
||||||
|
-DZ3_ENABLE_EXAMPLE_TARGETS=OFF \
|
||||||
|
-DZ3_BUILD_TEST_EXECUTABLES=OFF \
|
||||||
|
-DZ3_BUILD_EXECUTABLE=OFF \
|
||||||
|
-DZ3_SINGLE_THREADED=ON \
|
||||||
|
..; \
|
||||||
|
make; make install; \
|
||||||
|
rm -r /usr/src/z3; \
|
||||||
|
cd /usr/src; \
|
||||||
|
wget -q 'https://dl.bintray.com/boostorg/release/1.73.0/source/boost_1_73_0.tar.bz2' -O boost.tar.bz2; \
|
||||||
|
test "$(sha256sum boost.tar.bz2)" = "4eb3b8d442b426dc35346235c8733b5ae35ba431690e38c6a8263dce9fcbb402 boost.tar.bz2"; \
|
||||||
|
tar -xf boost.tar.bz2; \
|
||||||
|
rm boost.tar.bz2; \
|
||||||
|
cd boost_1_73_0; \
|
||||||
|
mv ../emscripten.jam .; \
|
||||||
|
./bootstrap.sh; \
|
||||||
|
echo "using emscripten : : em++ ;" >> project-config.jam ; \
|
||||||
|
./b2 toolset=emscripten link=static variant=release threading=single runtime-link=static \
|
||||||
|
--with-system --with-filesystem --with-test --with-program_options \
|
||||||
|
cxxflags="-Wno-unused-local-typedef -Wno-variadic-macros -Wno-c99-extensions -Wno-all" \
|
||||||
|
--prefix=/emsdk/emscripten/sdk/system install; \
|
||||||
|
rm -r /usr/src/boost_1_73_0
|
@ -79,10 +79,10 @@ rule init ( version ? : command * : options * )
|
|||||||
# @todo this seems to be the right way, but this is a list somehow
|
# @todo this seems to be the right way, but this is a list somehow
|
||||||
toolset.add-requirements <toolset>emscripten:<testing.launcher>node ;
|
toolset.add-requirements <toolset>emscripten:<testing.launcher>node ;
|
||||||
|
|
||||||
toolset.flags emscripten.compile STDHDRS $(condition) : /emsdk_portable/emscripten/sdk/system/include ;
|
toolset.flags emscripten.compile STDHDRS $(condition) : /emsdk/emscripten/sdk/system/include ;
|
||||||
toolset.flags emscripten.link STDLIBPATH $(condition) : /emsdk_portable/emscripten/sdk/system/lib ;
|
toolset.flags emscripten.link STDLIBPATH $(condition) : /emsdk/emscripten/sdk/system/lib ;
|
||||||
toolset.flags emscripten AR $(condition) : /emsdk_portable/emscripten/sdk/emar ;
|
toolset.flags emscripten AR $(condition) : /emsdk/emscripten/sdk/emar ;
|
||||||
toolset.flags emscripten RANLIB $(condition) : /emsdk_portable/emscripten/sdk/emranlib ;
|
toolset.flags emscripten RANLIB $(condition) : /emsdk/emscripten/sdk/emranlib ;
|
||||||
}
|
}
|
||||||
|
|
||||||
type.set-generated-target-suffix EXE : <toolset>emscripten : js ;
|
type.set-generated-target-suffix EXE : <toolset>emscripten : js ;
|
@ -110,7 +110,7 @@ matrix:
|
|||||||
before_install:
|
before_install:
|
||||||
- nvm install 8
|
- nvm install 8
|
||||||
- nvm use 8
|
- nvm use 8
|
||||||
- docker pull trzeci/emscripten:sdk-tag-1.39.3-64bit
|
- docker pull ethereum/solidity-buildpack-deps:emsdk-1.39.15-1
|
||||||
env:
|
env:
|
||||||
- SOLC_EMSCRIPTEN=On
|
- SOLC_EMSCRIPTEN=On
|
||||||
- SOLC_INSTALL_DEPS_TRAVIS=Off
|
- SOLC_INSTALL_DEPS_TRAVIS=Off
|
||||||
|
@ -21,10 +21,14 @@ Language Features:
|
|||||||
|
|
||||||
|
|
||||||
Compiler Features:
|
Compiler Features:
|
||||||
|
* 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.
|
||||||
|
* SMTChecker: Support array ``length``.
|
||||||
|
* SMTChecker: Support array ``push`` and ``pop``.
|
||||||
|
|
||||||
|
|
||||||
Bugfixes:
|
Bugfixes:
|
||||||
|
* Optimizer: Fixed a bug in BlockDeDuplicator.
|
||||||
|
* Type Checker: Disallow assignments to storage variables of type ``mapping``.
|
||||||
|
|
||||||
|
|
||||||
### 0.6.8 (2020-05-14)
|
### 0.6.8 (2020-05-14)
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
# Inherit default options
|
# Inherit default options
|
||||||
include("${CMAKE_CURRENT_LIST_DIR}/default.cmake")
|
include("${CMAKE_CURRENT_LIST_DIR}/default.cmake")
|
||||||
# Disable CVC4.
|
# Disable CVC4 and Z3.
|
||||||
set(USE_CVC4 OFF CACHE BOOL "Disable CVC4" FORCE)
|
set(USE_CVC4 OFF CACHE BOOL "Disable CVC4" FORCE)
|
||||||
|
set(USE_Z3 OFF CACHE BOOL "Disable Z3" FORCE)
|
||||||
# Enable fuzzers
|
# Enable fuzzers
|
||||||
set(OSSFUZZ ON CACHE BOOL "Enable fuzzer build" FORCE)
|
set(OSSFUZZ ON CACHE BOOL "Enable fuzzer build" FORCE)
|
||||||
set(LIB_FUZZING_ENGINE $ENV{LIB_FUZZING_ENGINE} CACHE STRING "Use fuzzer back-end defined by environment variable" FORCE)
|
set(LIB_FUZZING_ENGINE $ENV{LIB_FUZZING_ENGINE} CACHE STRING "Use fuzzer back-end defined by environment variable" FORCE)
|
||||||
|
@ -657,3 +657,14 @@ Notice that we do not clear knowledge about ``array`` and ``d`` because they
|
|||||||
are located in storage, even though they also have type ``uint[]``. However,
|
are located in storage, even though they also have type ``uint[]``. However,
|
||||||
if ``d`` was assigned, we would need to clear knowledge about ``array`` and
|
if ``d`` was assigned, we would need to clear knowledge about ``array`` and
|
||||||
vice-versa.
|
vice-versa.
|
||||||
|
|
||||||
|
Real World Assumptions
|
||||||
|
======================
|
||||||
|
|
||||||
|
Some scenarios can be expressed in Solidity and the EVM, but are expected to
|
||||||
|
never occur in practice.
|
||||||
|
One of such cases is the length of a dynamic storage array overflowing during a
|
||||||
|
push: If the ``push`` operation is applied to an array of length 2^256 - 1, its
|
||||||
|
length silently overflows.
|
||||||
|
However, this is unlikely to happen in practice, since the operations required
|
||||||
|
to grow the array to that point would take billions of years to execute.
|
||||||
|
@ -52,6 +52,7 @@ public:
|
|||||||
AssemblyItem newSub(AssemblyPointer const& _sub) { m_subs.push_back(_sub); return AssemblyItem(PushSub, m_subs.size() - 1); }
|
AssemblyItem newSub(AssemblyPointer const& _sub) { m_subs.push_back(_sub); return AssemblyItem(PushSub, m_subs.size() - 1); }
|
||||||
Assembly const& sub(size_t _sub) const { return *m_subs.at(_sub); }
|
Assembly const& sub(size_t _sub) const { return *m_subs.at(_sub); }
|
||||||
Assembly& sub(size_t _sub) { return *m_subs.at(_sub); }
|
Assembly& sub(size_t _sub) { return *m_subs.at(_sub); }
|
||||||
|
size_t numSubs() const { return m_subs.size(); }
|
||||||
AssemblyItem newPushSubSize(u256 const& _subId) { return AssemblyItem(PushSubSize, _subId); }
|
AssemblyItem newPushSubSize(u256 const& _subId) { return AssemblyItem(PushSubSize, _subId); }
|
||||||
AssemblyItem newPushLibraryAddress(std::string const& _identifier);
|
AssemblyItem newPushLibraryAddress(std::string const& _identifier);
|
||||||
AssemblyItem newPushImmutable(std::string const& _identifier);
|
AssemblyItem newPushImmutable(std::string const& _identifier);
|
||||||
|
@ -113,6 +113,10 @@ bool BlockDeduplicator::applyTagReplacement(
|
|||||||
if (subId != _subId)
|
if (subId != _subId)
|
||||||
continue;
|
continue;
|
||||||
auto it = _replacements.find(tagId);
|
auto it = _replacements.find(tagId);
|
||||||
|
// Recursively look for the element replaced by tagId
|
||||||
|
for (auto _it = it; _it != _replacements.end(); _it = _replacements.find(_it->second))
|
||||||
|
it = _it;
|
||||||
|
|
||||||
if (it != _replacements.end())
|
if (it != _replacements.end())
|
||||||
{
|
{
|
||||||
changed = true;
|
changed = true;
|
||||||
|
@ -143,6 +143,28 @@ public:
|
|||||||
// @returns true if the maximum error count has been reached.
|
// @returns true if the maximum error count has been reached.
|
||||||
bool hasExcessiveErrors() const;
|
bool hasExcessiveErrors() const;
|
||||||
|
|
||||||
|
class ErrorWatcher
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ErrorWatcher(ErrorReporter const& _errorReporter):
|
||||||
|
m_errorReporter(_errorReporter),
|
||||||
|
m_initialErrorCount(_errorReporter.errorCount())
|
||||||
|
{}
|
||||||
|
bool ok() const
|
||||||
|
{
|
||||||
|
solAssert(m_initialErrorCount <= m_errorReporter.errorCount(), "Unexpected error count.");
|
||||||
|
return m_initialErrorCount == m_errorReporter.errorCount();
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
ErrorReporter const& m_errorReporter;
|
||||||
|
unsigned const m_initialErrorCount;
|
||||||
|
};
|
||||||
|
|
||||||
|
ErrorWatcher errorWatcher() const
|
||||||
|
{
|
||||||
|
return ErrorWatcher(*this);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void error(
|
void error(
|
||||||
ErrorId _error,
|
ErrorId _error,
|
||||||
|
@ -79,6 +79,8 @@ set(sources
|
|||||||
codegen/ReturnInfo.cpp
|
codegen/ReturnInfo.cpp
|
||||||
codegen/YulUtilFunctions.h
|
codegen/YulUtilFunctions.h
|
||||||
codegen/YulUtilFunctions.cpp
|
codegen/YulUtilFunctions.cpp
|
||||||
|
codegen/ir/Common.cpp
|
||||||
|
codegen/ir/Common.h
|
||||||
codegen/ir/IRGenerator.cpp
|
codegen/ir/IRGenerator.cpp
|
||||||
codegen/ir/IRGenerator.h
|
codegen/ir/IRGenerator.h
|
||||||
codegen/ir/IRGeneratorForStatements.cpp
|
codegen/ir/IRGeneratorForStatements.cpp
|
||||||
|
@ -455,7 +455,15 @@ bool TypeChecker::visit(VariableDeclaration const& _variable)
|
|||||||
if (contractType->contractDefinition().isLibrary())
|
if (contractType->contractDefinition().isLibrary())
|
||||||
m_errorReporter.typeError(1273_error, _variable.location(), "The type of a variable cannot be a library.");
|
m_errorReporter.typeError(1273_error, _variable.location(), "The type of a variable cannot be a library.");
|
||||||
if (_variable.value())
|
if (_variable.value())
|
||||||
expectType(*_variable.value(), *varType);
|
{
|
||||||
|
if (_variable.isStateVariable() && dynamic_cast<MappingType const*>(varType))
|
||||||
|
{
|
||||||
|
m_errorReporter.typeError(6280_error, _variable.location(), "Mappings cannot be assigned to.");
|
||||||
|
_variable.value()->accept(*this);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
expectType(*_variable.value(), *varType);
|
||||||
|
}
|
||||||
if (_variable.isConstant())
|
if (_variable.isConstant())
|
||||||
{
|
{
|
||||||
if (!_variable.type()->isValueType())
|
if (!_variable.type()->isValueType())
|
||||||
|
@ -153,10 +153,10 @@ FunctionDefinition const* ContractDefinition::receiveFunction() const
|
|||||||
|
|
||||||
vector<EventDefinition const*> const& ContractDefinition::interfaceEvents() const
|
vector<EventDefinition const*> const& ContractDefinition::interfaceEvents() const
|
||||||
{
|
{
|
||||||
if (!m_interfaceEvents)
|
return m_interfaceEvents.init([&]{
|
||||||
{
|
|
||||||
set<string> eventsSeen;
|
set<string> eventsSeen;
|
||||||
m_interfaceEvents = make_unique<vector<EventDefinition const*>>();
|
vector<EventDefinition const*> interfaceEvents;
|
||||||
|
|
||||||
for (ContractDefinition const* contract: annotation().linearizedBaseContracts)
|
for (ContractDefinition const* contract: annotation().linearizedBaseContracts)
|
||||||
for (EventDefinition const* e: contract->events())
|
for (EventDefinition const* e: contract->events())
|
||||||
{
|
{
|
||||||
@ -169,19 +169,20 @@ vector<EventDefinition const*> const& ContractDefinition::interfaceEvents() cons
|
|||||||
if (eventsSeen.count(eventSignature) == 0)
|
if (eventsSeen.count(eventSignature) == 0)
|
||||||
{
|
{
|
||||||
eventsSeen.insert(eventSignature);
|
eventsSeen.insert(eventSignature);
|
||||||
m_interfaceEvents->push_back(e);
|
interfaceEvents.push_back(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return *m_interfaceEvents;
|
return interfaceEvents;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<pair<util::FixedHash<4>, FunctionTypePointer>> const& ContractDefinition::interfaceFunctionList(bool _includeInheritedFunctions) const
|
vector<pair<util::FixedHash<4>, FunctionTypePointer>> const& ContractDefinition::interfaceFunctionList(bool _includeInheritedFunctions) const
|
||||||
{
|
{
|
||||||
if (!m_interfaceFunctionList[_includeInheritedFunctions])
|
return m_interfaceFunctionList[_includeInheritedFunctions].init([&]{
|
||||||
{
|
|
||||||
set<string> signaturesSeen;
|
set<string> signaturesSeen;
|
||||||
m_interfaceFunctionList[_includeInheritedFunctions] = make_unique<vector<pair<util::FixedHash<4>, FunctionTypePointer>>>();
|
vector<pair<util::FixedHash<4>, FunctionTypePointer>> interfaceFunctionList;
|
||||||
|
|
||||||
for (ContractDefinition const* contract: annotation().linearizedBaseContracts)
|
for (ContractDefinition const* contract: annotation().linearizedBaseContracts)
|
||||||
{
|
{
|
||||||
if (_includeInheritedFunctions == false && contract != this)
|
if (_includeInheritedFunctions == false && contract != this)
|
||||||
@ -203,12 +204,13 @@ vector<pair<util::FixedHash<4>, FunctionTypePointer>> const& ContractDefinition:
|
|||||||
{
|
{
|
||||||
signaturesSeen.insert(functionSignature);
|
signaturesSeen.insert(functionSignature);
|
||||||
util::FixedHash<4> hash(util::keccak256(functionSignature));
|
util::FixedHash<4> hash(util::keccak256(functionSignature));
|
||||||
m_interfaceFunctionList[_includeInheritedFunctions]->emplace_back(hash, fun);
|
interfaceFunctionList.emplace_back(hash, fun);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return *m_interfaceFunctionList[_includeInheritedFunctions];
|
return interfaceFunctionList;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
TypePointer ContractDefinition::type() const
|
TypePointer ContractDefinition::type() const
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
#include <liblangutil/SourceLocation.h>
|
#include <liblangutil/SourceLocation.h>
|
||||||
#include <libevmasm/Instruction.h>
|
#include <libevmasm/Instruction.h>
|
||||||
#include <libsolutil/FixedHash.h>
|
#include <libsolutil/FixedHash.h>
|
||||||
|
#include <libsolutil/LazyInit.h>
|
||||||
|
|
||||||
#include <boost/noncopyable.hpp>
|
#include <boost/noncopyable.hpp>
|
||||||
#include <json/json.h>
|
#include <json/json.h>
|
||||||
@ -536,8 +537,8 @@ private:
|
|||||||
ContractKind m_contractKind;
|
ContractKind m_contractKind;
|
||||||
bool m_abstract{false};
|
bool m_abstract{false};
|
||||||
|
|
||||||
mutable std::unique_ptr<std::vector<std::pair<util::FixedHash<4>, FunctionTypePointer>>> m_interfaceFunctionList[2];
|
util::LazyInit<std::vector<std::pair<util::FixedHash<4>, FunctionTypePointer>>> m_interfaceFunctionList[2];
|
||||||
mutable std::unique_ptr<std::vector<EventDefinition const*>> m_interfaceEvents;
|
util::LazyInit<std::vector<EventDefinition const*>> m_interfaceEvents;
|
||||||
};
|
};
|
||||||
|
|
||||||
class InheritanceSpecifier: public ASTNode
|
class InheritanceSpecifier: public ASTNode
|
||||||
|
@ -207,26 +207,31 @@ void MemberList::combine(MemberList const & _other)
|
|||||||
|
|
||||||
pair<u256, unsigned> const* MemberList::memberStorageOffset(string const& _name) const
|
pair<u256, unsigned> const* MemberList::memberStorageOffset(string const& _name) const
|
||||||
{
|
{
|
||||||
if (!m_storageOffsets)
|
StorageOffsets const& offsets = storageOffsets();
|
||||||
{
|
|
||||||
TypePointers memberTypes;
|
|
||||||
memberTypes.reserve(m_memberTypes.size());
|
|
||||||
for (auto const& member: m_memberTypes)
|
|
||||||
memberTypes.push_back(member.type);
|
|
||||||
m_storageOffsets = std::make_unique<StorageOffsets>();
|
|
||||||
m_storageOffsets->computeOffsets(memberTypes);
|
|
||||||
}
|
|
||||||
for (size_t index = 0; index < m_memberTypes.size(); ++index)
|
for (size_t index = 0; index < m_memberTypes.size(); ++index)
|
||||||
if (m_memberTypes[index].name == _name)
|
if (m_memberTypes[index].name == _name)
|
||||||
return m_storageOffsets->offset(index);
|
return offsets.offset(index);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
u256 const& MemberList::storageSize() const
|
u256 const& MemberList::storageSize() const
|
||||||
{
|
{
|
||||||
// trigger lazy computation
|
return storageOffsets().storageSize();
|
||||||
memberStorageOffset("");
|
}
|
||||||
return m_storageOffsets->storageSize();
|
|
||||||
|
StorageOffsets const& MemberList::storageOffsets() const {
|
||||||
|
return m_storageOffsets.init([&]{
|
||||||
|
TypePointers memberTypes;
|
||||||
|
memberTypes.reserve(m_memberTypes.size());
|
||||||
|
for (auto const& member: m_memberTypes)
|
||||||
|
memberTypes.push_back(member.type);
|
||||||
|
|
||||||
|
StorageOffsets storageOffsets;
|
||||||
|
storageOffsets.computeOffsets(memberTypes);
|
||||||
|
|
||||||
|
return storageOffsets;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Helper functions for type identifier
|
/// Helper functions for type identifier
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
|
|
||||||
#include <libsolutil/Common.h>
|
#include <libsolutil/Common.h>
|
||||||
#include <libsolutil/CommonIO.h>
|
#include <libsolutil/CommonIO.h>
|
||||||
|
#include <libsolutil/LazyInit.h>
|
||||||
#include <libsolutil/Result.h>
|
#include <libsolutil/Result.h>
|
||||||
|
|
||||||
#include <boost/rational.hpp>
|
#include <boost/rational.hpp>
|
||||||
@ -139,8 +140,10 @@ public:
|
|||||||
MemberMap::const_iterator end() const { return m_memberTypes.end(); }
|
MemberMap::const_iterator end() const { return m_memberTypes.end(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
StorageOffsets const& storageOffsets() const;
|
||||||
|
|
||||||
MemberMap m_memberTypes;
|
MemberMap m_memberTypes;
|
||||||
mutable std::unique_ptr<StorageOffsets> m_storageOffsets;
|
util::LazyInit<StorageOffsets> m_storageOffsets;
|
||||||
};
|
};
|
||||||
|
|
||||||
static_assert(std::is_nothrow_move_constructible<MemberList>::value, "MemberList should be noexcept move constructible");
|
static_assert(std::is_nothrow_move_constructible<MemberList>::value, "MemberList should be noexcept move constructible");
|
||||||
|
@ -602,6 +602,25 @@ string YulUtilFunctions::overflowCheckedIntSubFunction(IntegerType const& _type)
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string YulUtilFunctions::extractByteArrayLengthFunction()
|
||||||
|
{
|
||||||
|
string functionName = "extract_byte_array_length";
|
||||||
|
return m_functionCollector.createFunction(functionName, [&]() {
|
||||||
|
Whiskers w(R"(
|
||||||
|
function <functionName>(data) -> length {
|
||||||
|
// Retrieve length both for in-place strings and off-place strings:
|
||||||
|
// Computes (x & (0x100 * (ISZERO (x & 1)) - 1)) / 2
|
||||||
|
// i.e. for short strings (x & 1 == 0) it does (x & 0xff) / 2 and for long strings it
|
||||||
|
// computes (x & (-1)) / 2, which is equivalent to just x / 2.
|
||||||
|
let mask := sub(mul(0x100, iszero(and(data, 1))), 1)
|
||||||
|
length := div(and(data, mask), 2)
|
||||||
|
}
|
||||||
|
)");
|
||||||
|
w("functionName", functionName);
|
||||||
|
return w.render();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
string YulUtilFunctions::arrayLengthFunction(ArrayType const& _type)
|
string YulUtilFunctions::arrayLengthFunction(ArrayType const& _type)
|
||||||
{
|
{
|
||||||
string functionName = "array_length_" + _type.identifier();
|
string functionName = "array_length_" + _type.identifier();
|
||||||
@ -615,12 +634,7 @@ string YulUtilFunctions::arrayLengthFunction(ArrayType const& _type)
|
|||||||
<?storage>
|
<?storage>
|
||||||
length := sload(value)
|
length := sload(value)
|
||||||
<?byteArray>
|
<?byteArray>
|
||||||
// Retrieve length both for in-place strings and off-place strings:
|
length := <extractByteArrayLength>(length)
|
||||||
// Computes (x & (0x100 * (ISZERO (x & 1)) - 1)) / 2
|
|
||||||
// i.e. for short strings (x & 1 == 0) it does (x & 0xff) / 2 and for long strings it
|
|
||||||
// computes (x & (-1)) / 2, which is equivalent to just x / 2.
|
|
||||||
let mask := sub(mul(0x100, iszero(and(length, 1))), 1)
|
|
||||||
length := div(and(length, mask), 2)
|
|
||||||
</byteArray>
|
</byteArray>
|
||||||
</storage>
|
</storage>
|
||||||
<!dynamic>
|
<!dynamic>
|
||||||
@ -634,7 +648,12 @@ string YulUtilFunctions::arrayLengthFunction(ArrayType const& _type)
|
|||||||
w("length", toCompactHexWithPrefix(_type.length()));
|
w("length", toCompactHexWithPrefix(_type.length()));
|
||||||
w("memory", _type.location() == DataLocation::Memory);
|
w("memory", _type.location() == DataLocation::Memory);
|
||||||
w("storage", _type.location() == DataLocation::Storage);
|
w("storage", _type.location() == DataLocation::Storage);
|
||||||
w("byteArray", _type.isByteArray());
|
if (_type.location() == DataLocation::Storage)
|
||||||
|
{
|
||||||
|
w("byteArray", _type.isByteArray());
|
||||||
|
if (_type.isByteArray())
|
||||||
|
w("extractByteArrayLength", extractByteArrayLengthFunction());
|
||||||
|
}
|
||||||
if (_type.isDynamicallySized())
|
if (_type.isDynamicallySized())
|
||||||
solAssert(
|
solAssert(
|
||||||
_type.location() != DataLocation::CallData,
|
_type.location() != DataLocation::CallData,
|
||||||
@ -689,8 +708,9 @@ string YulUtilFunctions::storageArrayPopFunction(ArrayType const& _type)
|
|||||||
{
|
{
|
||||||
solAssert(_type.location() == DataLocation::Storage, "");
|
solAssert(_type.location() == DataLocation::Storage, "");
|
||||||
solAssert(_type.isDynamicallySized(), "");
|
solAssert(_type.isDynamicallySized(), "");
|
||||||
solUnimplementedAssert(!_type.isByteArray(), "Byte Arrays not yet implemented!");
|
|
||||||
solUnimplementedAssert(_type.baseType()->storageBytes() <= 32, "Base type is not yet implemented.");
|
solUnimplementedAssert(_type.baseType()->storageBytes() <= 32, "Base type is not yet implemented.");
|
||||||
|
if (_type.isByteArray())
|
||||||
|
return storageByteArrayPopFunction(_type);
|
||||||
|
|
||||||
string functionName = "array_pop_" + _type.identifier();
|
string functionName = "array_pop_" + _type.identifier();
|
||||||
return m_functionCollector.createFunction(functionName, [&]() {
|
return m_functionCollector.createFunction(functionName, [&]() {
|
||||||
@ -699,10 +719,8 @@ string YulUtilFunctions::storageArrayPopFunction(ArrayType const& _type)
|
|||||||
let oldLen := <fetchLength>(array)
|
let oldLen := <fetchLength>(array)
|
||||||
if iszero(oldLen) { invalid() }
|
if iszero(oldLen) { invalid() }
|
||||||
let newLen := sub(oldLen, 1)
|
let newLen := sub(oldLen, 1)
|
||||||
|
|
||||||
let slot, offset := <indexAccess>(array, newLen)
|
let slot, offset := <indexAccess>(array, newLen)
|
||||||
<setToZero>(slot, offset)
|
<setToZero>(slot, offset)
|
||||||
|
|
||||||
sstore(array, newLen)
|
sstore(array, newLen)
|
||||||
})")
|
})")
|
||||||
("functionName", functionName)
|
("functionName", functionName)
|
||||||
@ -713,29 +731,115 @@ string YulUtilFunctions::storageArrayPopFunction(ArrayType const& _type)
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string YulUtilFunctions::storageByteArrayPopFunction(ArrayType const& _type)
|
||||||
|
{
|
||||||
|
solAssert(_type.location() == DataLocation::Storage, "");
|
||||||
|
solAssert(_type.isDynamicallySized(), "");
|
||||||
|
solAssert(_type.isByteArray(), "");
|
||||||
|
|
||||||
|
string functionName = "byte_array_pop_" + _type.identifier();
|
||||||
|
return m_functionCollector.createFunction(functionName, [&]() {
|
||||||
|
return Whiskers(R"(
|
||||||
|
function <functionName>(array) {
|
||||||
|
let data := sload(array)
|
||||||
|
let oldLen := <extractByteArrayLength>(data)
|
||||||
|
if iszero(oldLen) { invalid() }
|
||||||
|
|
||||||
|
switch eq(oldLen, 32)
|
||||||
|
case 1 {
|
||||||
|
// Here we have a special case where array transitions to shorter than 32
|
||||||
|
// So we need to copy data
|
||||||
|
let copyFromSlot := <dataAreaFunction>(array)
|
||||||
|
data := sload(copyFromSlot)
|
||||||
|
sstore(copyFromSlot, 0)
|
||||||
|
// New length is 31, encoded to 31 * 2 = 62
|
||||||
|
data := or(and(data, not(0xff)), 62)
|
||||||
|
}
|
||||||
|
default {
|
||||||
|
data := sub(data, 2)
|
||||||
|
let newLen := sub(oldLen, 1)
|
||||||
|
switch lt(oldLen, 32)
|
||||||
|
case 1 {
|
||||||
|
// set last element to zero
|
||||||
|
let mask := not(<shl>(mul(8, sub(31, newLen)), 0xff))
|
||||||
|
data := and(data, mask)
|
||||||
|
}
|
||||||
|
default {
|
||||||
|
let slot, offset := <indexAccess>(array, newLen)
|
||||||
|
<setToZero>(slot, offset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sstore(array, data)
|
||||||
|
})")
|
||||||
|
("functionName", functionName)
|
||||||
|
("extractByteArrayLength", extractByteArrayLengthFunction())
|
||||||
|
("dataAreaFunction", arrayDataAreaFunction(_type))
|
||||||
|
("indexAccess", storageArrayIndexAccessFunction(_type))
|
||||||
|
("setToZero", storageSetToZeroFunction(*_type.baseType()))
|
||||||
|
("shl", shiftLeftFunctionDynamic())
|
||||||
|
.render();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
string YulUtilFunctions::storageArrayPushFunction(ArrayType const& _type)
|
string YulUtilFunctions::storageArrayPushFunction(ArrayType const& _type)
|
||||||
{
|
{
|
||||||
solAssert(_type.location() == DataLocation::Storage, "");
|
solAssert(_type.location() == DataLocation::Storage, "");
|
||||||
solAssert(_type.isDynamicallySized(), "");
|
solAssert(_type.isDynamicallySized(), "");
|
||||||
solUnimplementedAssert(!_type.isByteArray(), "Byte Arrays not yet implemented!");
|
|
||||||
solUnimplementedAssert(_type.baseType()->storageBytes() <= 32, "Base type is not yet implemented.");
|
solUnimplementedAssert(_type.baseType()->storageBytes() <= 32, "Base type is not yet implemented.");
|
||||||
|
|
||||||
string functionName = "array_push_" + _type.identifier();
|
string functionName = "array_push_" + _type.identifier();
|
||||||
return m_functionCollector.createFunction(functionName, [&]() {
|
return m_functionCollector.createFunction(functionName, [&]() {
|
||||||
return Whiskers(R"(
|
return Whiskers(R"(
|
||||||
function <functionName>(array, value) {
|
function <functionName>(array, value) {
|
||||||
let oldLen := <fetchLength>(array)
|
<?isByteArray>
|
||||||
if iszero(lt(oldLen, <maxArrayLength>)) { invalid() }
|
let data := sload(array)
|
||||||
sstore(array, add(oldLen, 1))
|
let oldLen := <extractByteArrayLength>(data)
|
||||||
|
if iszero(lt(oldLen, <maxArrayLength>)) { invalid() }
|
||||||
|
|
||||||
let slot, offset := <indexAccess>(array, oldLen)
|
switch gt(oldLen, 31)
|
||||||
<storeValue>(slot, offset, value)
|
case 0 {
|
||||||
|
value := byte(0, value)
|
||||||
|
switch oldLen
|
||||||
|
case 31 {
|
||||||
|
// Here we have special case when array switches from short array to long array
|
||||||
|
// We need to copy data
|
||||||
|
let dataArea := <dataAreaFunction>(array)
|
||||||
|
data := and(data, not(0xff))
|
||||||
|
sstore(dataArea, or(and(0xff, value), data))
|
||||||
|
// New length is 32, encoded as (32 * 2 + 1)
|
||||||
|
sstore(array, 65)
|
||||||
|
}
|
||||||
|
default {
|
||||||
|
data := add(data, 2)
|
||||||
|
let shiftBits := mul(8, sub(31, oldLen))
|
||||||
|
let valueShifted := <shl>(shiftBits, and(0xff, value))
|
||||||
|
let mask := <shl>(shiftBits, 0xff)
|
||||||
|
data := or(and(data, not(mask)), valueShifted)
|
||||||
|
sstore(array, data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default {
|
||||||
|
sstore(array, add(data, 2))
|
||||||
|
let slot, offset := <indexAccess>(array, oldLen)
|
||||||
|
<storeValue>(slot, offset, value)
|
||||||
|
}
|
||||||
|
<!isByteArray>
|
||||||
|
let oldLen := sload(array)
|
||||||
|
if iszero(lt(oldLen, <maxArrayLength>)) { invalid() }
|
||||||
|
sstore(array, add(oldLen, 1))
|
||||||
|
let slot, offset := <indexAccess>(array, oldLen)
|
||||||
|
<storeValue>(slot, offset, value)
|
||||||
|
</isByteArray>
|
||||||
})")
|
})")
|
||||||
("functionName", functionName)
|
("functionName", functionName)
|
||||||
("fetchLength", arrayLengthFunction(_type))
|
("extractByteArrayLength", _type.isByteArray() ? extractByteArrayLengthFunction() : "")
|
||||||
|
("dataAreaFunction", arrayDataAreaFunction(_type))
|
||||||
|
("isByteArray", _type.isByteArray())
|
||||||
("indexAccess", storageArrayIndexAccessFunction(_type))
|
("indexAccess", storageArrayIndexAccessFunction(_type))
|
||||||
("storeValue", updateStorageValueFunction(*_type.baseType()))
|
("storeValue", updateStorageValueFunction(*_type.baseType()))
|
||||||
("maxArrayLength", (u256(1) << 64).str())
|
("maxArrayLength", (u256(1) << 64).str())
|
||||||
|
("shl", shiftLeftFunctionDynamic())
|
||||||
|
("shr", shiftRightFunction(248))
|
||||||
.render();
|
.render();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -947,21 +1051,33 @@ string YulUtilFunctions::arrayDataAreaFunction(ArrayType const& _type)
|
|||||||
|
|
||||||
string YulUtilFunctions::storageArrayIndexAccessFunction(ArrayType const& _type)
|
string YulUtilFunctions::storageArrayIndexAccessFunction(ArrayType const& _type)
|
||||||
{
|
{
|
||||||
solUnimplementedAssert(_type.baseType()->storageBytes() > 16, "");
|
|
||||||
|
|
||||||
string functionName = "storage_array_index_access_" + _type.identifier();
|
string functionName = "storage_array_index_access_" + _type.identifier();
|
||||||
return m_functionCollector.createFunction(functionName, [&]() {
|
return m_functionCollector.createFunction(functionName, [&]() {
|
||||||
return Whiskers(R"(
|
return Whiskers(R"(
|
||||||
function <functionName>(array, index) -> slot, offset {
|
function <functionName>(array, index) -> slot, offset {
|
||||||
if iszero(lt(index, <arrayLen>(array))) {
|
let arrayLength := <arrayLen>(array)
|
||||||
invalid()
|
if iszero(lt(index, arrayLength)) { invalid() }
|
||||||
}
|
|
||||||
|
|
||||||
let data := <dataAreaFunc>(array)
|
|
||||||
<?multipleItemsPerSlot>
|
<?multipleItemsPerSlot>
|
||||||
|
<?isBytesArray>
|
||||||
|
offset := sub(31, mod(index, 0x20))
|
||||||
|
switch lt(arrayLength, 0x20)
|
||||||
|
case 0 {
|
||||||
|
let dataArea := <dataAreaFunc>(array)
|
||||||
|
slot := add(dataArea, div(index, 0x20))
|
||||||
|
}
|
||||||
|
default {
|
||||||
|
slot := array
|
||||||
|
}
|
||||||
|
<!isBytesArray>
|
||||||
|
let itemsPerSlot := div(0x20, <storageBytes>)
|
||||||
|
let dataArea := <dataAreaFunc>(array)
|
||||||
|
slot := add(dataArea, div(index, itemsPerSlot))
|
||||||
|
offset := mod(index, itemsPerSlot)
|
||||||
|
</isBytesArray>
|
||||||
<!multipleItemsPerSlot>
|
<!multipleItemsPerSlot>
|
||||||
slot := add(data, mul(index, <storageSize>))
|
let dataArea := <dataAreaFunc>(array)
|
||||||
|
slot := add(dataArea, mul(index, <storageSize>))
|
||||||
offset := 0
|
offset := 0
|
||||||
</multipleItemsPerSlot>
|
</multipleItemsPerSlot>
|
||||||
}
|
}
|
||||||
@ -970,7 +1086,9 @@ string YulUtilFunctions::storageArrayIndexAccessFunction(ArrayType const& _type)
|
|||||||
("arrayLen", arrayLengthFunction(_type))
|
("arrayLen", arrayLengthFunction(_type))
|
||||||
("dataAreaFunc", arrayDataAreaFunction(_type))
|
("dataAreaFunc", arrayDataAreaFunction(_type))
|
||||||
("multipleItemsPerSlot", _type.baseType()->storageBytes() <= 16)
|
("multipleItemsPerSlot", _type.baseType()->storageBytes() <= 16)
|
||||||
|
("isBytesArray", _type.isByteArray())
|
||||||
("storageSize", _type.baseType()->storageSize().str())
|
("storageSize", _type.baseType()->storageSize().str())
|
||||||
|
("storageBytes", toString(_type.baseType()->storageBytes()))
|
||||||
.render();
|
.render();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -364,6 +364,14 @@ private:
|
|||||||
/// use exactly one variable to hold the value.
|
/// use exactly one variable to hold the value.
|
||||||
std::string conversionFunctionSpecial(Type const& _from, Type const& _to);
|
std::string conversionFunctionSpecial(Type const& _from, Type const& _to);
|
||||||
|
|
||||||
|
/// @returns function name that extracts and returns byte array length
|
||||||
|
/// signature: (data) -> length
|
||||||
|
std::string extractByteArrayLengthFunction();
|
||||||
|
|
||||||
|
/// @returns the name of a function that reduces the size of a storage byte array by one element
|
||||||
|
/// signature: (byteArray)
|
||||||
|
std::string storageByteArrayPopFunction(ArrayType const& _type);
|
||||||
|
|
||||||
std::string readFromMemoryOrCalldata(Type const& _type, bool _fromCalldata);
|
std::string readFromMemoryOrCalldata(Type const& _type, bool _fromCalldata);
|
||||||
|
|
||||||
langutil::EVMVersion m_evmVersion;
|
langutil::EVMVersion m_evmVersion;
|
||||||
|
86
libsolidity/codegen/ir/Common.cpp
Normal file
86
libsolidity/codegen/ir/Common.cpp
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
This file is part of solidity.
|
||||||
|
|
||||||
|
solidity is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
solidity is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <libsolidity/codegen/ir/Common.h>
|
||||||
|
|
||||||
|
#include <libsolutil/CommonIO.h>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace solidity::util;
|
||||||
|
using namespace solidity::frontend;
|
||||||
|
|
||||||
|
string IRNames::function(FunctionDefinition const& _function)
|
||||||
|
{
|
||||||
|
// @TODO previously, we had to distinguish creation context and runtime context,
|
||||||
|
// but since we do not work with jump positions anymore, this should not be a problem, right?
|
||||||
|
return "fun_" + _function.name() + "_" + to_string(_function.id());
|
||||||
|
}
|
||||||
|
|
||||||
|
string IRNames::function(VariableDeclaration const& _varDecl)
|
||||||
|
{
|
||||||
|
return "getter_fun_" + _varDecl.name() + "_" + to_string(_varDecl.id());
|
||||||
|
}
|
||||||
|
|
||||||
|
string IRNames::creationObject(ContractDefinition const& _contract)
|
||||||
|
{
|
||||||
|
return _contract.name() + "_" + toString(_contract.id());
|
||||||
|
}
|
||||||
|
|
||||||
|
string IRNames::runtimeObject(ContractDefinition const& _contract)
|
||||||
|
{
|
||||||
|
return _contract.name() + "_" + toString(_contract.id()) + "_deployed";
|
||||||
|
}
|
||||||
|
|
||||||
|
string IRNames::implicitConstructor(ContractDefinition const& _contract)
|
||||||
|
{
|
||||||
|
return "constructor_" + _contract.name() + "_" + to_string(_contract.id());
|
||||||
|
}
|
||||||
|
|
||||||
|
string IRNames::constantValueFunction(VariableDeclaration const& _constant)
|
||||||
|
{
|
||||||
|
solAssert(_constant.isConstant(), "");
|
||||||
|
return "constant_" + _constant.name() + "_" + to_string(_constant.id());
|
||||||
|
}
|
||||||
|
|
||||||
|
string IRNames::localVariable(VariableDeclaration const& _declaration)
|
||||||
|
{
|
||||||
|
return "vloc_" + _declaration.name() + '_' + std::to_string(_declaration.id());
|
||||||
|
}
|
||||||
|
|
||||||
|
string IRNames::localVariable(Expression const& _expression)
|
||||||
|
{
|
||||||
|
return "expr_" + to_string(_expression.id());
|
||||||
|
}
|
||||||
|
|
||||||
|
string IRNames::trySuccessConditionVariable(Expression const& _expression)
|
||||||
|
{
|
||||||
|
auto annotation = dynamic_cast<FunctionCallAnnotation const*>(&_expression.annotation());
|
||||||
|
solAssert(annotation, "");
|
||||||
|
solAssert(annotation->tryCall, "Parameter must be a FunctionCall with tryCall-annotation set.");
|
||||||
|
|
||||||
|
return "trySuccessCondition_" + to_string(_expression.id());
|
||||||
|
}
|
||||||
|
|
||||||
|
string IRNames::tupleComponent(size_t _i)
|
||||||
|
{
|
||||||
|
return "component_" + to_string(_i + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
string IRNames::zeroValue(Type const& _type, string const& _variableName)
|
||||||
|
{
|
||||||
|
return "zero_value_for_type_" + _type.identifier() + _variableName;
|
||||||
|
}
|
47
libsolidity/codegen/ir/Common.h
Normal file
47
libsolidity/codegen/ir/Common.h
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
This file is part of solidity.
|
||||||
|
|
||||||
|
solidity is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
solidity is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Miscellaneous utilities for use in IR generator.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <libsolidity/ast/AST.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace solidity::frontend
|
||||||
|
{
|
||||||
|
|
||||||
|
struct IRNames
|
||||||
|
{
|
||||||
|
static std::string function(FunctionDefinition const& _function);
|
||||||
|
static std::string function(VariableDeclaration const& _varDecl);
|
||||||
|
static std::string creationObject(ContractDefinition const& _contract);
|
||||||
|
static std::string runtimeObject(ContractDefinition const& _contract);
|
||||||
|
static std::string implicitConstructor(ContractDefinition const& _contract);
|
||||||
|
static std::string constantValueFunction(VariableDeclaration const& _constant);
|
||||||
|
static std::string localVariable(VariableDeclaration const& _declaration);
|
||||||
|
static std::string localVariable(Expression const& _expression);
|
||||||
|
/// @returns the variable name that can be used to inspect the success or failure of an external
|
||||||
|
/// function call that was invoked as part of the try statement.
|
||||||
|
static std::string trySuccessConditionVariable(Expression const& _expression);
|
||||||
|
static std::string tupleComponent(size_t _i);
|
||||||
|
static std::string zeroValue(Type const& _type, std::string const& _variableName);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -36,7 +36,7 @@ using namespace solidity::frontend;
|
|||||||
|
|
||||||
string IRGenerationContext::enqueueFunctionForCodeGeneration(FunctionDefinition const& _function)
|
string IRGenerationContext::enqueueFunctionForCodeGeneration(FunctionDefinition const& _function)
|
||||||
{
|
{
|
||||||
string name = functionName(_function);
|
string name = IRNames::function(_function);
|
||||||
|
|
||||||
if (!m_functions.contains(name))
|
if (!m_functions.contains(name))
|
||||||
m_functionGenerationQueue.insert(&_function);
|
m_functionGenerationQueue.insert(&_function);
|
||||||
@ -116,43 +116,11 @@ void IRGenerationContext::addStateVariable(
|
|||||||
m_stateVariables[&_declaration] = make_pair(move(_storageOffset), _byteOffset);
|
m_stateVariables[&_declaration] = make_pair(move(_storageOffset), _byteOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
string IRGenerationContext::functionName(FunctionDefinition const& _function)
|
|
||||||
{
|
|
||||||
// @TODO previously, we had to distinguish creation context and runtime context,
|
|
||||||
// but since we do not work with jump positions anymore, this should not be a problem, right?
|
|
||||||
return "fun_" + _function.name() + "_" + to_string(_function.id());
|
|
||||||
}
|
|
||||||
|
|
||||||
string IRGenerationContext::functionName(VariableDeclaration const& _varDecl)
|
|
||||||
{
|
|
||||||
return "getter_fun_" + _varDecl.name() + "_" + to_string(_varDecl.id());
|
|
||||||
}
|
|
||||||
|
|
||||||
string IRGenerationContext::creationObjectName(ContractDefinition const& _contract) const
|
|
||||||
{
|
|
||||||
return _contract.name() + "_" + toString(_contract.id());
|
|
||||||
}
|
|
||||||
string IRGenerationContext::runtimeObjectName(ContractDefinition const& _contract) const
|
|
||||||
{
|
|
||||||
return _contract.name() + "_" + toString(_contract.id()) + "_deployed";
|
|
||||||
}
|
|
||||||
|
|
||||||
string IRGenerationContext::newYulVariable()
|
string IRGenerationContext::newYulVariable()
|
||||||
{
|
{
|
||||||
return "_" + to_string(++m_varCounter);
|
return "_" + to_string(++m_varCounter);
|
||||||
}
|
}
|
||||||
|
|
||||||
string IRGenerationContext::trySuccessConditionVariable(Expression const& _expression) const
|
|
||||||
{
|
|
||||||
// NB: The TypeChecker already ensured that the Expression is of type FunctionCall.
|
|
||||||
solAssert(
|
|
||||||
static_cast<FunctionCallAnnotation const&>(_expression.annotation()).tryCall,
|
|
||||||
"Parameter must be a FunctionCall with tryCall-annotation set."
|
|
||||||
);
|
|
||||||
|
|
||||||
return "trySuccessCondition_" + to_string(_expression.id());
|
|
||||||
}
|
|
||||||
|
|
||||||
string IRGenerationContext::internalDispatch(size_t _in, size_t _out)
|
string IRGenerationContext::internalDispatch(size_t _in, size_t _out)
|
||||||
{
|
{
|
||||||
string funName = "dispatch_internal_in_" + to_string(_in) + "_out_" + to_string(_out);
|
string funName = "dispatch_internal_in_" + to_string(_in) + "_out_" + to_string(_out);
|
||||||
@ -196,7 +164,7 @@ string IRGenerationContext::internalDispatch(size_t _in, size_t _out)
|
|||||||
|
|
||||||
functions.emplace_back(map<string, string> {
|
functions.emplace_back(map<string, string> {
|
||||||
{ "funID", to_string(function->id()) },
|
{ "funID", to_string(function->id()) },
|
||||||
{ "name", functionName(*function)}
|
{ "name", IRNames::function(*function)}
|
||||||
});
|
});
|
||||||
|
|
||||||
enqueueFunctionForCodeGeneration(*function);
|
enqueueFunctionForCodeGeneration(*function);
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
#include <libsolidity/interface/DebugSettings.h>
|
#include <libsolidity/interface/DebugSettings.h>
|
||||||
|
|
||||||
#include <libsolidity/codegen/MultiUseYulFunctionCollector.h>
|
#include <libsolidity/codegen/MultiUseYulFunctionCollector.h>
|
||||||
|
#include <libsolidity/codegen/ir/Common.h>
|
||||||
|
|
||||||
#include <liblangutil/EVMVersion.h>
|
#include <liblangutil/EVMVersion.h>
|
||||||
|
|
||||||
@ -99,12 +100,6 @@ public:
|
|||||||
return m_stateVariables.at(&_varDecl);
|
return m_stateVariables.at(&_varDecl);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string functionName(FunctionDefinition const& _function);
|
|
||||||
std::string functionName(VariableDeclaration const& _varDecl);
|
|
||||||
|
|
||||||
std::string creationObjectName(ContractDefinition const& _contract) const;
|
|
||||||
std::string runtimeObjectName(ContractDefinition const& _contract) const;
|
|
||||||
|
|
||||||
std::string newYulVariable();
|
std::string newYulVariable();
|
||||||
|
|
||||||
std::string internalDispatch(size_t _in, size_t _out);
|
std::string internalDispatch(size_t _in, size_t _out);
|
||||||
@ -122,10 +117,6 @@ public:
|
|||||||
|
|
||||||
RevertStrings revertStrings() const { return m_revertStrings; }
|
RevertStrings revertStrings() const { return m_revertStrings; }
|
||||||
|
|
||||||
/// @returns the variable name that can be used to inspect the success or failure of an external
|
|
||||||
/// function call that was invoked as part of the try statement.
|
|
||||||
std::string trySuccessConditionVariable(Expression const& _expression) const;
|
|
||||||
|
|
||||||
std::set<ContractDefinition const*, ASTNode::CompareByID>& subObjectsCreated() { return m_subObjects; }
|
std::set<ContractDefinition const*, ASTNode::CompareByID>& subObjectsCreated() { return m_subObjects; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -114,7 +114,7 @@ string IRGenerator::generate(
|
|||||||
for (VariableDeclaration const* var: ContractType(_contract).immutableVariables())
|
for (VariableDeclaration const* var: ContractType(_contract).immutableVariables())
|
||||||
m_context.registerImmutableVariable(*var);
|
m_context.registerImmutableVariable(*var);
|
||||||
|
|
||||||
t("CreationObject", m_context.creationObjectName(_contract));
|
t("CreationObject", IRNames::creationObject(_contract));
|
||||||
t("memoryInit", memoryInit());
|
t("memoryInit", memoryInit());
|
||||||
t("notLibrary", !_contract.isLibrary());
|
t("notLibrary", !_contract.isLibrary());
|
||||||
|
|
||||||
@ -127,12 +127,12 @@ string IRGenerator::generate(
|
|||||||
constructorParams.emplace_back(m_context.newYulVariable());
|
constructorParams.emplace_back(m_context.newYulVariable());
|
||||||
t(
|
t(
|
||||||
"copyConstructorArguments",
|
"copyConstructorArguments",
|
||||||
m_utils.copyConstructorArgumentsToMemoryFunction(_contract, m_context.creationObjectName(_contract))
|
m_utils.copyConstructorArgumentsToMemoryFunction(_contract, IRNames::creationObject(_contract))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
t("constructorParams", joinHumanReadable(constructorParams));
|
t("constructorParams", joinHumanReadable(constructorParams));
|
||||||
t("constructorHasParams", !constructorParams.empty());
|
t("constructorHasParams", !constructorParams.empty());
|
||||||
t("implicitConstructor", implicitConstructorName(_contract));
|
t("implicitConstructor", IRNames::implicitConstructor(_contract));
|
||||||
|
|
||||||
t("deploy", deployCode(_contract));
|
t("deploy", deployCode(_contract));
|
||||||
generateImplicitConstructors(_contract);
|
generateImplicitConstructors(_contract);
|
||||||
@ -142,7 +142,7 @@ string IRGenerator::generate(
|
|||||||
|
|
||||||
resetContext(_contract);
|
resetContext(_contract);
|
||||||
// Do not register immutables to avoid assignment.
|
// Do not register immutables to avoid assignment.
|
||||||
t("RuntimeObject", m_context.runtimeObjectName(_contract));
|
t("RuntimeObject", IRNames::runtimeObject(_contract));
|
||||||
t("dispatch", dispatchRoutine(_contract));
|
t("dispatch", dispatchRoutine(_contract));
|
||||||
generateQueuedFunctions();
|
generateQueuedFunctions();
|
||||||
t("runtimeFunctions", m_context.functionCollector().requestedFunctions());
|
t("runtimeFunctions", m_context.functionCollector().requestedFunctions());
|
||||||
@ -166,7 +166,7 @@ void IRGenerator::generateQueuedFunctions()
|
|||||||
|
|
||||||
string IRGenerator::generateFunction(FunctionDefinition const& _function)
|
string IRGenerator::generateFunction(FunctionDefinition const& _function)
|
||||||
{
|
{
|
||||||
string functionName = m_context.functionName(_function);
|
string functionName = IRNames::function(_function);
|
||||||
return m_context.functionCollector().createFunction(functionName, [&]() {
|
return m_context.functionCollector().createFunction(functionName, [&]() {
|
||||||
Whiskers t(R"(
|
Whiskers t(R"(
|
||||||
function <functionName>(<params>)<?+retParams> -> <retParams></+retParams> {
|
function <functionName>(<params>)<?+retParams> -> <retParams></+retParams> {
|
||||||
@ -195,7 +195,7 @@ string IRGenerator::generateFunction(FunctionDefinition const& _function)
|
|||||||
|
|
||||||
string IRGenerator::generateGetter(VariableDeclaration const& _varDecl)
|
string IRGenerator::generateGetter(VariableDeclaration const& _varDecl)
|
||||||
{
|
{
|
||||||
string functionName = m_context.functionName(_varDecl);
|
string functionName = IRNames::function(_varDecl);
|
||||||
|
|
||||||
Type const* type = _varDecl.annotation().type;
|
Type const* type = _varDecl.annotation().type;
|
||||||
|
|
||||||
@ -378,7 +378,7 @@ void IRGenerator::generateImplicitConstructors(ContractDefinition const& _contra
|
|||||||
ContractDefinition const* contract = _contract.annotation().linearizedBaseContracts[i];
|
ContractDefinition const* contract = _contract.annotation().linearizedBaseContracts[i];
|
||||||
baseConstructorParams.erase(contract);
|
baseConstructorParams.erase(contract);
|
||||||
|
|
||||||
m_context.functionCollector().createFunction(implicitConstructorName(*contract), [&]() {
|
m_context.functionCollector().createFunction(IRNames::implicitConstructor(*contract), [&]() {
|
||||||
Whiskers t(R"(
|
Whiskers t(R"(
|
||||||
function <functionName>(<params><comma><baseParams>) {
|
function <functionName>(<params><comma><baseParams>) {
|
||||||
<evalBaseArguments>
|
<evalBaseArguments>
|
||||||
@ -395,7 +395,7 @@ void IRGenerator::generateImplicitConstructors(ContractDefinition const& _contra
|
|||||||
vector<string> baseParams = listAllParams(baseConstructorParams);
|
vector<string> baseParams = listAllParams(baseConstructorParams);
|
||||||
t("baseParams", joinHumanReadable(baseParams));
|
t("baseParams", joinHumanReadable(baseParams));
|
||||||
t("comma", !params.empty() && !baseParams.empty() ? ", " : "");
|
t("comma", !params.empty() && !baseParams.empty() ? ", " : "");
|
||||||
t("functionName", implicitConstructorName(*contract));
|
t("functionName", IRNames::implicitConstructor(*contract));
|
||||||
pair<string, map<ContractDefinition const*, vector<string>>> evaluatedArgs = evaluateConstructorArguments(*contract);
|
pair<string, map<ContractDefinition const*, vector<string>>> evaluatedArgs = evaluateConstructorArguments(*contract);
|
||||||
baseConstructorParams.insert(evaluatedArgs.second.begin(), evaluatedArgs.second.end());
|
baseConstructorParams.insert(evaluatedArgs.second.begin(), evaluatedArgs.second.end());
|
||||||
t("evalBaseArguments", evaluatedArgs.first);
|
t("evalBaseArguments", evaluatedArgs.first);
|
||||||
@ -403,7 +403,7 @@ void IRGenerator::generateImplicitConstructors(ContractDefinition const& _contra
|
|||||||
{
|
{
|
||||||
t("hasNextConstructor", true);
|
t("hasNextConstructor", true);
|
||||||
ContractDefinition const* nextContract = _contract.annotation().linearizedBaseContracts[i + 1];
|
ContractDefinition const* nextContract = _contract.annotation().linearizedBaseContracts[i + 1];
|
||||||
t("nextConstructor", implicitConstructorName(*nextContract));
|
t("nextConstructor", IRNames::implicitConstructor(*nextContract));
|
||||||
t("nextParams", joinHumanReadable(listAllParams(baseConstructorParams)));
|
t("nextParams", joinHumanReadable(listAllParams(baseConstructorParams)));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -431,7 +431,7 @@ string IRGenerator::deployCode(ContractDefinition const& _contract)
|
|||||||
|
|
||||||
return(0, datasize("<object>"))
|
return(0, datasize("<object>"))
|
||||||
)X");
|
)X");
|
||||||
t("object", m_context.runtimeObjectName(_contract));
|
t("object", IRNames::runtimeObject(_contract));
|
||||||
|
|
||||||
vector<map<string, string>> loadImmutables;
|
vector<map<string, string>> loadImmutables;
|
||||||
vector<map<string, string>> storeImmutables;
|
vector<map<string, string>> storeImmutables;
|
||||||
@ -462,11 +462,6 @@ string IRGenerator::callValueCheck()
|
|||||||
return "if callvalue() { revert(0, 0) }";
|
return "if callvalue() { revert(0, 0) }";
|
||||||
}
|
}
|
||||||
|
|
||||||
string IRGenerator::implicitConstructorName(ContractDefinition const& _contract)
|
|
||||||
{
|
|
||||||
return "constructor_" + _contract.name() + "_" + to_string(_contract.id());
|
|
||||||
}
|
|
||||||
|
|
||||||
string IRGenerator::dispatchRoutine(ContractDefinition const& _contract)
|
string IRGenerator::dispatchRoutine(ContractDefinition const& _contract)
|
||||||
{
|
{
|
||||||
Whiskers t(R"X(
|
Whiskers t(R"X(
|
||||||
|
@ -92,8 +92,6 @@ private:
|
|||||||
std::string deployCode(ContractDefinition const& _contract);
|
std::string deployCode(ContractDefinition const& _contract);
|
||||||
std::string callValueCheck();
|
std::string callValueCheck();
|
||||||
|
|
||||||
std::string implicitConstructorName(ContractDefinition const& _contract);
|
|
||||||
|
|
||||||
std::string dispatchRoutine(ContractDefinition const& _contract);
|
std::string dispatchRoutine(ContractDefinition const& _contract);
|
||||||
|
|
||||||
std::string memoryInit();
|
std::string memoryInit();
|
||||||
|
@ -181,7 +181,7 @@ IRVariable IRGeneratorForStatements::evaluateExpression(Expression const& _expre
|
|||||||
|
|
||||||
string IRGeneratorForStatements::constantValueFunction(VariableDeclaration const& _constant)
|
string IRGeneratorForStatements::constantValueFunction(VariableDeclaration const& _constant)
|
||||||
{
|
{
|
||||||
string functionName = "constant_" + _constant.name() + "_" + to_string(_constant.id());
|
string functionName = IRNames::constantValueFunction(_constant);
|
||||||
return m_context.functionCollector().createFunction(functionName, [&] {
|
return m_context.functionCollector().createFunction(functionName, [&] {
|
||||||
Whiskers templ(R"(
|
Whiskers templ(R"(
|
||||||
function <functionName>() -> <ret> {
|
function <functionName>() -> <ret> {
|
||||||
@ -952,14 +952,6 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
|||||||
"))\n";
|
"))\n";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case FunctionType::Kind::ECRecover:
|
|
||||||
case FunctionType::Kind::SHA256:
|
|
||||||
case FunctionType::Kind::RIPEMD160:
|
|
||||||
{
|
|
||||||
solAssert(!_functionCall.annotation().tryCall, "");
|
|
||||||
appendExternalFunctionCall(_functionCall, arguments);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case FunctionType::Kind::ArrayPop:
|
case FunctionType::Kind::ArrayPop:
|
||||||
{
|
{
|
||||||
auto const& memberAccessExpression = dynamic_cast<MemberAccess const&>(_functionCall.expression()).expression();
|
auto const& memberAccessExpression = dynamic_cast<MemberAccess const&>(_functionCall.expression()).expression();
|
||||||
@ -971,10 +963,12 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
|||||||
")\n";
|
")\n";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case FunctionType::Kind::ByteArrayPush:
|
||||||
case FunctionType::Kind::ArrayPush:
|
case FunctionType::Kind::ArrayPush:
|
||||||
{
|
{
|
||||||
auto const& memberAccessExpression = dynamic_cast<MemberAccess const&>(_functionCall.expression()).expression();
|
auto const& memberAccessExpression = dynamic_cast<MemberAccess const&>(_functionCall.expression()).expression();
|
||||||
ArrayType const& arrayType = dynamic_cast<ArrayType const&>(*memberAccessExpression.annotation().type);
|
ArrayType const& arrayType = dynamic_cast<ArrayType const&>(*memberAccessExpression.annotation().type);
|
||||||
|
|
||||||
if (arguments.empty())
|
if (arguments.empty())
|
||||||
{
|
{
|
||||||
auto slotName = m_context.newYulVariable();
|
auto slotName = m_context.newYulVariable();
|
||||||
@ -1111,7 +1105,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
|||||||
t("memEnd", m_context.newYulVariable());
|
t("memEnd", m_context.newYulVariable());
|
||||||
t("allocateTemporaryMemory", m_utils.allocationTemporaryMemoryFunction());
|
t("allocateTemporaryMemory", m_utils.allocationTemporaryMemoryFunction());
|
||||||
t("releaseTemporaryMemory", m_utils.releaseTemporaryMemoryFunction());
|
t("releaseTemporaryMemory", m_utils.releaseTemporaryMemoryFunction());
|
||||||
t("object", m_context.creationObjectName(*contract));
|
t("object", IRNames::creationObject(*contract));
|
||||||
t("abiEncode",
|
t("abiEncode",
|
||||||
m_context.abiFunctions().tupleEncoder(argumentTypes, functionType->parameterTypes(), false)
|
m_context.abiFunctions().tupleEncoder(argumentTypes, functionType->parameterTypes(), false)
|
||||||
);
|
);
|
||||||
@ -1153,6 +1147,69 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case FunctionType::Kind::ECRecover:
|
||||||
|
case FunctionType::Kind::RIPEMD160:
|
||||||
|
case FunctionType::Kind::SHA256:
|
||||||
|
{
|
||||||
|
solAssert(!_functionCall.annotation().tryCall, "");
|
||||||
|
solAssert(!functionType->valueSet(), "");
|
||||||
|
solAssert(!functionType->gasSet(), "");
|
||||||
|
solAssert(!functionType->bound(), "");
|
||||||
|
|
||||||
|
static map<FunctionType::Kind, std::tuple<u160, size_t>> precompiles = {
|
||||||
|
{FunctionType::Kind::ECRecover, std::make_tuple(1, 0)},
|
||||||
|
{FunctionType::Kind::SHA256, std::make_tuple(2, 0)},
|
||||||
|
{FunctionType::Kind::RIPEMD160, std::make_tuple(3, 12)},
|
||||||
|
};
|
||||||
|
auto [ address, offset ] = precompiles[functionType->kind()];
|
||||||
|
TypePointers argumentTypes;
|
||||||
|
vector<string> argumentStrings;
|
||||||
|
for (auto const& arg: arguments)
|
||||||
|
{
|
||||||
|
argumentTypes.emplace_back(&type(*arg));
|
||||||
|
argumentStrings += IRVariable(*arg).stackSlots();
|
||||||
|
}
|
||||||
|
Whiskers templ(R"(
|
||||||
|
let <pos> := <allocateTemporary>()
|
||||||
|
let <end> := <encodeArgs>(<pos> <argumentString>)
|
||||||
|
<?isECRecover>
|
||||||
|
mstore(0, 0)
|
||||||
|
</isECRecover>
|
||||||
|
let <success> := <call>(<gas>, <address> <?isCall>, 0</isCall>, <pos>, sub(<end>, <pos>), 0, 32)
|
||||||
|
if iszero(<success>) { <forwardingRevert>() }
|
||||||
|
let <retVars> := <shl>(mload(0))
|
||||||
|
)");
|
||||||
|
templ("call", m_context.evmVersion().hasStaticCall() ? "staticcall" : "call");
|
||||||
|
templ("isCall", !m_context.evmVersion().hasStaticCall());
|
||||||
|
templ("shl", m_utils.shiftLeftFunction(offset * 8));
|
||||||
|
templ("allocateTemporary", m_utils.allocationTemporaryMemoryFunction());
|
||||||
|
templ("pos", m_context.newYulVariable());
|
||||||
|
templ("end", m_context.newYulVariable());
|
||||||
|
templ("isECRecover", FunctionType::Kind::ECRecover == functionType->kind());
|
||||||
|
if (FunctionType::Kind::ECRecover == functionType->kind())
|
||||||
|
templ("encodeArgs", m_context.abiFunctions().tupleEncoder(argumentTypes, parameterTypes));
|
||||||
|
else
|
||||||
|
templ("encodeArgs", m_context.abiFunctions().tupleEncoderPacked(argumentTypes, parameterTypes));
|
||||||
|
templ("argumentString", joinHumanReadablePrefixed(argumentStrings));
|
||||||
|
templ("address", toString(address));
|
||||||
|
templ("success", m_context.newYulVariable());
|
||||||
|
templ("retVars", IRVariable(_functionCall).commaSeparatedList());
|
||||||
|
templ("forwardingRevert", m_utils.forwardingRevertFunction());
|
||||||
|
if (m_context.evmVersion().canOverchargeGasForCall())
|
||||||
|
// Send all gas (requires tangerine whistle EVM)
|
||||||
|
templ("gas", "gas()");
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// @todo The value 10 is not exact and this could be fine-tuned,
|
||||||
|
// but this has worked for years in the old code generator.
|
||||||
|
u256 gasNeededByCaller = evmasm::GasCosts::callGas(m_context.evmVersion()) + 10 + evmasm::GasCosts::callNewAccountGas;
|
||||||
|
templ("gas", "sub(gas(), " + formatNumber(gasNeededByCaller) + ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
m_code << templ.render();
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
solUnimplemented("FunctionKind " + toString(static_cast<int>(functionType->kind())) + " not yet implemented");
|
solUnimplemented("FunctionKind " + toString(static_cast<int>(functionType->kind())) + " not yet implemented");
|
||||||
}
|
}
|
||||||
@ -1316,7 +1373,7 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
|
|||||||
)")
|
)")
|
||||||
("allocationFunction", m_utils.allocationFunction())
|
("allocationFunction", m_utils.allocationFunction())
|
||||||
("size", m_context.newYulVariable())
|
("size", m_context.newYulVariable())
|
||||||
("objectName", m_context.creationObjectName(contract))
|
("objectName", IRNames::creationObject(contract))
|
||||||
("result", IRVariable(_memberAccess).commaSeparatedList()).render();
|
("result", IRVariable(_memberAccess).commaSeparatedList()).render();
|
||||||
}
|
}
|
||||||
else if (member == "name")
|
else if (member == "name")
|
||||||
@ -1835,7 +1892,7 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
|
|||||||
templ("pos", m_context.newYulVariable());
|
templ("pos", m_context.newYulVariable());
|
||||||
templ("end", m_context.newYulVariable());
|
templ("end", m_context.newYulVariable());
|
||||||
if (_functionCall.annotation().tryCall)
|
if (_functionCall.annotation().tryCall)
|
||||||
templ("success", m_context.trySuccessConditionVariable(_functionCall));
|
templ("success", IRNames::trySuccessConditionVariable(_functionCall));
|
||||||
else
|
else
|
||||||
templ("success", m_context.newYulVariable());
|
templ("success", m_context.newYulVariable());
|
||||||
templ("freeMemory", freeMemory());
|
templ("freeMemory", freeMemory());
|
||||||
@ -2070,10 +2127,7 @@ void IRGeneratorForStatements::declareAssign(IRVariable const& _lhs, IRVariable
|
|||||||
|
|
||||||
IRVariable IRGeneratorForStatements::zeroValue(Type const& _type, bool _splitFunctionTypes)
|
IRVariable IRGeneratorForStatements::zeroValue(Type const& _type, bool _splitFunctionTypes)
|
||||||
{
|
{
|
||||||
IRVariable irVar{
|
IRVariable irVar{IRNames::zeroValue(_type, m_context.newYulVariable()), _type};
|
||||||
"zero_value_for_type_" + _type.identifier() + m_context.newYulVariable(),
|
|
||||||
_type
|
|
||||||
};
|
|
||||||
define(irVar) << m_utils.zeroValueFunction(_type, _splitFunctionTypes) << "()\n";
|
define(irVar) << m_utils.zeroValueFunction(_type, _splitFunctionTypes) << "()\n";
|
||||||
return irVar;
|
return irVar;
|
||||||
}
|
}
|
||||||
@ -2392,7 +2446,7 @@ bool IRGeneratorForStatements::visit(TryStatement const& _tryStatement)
|
|||||||
Expression const& externalCall = _tryStatement.externalCall();
|
Expression const& externalCall = _tryStatement.externalCall();
|
||||||
externalCall.accept(*this);
|
externalCall.accept(*this);
|
||||||
|
|
||||||
m_code << "switch iszero(" << m_context.trySuccessConditionVariable(externalCall) << ")\n";
|
m_code << "switch iszero(" << IRNames::trySuccessConditionVariable(externalCall) << ")\n";
|
||||||
|
|
||||||
m_code << "case 0 { // success case\n";
|
m_code << "case 0 { // success case\n";
|
||||||
TryCatchClause const& successClause = *_tryStatement.clauses().front();
|
TryCatchClause const& successClause = *_tryStatement.clauses().front();
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
#include <libsolidity/codegen/ir/Common.h>
|
||||||
#include <libsolidity/codegen/ir/IRVariable.h>
|
#include <libsolidity/codegen/ir/IRVariable.h>
|
||||||
#include <libsolidity/ast/AST.h>
|
#include <libsolidity/ast/AST.h>
|
||||||
#include <boost/range/adaptor/transformed.hpp>
|
#include <boost/range/adaptor/transformed.hpp>
|
||||||
@ -30,19 +31,13 @@ IRVariable::IRVariable(std::string _baseName, Type const& _type):
|
|||||||
}
|
}
|
||||||
|
|
||||||
IRVariable::IRVariable(VariableDeclaration const& _declaration):
|
IRVariable::IRVariable(VariableDeclaration const& _declaration):
|
||||||
IRVariable(
|
IRVariable(IRNames::localVariable(_declaration), *_declaration.annotation().type)
|
||||||
"vloc_" + _declaration.name() + '_' + std::to_string(_declaration.id()),
|
|
||||||
*_declaration.annotation().type
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
solAssert(!_declaration.isStateVariable(), "");
|
solAssert(!_declaration.isStateVariable(), "");
|
||||||
}
|
}
|
||||||
|
|
||||||
IRVariable::IRVariable(Expression const& _expression):
|
IRVariable::IRVariable(Expression const& _expression):
|
||||||
IRVariable(
|
IRVariable(IRNames::localVariable(_expression), *_expression.annotation().type)
|
||||||
"expr_" + to_string(_expression.id()),
|
|
||||||
*_expression.annotation().type
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,7 +94,7 @@ IRVariable IRVariable::tupleComponent(size_t _i) const
|
|||||||
m_type.category() == Type::Category::Tuple,
|
m_type.category() == Type::Category::Tuple,
|
||||||
"Requested tuple component of non-tuple IR variable."
|
"Requested tuple component of non-tuple IR variable."
|
||||||
);
|
);
|
||||||
return part("component_" + std::to_string(_i + 1));
|
return part(IRNames::tupleComponent(_i));
|
||||||
}
|
}
|
||||||
|
|
||||||
string IRVariable::suffixedName(string const& _suffix) const
|
string IRVariable::suffixedName(string const& _suffix) const
|
||||||
|
@ -98,17 +98,45 @@ void CHC::analyze(SourceUnit const& _source)
|
|||||||
|
|
||||||
for (auto const& [scope, target]: m_verificationTargets)
|
for (auto const& [scope, target]: m_verificationTargets)
|
||||||
{
|
{
|
||||||
auto assertions = transactionAssertions(scope);
|
if (target.type == VerificationTarget::Type::Assert)
|
||||||
for (auto const* assertion: assertions)
|
|
||||||
{
|
{
|
||||||
|
auto assertions = transactionAssertions(scope);
|
||||||
|
for (auto const* assertion: assertions)
|
||||||
|
{
|
||||||
|
createErrorBlock();
|
||||||
|
connectBlocks(target.value, error(), target.constraints && (target.errorId == assertion->id()));
|
||||||
|
auto [result, model] = query(error(), assertion->location());
|
||||||
|
// This should be fine but it's a bug in the old compiler
|
||||||
|
(void)model;
|
||||||
|
if (result == smt::CheckResult::UNSATISFIABLE)
|
||||||
|
m_safeAssertions.insert(assertion);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (target.type == VerificationTarget::Type::PopEmptyArray)
|
||||||
|
{
|
||||||
|
solAssert(dynamic_cast<FunctionCall const*>(scope), "");
|
||||||
createErrorBlock();
|
createErrorBlock();
|
||||||
connectBlocks(target.value, error(), target.constraints && (target.errorId == assertion->id()));
|
connectBlocks(target.value, error(), target.constraints && (target.errorId == scope->id()));
|
||||||
auto [result, model] = query(error(), assertion->location());
|
auto [result, model] = query(error(), scope->location());
|
||||||
// This should be fine but it's a bug in the old compiler
|
// This should be fine but it's a bug in the old compiler
|
||||||
(void)model;
|
(void)model;
|
||||||
if (result == smt::CheckResult::UNSATISFIABLE)
|
if (result != smt::CheckResult::UNSATISFIABLE)
|
||||||
m_safeAssertions.insert(assertion);
|
{
|
||||||
|
string msg = "Empty array \"pop\" ";
|
||||||
|
if (result == smt::CheckResult::SATISFIABLE)
|
||||||
|
msg += "detected here.";
|
||||||
|
else
|
||||||
|
msg += "might happen here.";
|
||||||
|
m_unsafeTargets.insert(scope);
|
||||||
|
m_outerErrorReporter.warning(
|
||||||
|
2529_error,
|
||||||
|
scope->location(),
|
||||||
|
msg
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
solAssert(false, "");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -161,7 +189,7 @@ void CHC::endVisit(ContractDefinition const& _contract)
|
|||||||
auto stateExprs = vector<smt::Expression>{m_error.currentValue()} + currentStateVariables();
|
auto stateExprs = vector<smt::Expression>{m_error.currentValue()} + currentStateVariables();
|
||||||
setCurrentBlock(*m_constructorSummaryPredicate, &stateExprs);
|
setCurrentBlock(*m_constructorSummaryPredicate, &stateExprs);
|
||||||
|
|
||||||
addVerificationTarget(m_currentContract, m_currentBlock, smt::Expression(true), m_error.currentValue());
|
addAssertVerificationTarget(m_currentContract, m_currentBlock, smt::Expression(true), m_error.currentValue());
|
||||||
connectBlocks(m_currentBlock, interface(), m_error.currentValue() == 0);
|
connectBlocks(m_currentBlock, interface(), m_error.currentValue() == 0);
|
||||||
|
|
||||||
SMTEncoder::endVisit(_contract);
|
SMTEncoder::endVisit(_contract);
|
||||||
@ -256,7 +284,7 @@ void CHC::endVisit(FunctionDefinition const& _function)
|
|||||||
|
|
||||||
if (_function.isPublic())
|
if (_function.isPublic())
|
||||||
{
|
{
|
||||||
addVerificationTarget(&_function, m_currentBlock, sum, assertionError);
|
addAssertVerificationTarget(&_function, m_currentBlock, sum, assertionError);
|
||||||
connectBlocks(m_currentBlock, iface, sum && (assertionError == 0));
|
connectBlocks(m_currentBlock, iface, sum && (assertionError == 0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -556,10 +584,34 @@ void CHC::unknownFunctionCall(FunctionCall const&)
|
|||||||
m_unknownFunctionCallSeen = true;
|
m_unknownFunctionCallSeen = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CHC::makeArrayPopVerificationTarget(FunctionCall const& _arrayPop)
|
||||||
|
{
|
||||||
|
FunctionType const& funType = dynamic_cast<FunctionType const&>(*_arrayPop.expression().annotation().type);
|
||||||
|
solAssert(funType.kind() == FunctionType::Kind::ArrayPop, "");
|
||||||
|
|
||||||
|
auto memberAccess = dynamic_cast<MemberAccess const*>(&_arrayPop.expression());
|
||||||
|
solAssert(memberAccess, "");
|
||||||
|
auto symbArray = dynamic_pointer_cast<smt::SymbolicArrayVariable>(m_context.expression(memberAccess->expression()));
|
||||||
|
solAssert(symbArray, "");
|
||||||
|
|
||||||
|
auto previousError = m_error.currentValue();
|
||||||
|
m_error.increaseIndex();
|
||||||
|
|
||||||
|
addArrayPopVerificationTarget(&_arrayPop, m_error.currentValue());
|
||||||
|
connectBlocks(
|
||||||
|
m_currentBlock,
|
||||||
|
m_currentFunction->isConstructor() ? summary(*m_currentContract) : summary(*m_currentFunction),
|
||||||
|
currentPathConditions() && symbArray->length() <= 0 && m_error.currentValue() == _arrayPop.id()
|
||||||
|
);
|
||||||
|
|
||||||
|
m_context.addAssertion(m_error.currentValue() == previousError);
|
||||||
|
}
|
||||||
|
|
||||||
void CHC::resetSourceAnalysis()
|
void CHC::resetSourceAnalysis()
|
||||||
{
|
{
|
||||||
m_verificationTargets.clear();
|
m_verificationTargets.clear();
|
||||||
m_safeAssertions.clear();
|
m_safeAssertions.clear();
|
||||||
|
m_unsafeTargets.clear();
|
||||||
m_functionAssertions.clear();
|
m_functionAssertions.clear();
|
||||||
m_callGraph.clear();
|
m_callGraph.clear();
|
||||||
m_summaries.clear();
|
m_summaries.clear();
|
||||||
@ -974,9 +1026,35 @@ pair<smt::CheckResult, vector<string>> CHC::query(smt::Expression const& _query,
|
|||||||
return {result, values};
|
return {result, values};
|
||||||
}
|
}
|
||||||
|
|
||||||
void CHC::addVerificationTarget(ASTNode const* _scope, smt::Expression _from, smt::Expression _constraints, smt::Expression _errorId)
|
void CHC::addVerificationTarget(
|
||||||
|
ASTNode const* _scope,
|
||||||
|
VerificationTarget::Type _type,
|
||||||
|
smt::Expression _from,
|
||||||
|
smt::Expression _constraints,
|
||||||
|
smt::Expression _errorId
|
||||||
|
)
|
||||||
{
|
{
|
||||||
m_verificationTargets.emplace(_scope, CHCVerificationTarget{{VerificationTarget::Type::Assert, _from, _constraints}, _errorId});
|
m_verificationTargets.emplace(_scope, CHCVerificationTarget{{_type, _from, _constraints}, _errorId});
|
||||||
|
}
|
||||||
|
|
||||||
|
void CHC::addAssertVerificationTarget(ASTNode const* _scope, smt::Expression _from, smt::Expression _constraints, smt::Expression _errorId)
|
||||||
|
{
|
||||||
|
addVerificationTarget(_scope, VerificationTarget::Type::Assert, _from, _constraints, _errorId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CHC::addArrayPopVerificationTarget(ASTNode const* _scope, smt::Expression _errorId)
|
||||||
|
{
|
||||||
|
solAssert(m_currentContract, "");
|
||||||
|
solAssert(m_currentFunction, "");
|
||||||
|
|
||||||
|
if (m_currentFunction->isConstructor())
|
||||||
|
addVerificationTarget(_scope, VerificationTarget::Type::PopEmptyArray, summary(*m_currentContract), smt::Expression(true), _errorId);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto iface = (*m_interfaces.at(m_currentContract))(initialStateVariables());
|
||||||
|
auto sum = summary(*m_currentFunction);
|
||||||
|
addVerificationTarget(_scope, VerificationTarget::Type::PopEmptyArray, iface, sum, _errorId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
string CHC::uniquePrefix()
|
string CHC::uniquePrefix()
|
||||||
|
@ -78,6 +78,7 @@ private:
|
|||||||
void visitAssert(FunctionCall const& _funCall);
|
void visitAssert(FunctionCall const& _funCall);
|
||||||
void internalFunctionCall(FunctionCall const& _funCall);
|
void internalFunctionCall(FunctionCall const& _funCall);
|
||||||
void unknownFunctionCall(FunctionCall const& _funCall);
|
void unknownFunctionCall(FunctionCall const& _funCall);
|
||||||
|
void makeArrayPopVerificationTarget(FunctionCall const& _arrayPop) override;
|
||||||
//@}
|
//@}
|
||||||
|
|
||||||
struct IdCompare
|
struct IdCompare
|
||||||
@ -182,7 +183,9 @@ private:
|
|||||||
/// @returns <false, model> otherwise.
|
/// @returns <false, model> otherwise.
|
||||||
std::pair<smt::CheckResult, std::vector<std::string>> query(smt::Expression const& _query, langutil::SourceLocation const& _location);
|
std::pair<smt::CheckResult, std::vector<std::string>> query(smt::Expression const& _query, langutil::SourceLocation const& _location);
|
||||||
|
|
||||||
void addVerificationTarget(ASTNode const* _scope, smt::Expression _from, smt::Expression _constraints, smt::Expression _errorId);
|
void addVerificationTarget(ASTNode const* _scope, VerificationTarget::Type _type, smt::Expression _from, smt::Expression _constraints, smt::Expression _errorId);
|
||||||
|
void addAssertVerificationTarget(ASTNode const* _scope, smt::Expression _from, smt::Expression _constraints, smt::Expression _errorId);
|
||||||
|
void addArrayPopVerificationTarget(ASTNode const* _scope, smt::Expression _errorId);
|
||||||
//@}
|
//@}
|
||||||
|
|
||||||
/// Misc.
|
/// Misc.
|
||||||
@ -245,6 +248,8 @@ private:
|
|||||||
|
|
||||||
/// Assertions proven safe.
|
/// Assertions proven safe.
|
||||||
std::set<Expression const*> m_safeAssertions;
|
std::set<Expression const*> m_safeAssertions;
|
||||||
|
/// Targets proven unsafe.
|
||||||
|
std::set<ASTNode const*> m_unsafeTargets;
|
||||||
//@}
|
//@}
|
||||||
|
|
||||||
/// Control-flow.
|
/// Control-flow.
|
||||||
|
@ -206,6 +206,15 @@ CVC4::Expr CVC4Interface::toCVC4Expr(Expression const& _expr)
|
|||||||
CVC4::Expr s = dt[0][index].getSelector();
|
CVC4::Expr s = dt[0][index].getSelector();
|
||||||
return m_context.mkExpr(CVC4::kind::APPLY_SELECTOR, s, arguments[0]);
|
return m_context.mkExpr(CVC4::kind::APPLY_SELECTOR, s, arguments[0]);
|
||||||
}
|
}
|
||||||
|
else if (n == "tuple_constructor")
|
||||||
|
{
|
||||||
|
shared_ptr<TupleSort> tupleSort = std::dynamic_pointer_cast<TupleSort>(_expr.sort);
|
||||||
|
solAssert(tupleSort, "");
|
||||||
|
CVC4::DatatypeType tt = m_context.mkTupleType(cvc4Sort(tupleSort->components));
|
||||||
|
CVC4::Datatype const& dt = tt.getDatatype();
|
||||||
|
CVC4::Expr c = dt[0].getConstructor();
|
||||||
|
return m_context.mkExpr(CVC4::kind::APPLY_CONSTRUCTOR, c, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
solAssert(false, "");
|
solAssert(false, "");
|
||||||
}
|
}
|
||||||
|
@ -646,6 +646,12 @@ void SMTEncoder::endVisit(FunctionCall const& _funCall)
|
|||||||
m_context.state().transfer(m_context.state().thisAddress(), expr(address), expr(*value));
|
m_context.state().transfer(m_context.state().thisAddress(), expr(address), expr(*value));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case FunctionType::Kind::ArrayPush:
|
||||||
|
arrayPush(_funCall);
|
||||||
|
break;
|
||||||
|
case FunctionType::Kind::ArrayPop:
|
||||||
|
arrayPop(_funCall);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
m_errorReporter.warning(
|
m_errorReporter.warning(
|
||||||
4588_error,
|
4588_error,
|
||||||
@ -890,6 +896,23 @@ bool SMTEncoder::visit(MemberAccess const& _memberAccess)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (exprType->category() == Type::Category::Array)
|
||||||
|
{
|
||||||
|
_memberAccess.expression().accept(*this);
|
||||||
|
if (_memberAccess.memberName() == "length")
|
||||||
|
{
|
||||||
|
auto symbArray = dynamic_pointer_cast<smt::SymbolicArrayVariable>(m_context.expression(_memberAccess.expression()));
|
||||||
|
solAssert(symbArray, "");
|
||||||
|
defineExpr(_memberAccess, symbArray->length());
|
||||||
|
m_uninterpretedTerms.insert(&_memberAccess);
|
||||||
|
setSymbolicUnknownValue(
|
||||||
|
expr(_memberAccess),
|
||||||
|
_memberAccess.annotation().type,
|
||||||
|
m_context
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
m_errorReporter.warning(
|
m_errorReporter.warning(
|
||||||
7650_error,
|
7650_error,
|
||||||
@ -939,9 +962,10 @@ void SMTEncoder::endVisit(IndexAccess const& _indexAccess)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
solAssert(array, "");
|
auto arrayVar = dynamic_pointer_cast<smt::SymbolicArrayVariable>(array);
|
||||||
|
solAssert(arrayVar, "");
|
||||||
defineExpr(_indexAccess, smt::Expression::select(
|
defineExpr(_indexAccess, smt::Expression::select(
|
||||||
array->currentValue(),
|
arrayVar->elements(),
|
||||||
expr(*_indexAccess.indexExpression())
|
expr(*_indexAccess.indexExpression())
|
||||||
));
|
));
|
||||||
setSymbolicUnknownValue(
|
setSymbolicUnknownValue(
|
||||||
@ -1013,16 +1037,20 @@ void SMTEncoder::arrayIndexAssignment(Expression const& _expr, smt::Expression c
|
|||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
auto symbArray = dynamic_pointer_cast<smt::SymbolicArrayVariable>(m_context.variable(*varDecl));
|
||||||
smt::Expression store = smt::Expression::store(
|
smt::Expression store = smt::Expression::store(
|
||||||
m_context.variable(*varDecl)->currentValue(),
|
symbArray->elements(),
|
||||||
expr(*indexAccess->indexExpression()),
|
expr(*indexAccess->indexExpression()),
|
||||||
toStore
|
toStore
|
||||||
);
|
);
|
||||||
m_context.addAssertion(m_context.newValue(*varDecl) == store);
|
auto oldLength = symbArray->length();
|
||||||
|
symbArray->increaseIndex();
|
||||||
|
m_context.addAssertion(symbArray->elements() == store);
|
||||||
|
m_context.addAssertion(symbArray->length() == oldLength);
|
||||||
// Update the SMT select value after the assignment,
|
// Update the SMT select value after the assignment,
|
||||||
// necessary for sound models.
|
// necessary for sound models.
|
||||||
defineExpr(*indexAccess, smt::Expression::select(
|
defineExpr(*indexAccess, smt::Expression::select(
|
||||||
m_context.variable(*varDecl)->currentValue(),
|
symbArray->elements(),
|
||||||
expr(*indexAccess->indexExpression())
|
expr(*indexAccess->indexExpression())
|
||||||
));
|
));
|
||||||
|
|
||||||
@ -1030,7 +1058,12 @@ void SMTEncoder::arrayIndexAssignment(Expression const& _expr, smt::Expression c
|
|||||||
}
|
}
|
||||||
else if (auto base = dynamic_cast<IndexAccess const*>(&indexAccess->baseExpression()))
|
else if (auto base = dynamic_cast<IndexAccess const*>(&indexAccess->baseExpression()))
|
||||||
{
|
{
|
||||||
toStore = smt::Expression::store(expr(*base), expr(*indexAccess->indexExpression()), toStore);
|
auto symbArray = dynamic_pointer_cast<smt::SymbolicArrayVariable>(m_context.expression(*base));
|
||||||
|
solAssert(symbArray, "");
|
||||||
|
toStore = smt::Expression::tuple_constructor(
|
||||||
|
smt::Expression(base->annotation().type),
|
||||||
|
{smt::Expression::store(symbArray->elements(), expr(*indexAccess->indexExpression()), toStore), symbArray->length()}
|
||||||
|
);
|
||||||
indexAccess = base;
|
indexAccess = base;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -1045,6 +1078,76 @@ void SMTEncoder::arrayIndexAssignment(Expression const& _expr, smt::Expression c
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SMTEncoder::arrayPush(FunctionCall const& _funCall)
|
||||||
|
{
|
||||||
|
auto memberAccess = dynamic_cast<MemberAccess const*>(&_funCall.expression());
|
||||||
|
solAssert(memberAccess, "");
|
||||||
|
auto symbArray = dynamic_pointer_cast<smt::SymbolicArrayVariable>(m_context.expression(memberAccess->expression()));
|
||||||
|
solAssert(symbArray, "");
|
||||||
|
auto oldLength = symbArray->length();
|
||||||
|
m_context.addAssertion(oldLength >= 0);
|
||||||
|
// Real world assumption: the array length is assumed to not overflow.
|
||||||
|
// This assertion guarantees that both the current and updated lengths have the above property.
|
||||||
|
m_context.addAssertion(oldLength + 1 < (smt::maxValue(*TypeProvider::uint256()) - 1));
|
||||||
|
|
||||||
|
auto const& arguments = _funCall.arguments();
|
||||||
|
smt::Expression element = arguments.empty() ?
|
||||||
|
smt::zeroValue(_funCall.annotation().type) :
|
||||||
|
expr(*arguments.front());
|
||||||
|
smt::Expression store = smt::Expression::store(
|
||||||
|
symbArray->elements(),
|
||||||
|
oldLength,
|
||||||
|
element
|
||||||
|
);
|
||||||
|
symbArray->increaseIndex();
|
||||||
|
m_context.addAssertion(symbArray->elements() == store);
|
||||||
|
m_context.addAssertion(symbArray->length() == oldLength + 1);
|
||||||
|
|
||||||
|
if (arguments.empty())
|
||||||
|
defineExpr(_funCall, element);
|
||||||
|
|
||||||
|
arrayPushPopAssign(memberAccess->expression(), symbArray->currentValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
void SMTEncoder::arrayPop(FunctionCall const& _funCall)
|
||||||
|
{
|
||||||
|
auto memberAccess = dynamic_cast<MemberAccess const*>(&_funCall.expression());
|
||||||
|
solAssert(memberAccess, "");
|
||||||
|
auto symbArray = dynamic_pointer_cast<smt::SymbolicArrayVariable>(m_context.expression(memberAccess->expression()));
|
||||||
|
solAssert(symbArray, "");
|
||||||
|
|
||||||
|
makeArrayPopVerificationTarget(_funCall);
|
||||||
|
|
||||||
|
auto oldElements = symbArray->elements();
|
||||||
|
auto oldLength = symbArray->length();
|
||||||
|
m_context.addAssertion(oldLength > 0);
|
||||||
|
|
||||||
|
symbArray->increaseIndex();
|
||||||
|
m_context.addAssertion(symbArray->elements() == oldElements);
|
||||||
|
auto newLength = smt::Expression::ite(
|
||||||
|
oldLength == 0,
|
||||||
|
smt::maxValue(*TypeProvider::uint256()),
|
||||||
|
oldLength - 1
|
||||||
|
);
|
||||||
|
m_context.addAssertion(symbArray->length() == oldLength - 1);
|
||||||
|
|
||||||
|
arrayPushPopAssign(memberAccess->expression(), symbArray->currentValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
void SMTEncoder::arrayPushPopAssign(Expression const& _expr, smt::Expression const& _array)
|
||||||
|
{
|
||||||
|
if (auto const* id = dynamic_cast<Identifier const*>(&_expr))
|
||||||
|
{
|
||||||
|
auto varDecl = identifierToVariable(*id);
|
||||||
|
solAssert(varDecl, "");
|
||||||
|
m_context.addAssertion(m_context.newValue(*varDecl) == _array);
|
||||||
|
}
|
||||||
|
else if (auto const* indexAccess = dynamic_cast<IndexAccess const*>(&_expr))
|
||||||
|
arrayIndexAssignment(*indexAccess, _array);
|
||||||
|
else
|
||||||
|
solAssert(false, "");
|
||||||
|
}
|
||||||
|
|
||||||
void SMTEncoder::defineGlobalVariable(string const& _name, Expression const& _expr, bool _increaseIndex)
|
void SMTEncoder::defineGlobalVariable(string const& _name, Expression const& _expr, bool _increaseIndex)
|
||||||
{
|
{
|
||||||
if (!m_context.knownGlobalSymbol(_name))
|
if (!m_context.knownGlobalSymbol(_name))
|
||||||
@ -1326,7 +1429,14 @@ smt::Expression SMTEncoder::compoundAssignment(Assignment const& _assignment)
|
|||||||
|
|
||||||
void SMTEncoder::assignment(VariableDeclaration const& _variable, Expression const& _value)
|
void SMTEncoder::assignment(VariableDeclaration const& _variable, Expression const& _value)
|
||||||
{
|
{
|
||||||
assignment(_variable, expr(_value, _variable.type()));
|
// In general, at this point, the SMT sorts of _variable and _value are the same,
|
||||||
|
// even if there is implicit conversion.
|
||||||
|
// This is a special case where the SMT sorts are different.
|
||||||
|
// For now we are unaware of other cases where this happens, but if they do appear
|
||||||
|
// we should extract this into an `implicitConversion` function.
|
||||||
|
if (_variable.type()->category() != Type::Category::Array || _value.annotation().type->category() != Type::Category::StringLiteral)
|
||||||
|
assignment(_variable, expr(_value, _variable.type()));
|
||||||
|
// TODO else { store each string literal byte into the array }
|
||||||
}
|
}
|
||||||
|
|
||||||
void SMTEncoder::assignment(VariableDeclaration const& _variable, smt::Expression const& _value)
|
void SMTEncoder::assignment(VariableDeclaration const& _variable, smt::Expression const& _value)
|
||||||
@ -1616,7 +1726,7 @@ SMTEncoder::VariableIndices SMTEncoder::copyVariableIndices()
|
|||||||
void SMTEncoder::resetVariableIndices(VariableIndices const& _indices)
|
void SMTEncoder::resetVariableIndices(VariableIndices const& _indices)
|
||||||
{
|
{
|
||||||
for (auto const& var: _indices)
|
for (auto const& var: _indices)
|
||||||
m_context.variable(*var.first)->index() = var.second;
|
m_context.variable(*var.first)->setIndex(var.second);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SMTEncoder::clearIndices(ContractDefinition const* _contract, FunctionDefinition const* _function)
|
void SMTEncoder::clearIndices(ContractDefinition const* _contract, FunctionDefinition const* _function)
|
||||||
|
@ -137,6 +137,13 @@ protected:
|
|||||||
/// Handles assignment to SMT array index.
|
/// Handles assignment to SMT array index.
|
||||||
void arrayIndexAssignment(Expression const& _expr, smt::Expression const& _rightHandSide);
|
void arrayIndexAssignment(Expression const& _expr, smt::Expression const& _rightHandSide);
|
||||||
|
|
||||||
|
void arrayPush(FunctionCall const& _funCall);
|
||||||
|
void arrayPop(FunctionCall const& _funCall);
|
||||||
|
void arrayPushPopAssign(Expression const& _expr, smt::Expression const& _array);
|
||||||
|
/// Allows BMC and CHC to create verification targets for popping
|
||||||
|
/// an empty array.
|
||||||
|
virtual void makeArrayPopVerificationTarget(FunctionCall const&) {}
|
||||||
|
|
||||||
/// Division expression in the given type. Requires special treatment because
|
/// Division expression in the given type. Requires special treatment because
|
||||||
/// of rounding for signed division.
|
/// of rounding for signed division.
|
||||||
smt::Expression division(smt::Expression _left, smt::Expression _right, IntegerType const& _type);
|
smt::Expression division(smt::Expression _left, smt::Expression _right, IntegerType const& _type);
|
||||||
@ -240,7 +247,7 @@ protected:
|
|||||||
|
|
||||||
struct VerificationTarget
|
struct VerificationTarget
|
||||||
{
|
{
|
||||||
enum class Type { ConstantCondition, Underflow, Overflow, UnderOverflow, DivByZero, Balance, Assert } type;
|
enum class Type { ConstantCondition, Underflow, Overflow, UnderOverflow, DivByZero, Balance, Assert, PopEmptyArray } type;
|
||||||
smt::Expression value;
|
smt::Expression value;
|
||||||
smt::Expression constraints;
|
smt::Expression constraints;
|
||||||
};
|
};
|
||||||
|
@ -153,7 +153,15 @@ string SMTLib2Interface::toSExpr(smt::Expression const& _expr)
|
|||||||
auto tupleSort = dynamic_pointer_cast<TupleSort>(_expr.arguments.at(0).sort);
|
auto tupleSort = dynamic_pointer_cast<TupleSort>(_expr.arguments.at(0).sort);
|
||||||
unsigned index = std::stoi(_expr.arguments.at(1).name);
|
unsigned index = std::stoi(_expr.arguments.at(1).name);
|
||||||
solAssert(index < tupleSort->members.size(), "");
|
solAssert(index < tupleSort->members.size(), "");
|
||||||
sexpr += tupleSort->members.at(index) + " " + toSExpr(_expr.arguments.at(0));
|
sexpr += "|" + tupleSort->members.at(index) + "| " + toSExpr(_expr.arguments.at(0));
|
||||||
|
}
|
||||||
|
else if (_expr.name == "tuple_constructor")
|
||||||
|
{
|
||||||
|
auto tupleSort = dynamic_pointer_cast<TupleSort>(_expr.sort);
|
||||||
|
solAssert(tupleSort, "");
|
||||||
|
sexpr += "|" + tupleSort->name + "|";
|
||||||
|
for (auto const& arg: _expr.arguments)
|
||||||
|
sexpr += " " + toSExpr(arg);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -182,18 +190,19 @@ string SMTLib2Interface::toSmtLibSort(Sort const& _sort)
|
|||||||
case Kind::Tuple:
|
case Kind::Tuple:
|
||||||
{
|
{
|
||||||
auto const& tupleSort = dynamic_cast<TupleSort const&>(_sort);
|
auto const& tupleSort = dynamic_cast<TupleSort const&>(_sort);
|
||||||
if (!m_userSorts.count(tupleSort.name))
|
string tupleName = "|" + tupleSort.name + "|";
|
||||||
|
if (!m_userSorts.count(tupleName))
|
||||||
{
|
{
|
||||||
m_userSorts.insert(tupleSort.name);
|
m_userSorts.insert(tupleName);
|
||||||
string decl("(declare-datatypes ((" + tupleSort.name + " 0)) (((" + tupleSort.name);
|
string decl("(declare-datatypes ((" + tupleName + " 0)) (((" + tupleName);
|
||||||
solAssert(tupleSort.members.size() == tupleSort.components.size(), "");
|
solAssert(tupleSort.members.size() == tupleSort.components.size(), "");
|
||||||
for (unsigned i = 0; i < tupleSort.members.size(); ++i)
|
for (unsigned i = 0; i < tupleSort.members.size(); ++i)
|
||||||
decl += " (" + tupleSort.members.at(i) + " " + toSmtLibSort(*tupleSort.components.at(i)) + ")";
|
decl += " (|" + tupleSort.members.at(i) + "| " + toSmtLibSort(*tupleSort.components.at(i)) + ")";
|
||||||
decl += "))))";
|
decl += "))))";
|
||||||
write(decl);
|
write(decl);
|
||||||
}
|
}
|
||||||
|
|
||||||
return tupleSort.name;
|
return tupleName;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
solAssert(false, "Invalid SMT sort");
|
solAssert(false, "Invalid SMT sort");
|
||||||
|
@ -29,5 +29,12 @@ SSAVariable::SSAVariable()
|
|||||||
void SSAVariable::resetIndex()
|
void SSAVariable::resetIndex()
|
||||||
{
|
{
|
||||||
m_currentIndex = 0;
|
m_currentIndex = 0;
|
||||||
m_nextFreeIndex = make_unique<unsigned>(1);
|
m_nextFreeIndex = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SSAVariable::setIndex(unsigned _index)
|
||||||
|
{
|
||||||
|
m_currentIndex = _index;
|
||||||
|
if (m_nextFreeIndex <= _index)
|
||||||
|
m_nextFreeIndex = _index + 1;
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,10 @@ class SSAVariable
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
SSAVariable();
|
SSAVariable();
|
||||||
|
/// Resets index to 0 and next index to 1.
|
||||||
void resetIndex();
|
void resetIndex();
|
||||||
|
/// Sets index to _index and only adjusts next if next <= _index.
|
||||||
|
void setIndex(unsigned _index);
|
||||||
|
|
||||||
/// This function returns the current index of this SSA variable.
|
/// This function returns the current index of this SSA variable.
|
||||||
unsigned index() const { return m_currentIndex; }
|
unsigned index() const { return m_currentIndex; }
|
||||||
@ -37,12 +40,12 @@ public:
|
|||||||
|
|
||||||
unsigned operator++()
|
unsigned operator++()
|
||||||
{
|
{
|
||||||
return m_currentIndex = (*m_nextFreeIndex)++;
|
return m_currentIndex = m_nextFreeIndex++;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
unsigned m_currentIndex;
|
unsigned m_currentIndex;
|
||||||
std::unique_ptr<unsigned> m_nextFreeIndex;
|
unsigned m_nextFreeIndex;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
#include <boost/noncopyable.hpp>
|
#include <boost/noncopyable.hpp>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@ -63,7 +64,8 @@ class Expression
|
|||||||
friend class SolverInterface;
|
friend class SolverInterface;
|
||||||
public:
|
public:
|
||||||
explicit Expression(bool _v): Expression(_v ? "true" : "false", Kind::Bool) {}
|
explicit Expression(bool _v): Expression(_v ? "true" : "false", Kind::Bool) {}
|
||||||
explicit Expression(frontend::TypePointer _type): Expression(_type->toString(), {}, std::make_shared<SortSort>(smtSort(*_type))) {}
|
explicit Expression(frontend::TypePointer _type): Expression(_type->toString(true), {}, std::make_shared<SortSort>(smtSort(*_type))) {}
|
||||||
|
explicit Expression(std::shared_ptr<SortSort> _sort): Expression("", {}, _sort) {}
|
||||||
Expression(size_t _number): Expression(std::to_string(_number), Kind::Int) {}
|
Expression(size_t _number): Expression(std::to_string(_number), Kind::Int) {}
|
||||||
Expression(u256 const& _number): Expression(_number.str(), Kind::Int) {}
|
Expression(u256 const& _number): Expression(_number.str(), Kind::Int) {}
|
||||||
Expression(s256 const& _number): Expression(_number.str(), Kind::Int) {}
|
Expression(s256 const& _number): Expression(_number.str(), Kind::Int) {}
|
||||||
@ -76,6 +78,13 @@ public:
|
|||||||
|
|
||||||
bool hasCorrectArity() const
|
bool hasCorrectArity() const
|
||||||
{
|
{
|
||||||
|
if (name == "tuple_constructor")
|
||||||
|
{
|
||||||
|
auto tupleSort = std::dynamic_pointer_cast<TupleSort>(sort);
|
||||||
|
solAssert(tupleSort, "");
|
||||||
|
return arguments.size() == tupleSort->components.size();
|
||||||
|
}
|
||||||
|
|
||||||
static std::map<std::string, unsigned> const operatorsArity{
|
static std::map<std::string, unsigned> const operatorsArity{
|
||||||
{"ite", 3},
|
{"ite", 3},
|
||||||
{"not", 1},
|
{"not", 1},
|
||||||
@ -138,8 +147,7 @@ public:
|
|||||||
/// The function is pure and returns the modified array.
|
/// The function is pure and returns the modified array.
|
||||||
static Expression store(Expression _array, Expression _index, Expression _element)
|
static Expression store(Expression _array, Expression _index, Expression _element)
|
||||||
{
|
{
|
||||||
solAssert(_array.sort->kind == Kind::Array, "");
|
auto arraySort = std::dynamic_pointer_cast<ArraySort>(_array.sort);
|
||||||
std::shared_ptr<ArraySort> arraySort = std::dynamic_pointer_cast<ArraySort>(_array.sort);
|
|
||||||
solAssert(arraySort, "");
|
solAssert(arraySort, "");
|
||||||
solAssert(_index.sort, "");
|
solAssert(_index.sort, "");
|
||||||
solAssert(_element.sort, "");
|
solAssert(_element.sort, "");
|
||||||
@ -180,6 +188,20 @@ public:
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Expression tuple_constructor(Expression _tuple, std::vector<Expression> _arguments)
|
||||||
|
{
|
||||||
|
solAssert(_tuple.sort->kind == Kind::Sort, "");
|
||||||
|
auto sortSort = std::dynamic_pointer_cast<SortSort>(_tuple.sort);
|
||||||
|
auto tupleSort = std::dynamic_pointer_cast<TupleSort>(sortSort->inner);
|
||||||
|
solAssert(tupleSort, "");
|
||||||
|
solAssert(_arguments.size() == tupleSort->components.size(), "");
|
||||||
|
return Expression(
|
||||||
|
"tuple_constructor",
|
||||||
|
std::move(_arguments),
|
||||||
|
tupleSort
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
friend Expression operator!(Expression _a)
|
friend Expression operator!(Expression _a)
|
||||||
{
|
{
|
||||||
return Expression("not", std::move(_a), Kind::Bool);
|
return Expression("not", std::move(_a), Kind::Bool);
|
||||||
|
@ -47,7 +47,7 @@ Expression SymbolicState::balance()
|
|||||||
|
|
||||||
Expression SymbolicState::balance(Expression _address)
|
Expression SymbolicState::balance(Expression _address)
|
||||||
{
|
{
|
||||||
return Expression::select(m_balances.currentValue(), move(_address));
|
return Expression::select(m_balances.elements(), move(_address));
|
||||||
}
|
}
|
||||||
|
|
||||||
void SymbolicState::transfer(Expression _from, Expression _to, Expression _value)
|
void SymbolicState::transfer(Expression _from, Expression _to, Expression _value)
|
||||||
@ -72,11 +72,13 @@ void SymbolicState::transfer(Expression _from, Expression _to, Expression _value
|
|||||||
void SymbolicState::addBalance(Expression _address, Expression _value)
|
void SymbolicState::addBalance(Expression _address, Expression _value)
|
||||||
{
|
{
|
||||||
auto newBalances = Expression::store(
|
auto newBalances = Expression::store(
|
||||||
m_balances.currentValue(),
|
m_balances.elements(),
|
||||||
_address,
|
_address,
|
||||||
balance(_address) + move(_value)
|
balance(_address) + move(_value)
|
||||||
);
|
);
|
||||||
|
auto oldLength = m_balances.length();
|
||||||
m_balances.increaseIndex();
|
m_balances.increaseIndex();
|
||||||
m_context.addAssertion(newBalances == m_balances.currentValue());
|
m_context.addAssertion(m_balances.elements() == newBalances);
|
||||||
|
m_context.addAssertion(m_balances.length() == oldLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
#include <libsolidity/ast/Types.h>
|
#include <libsolidity/ast/Types.h>
|
||||||
#include <libsolutil/CommonData.h>
|
#include <libsolutil/CommonData.h>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
@ -55,17 +56,18 @@ SortPointer smtSort(frontend::Type const& _type)
|
|||||||
}
|
}
|
||||||
case Kind::Array:
|
case Kind::Array:
|
||||||
{
|
{
|
||||||
|
shared_ptr<ArraySort> array;
|
||||||
if (isMapping(_type.category()))
|
if (isMapping(_type.category()))
|
||||||
{
|
{
|
||||||
auto mapType = dynamic_cast<frontend::MappingType const*>(&_type);
|
auto mapType = dynamic_cast<frontend::MappingType const*>(&_type);
|
||||||
solAssert(mapType, "");
|
solAssert(mapType, "");
|
||||||
return make_shared<ArraySort>(smtSortAbstractFunction(*mapType->keyType()), smtSortAbstractFunction(*mapType->valueType()));
|
array = make_shared<ArraySort>(smtSortAbstractFunction(*mapType->keyType()), smtSortAbstractFunction(*mapType->valueType()));
|
||||||
}
|
}
|
||||||
else if (isStringLiteral(_type.category()))
|
else if (isStringLiteral(_type.category()))
|
||||||
{
|
{
|
||||||
auto stringLitType = dynamic_cast<frontend::StringLiteralType const*>(&_type);
|
auto stringLitType = dynamic_cast<frontend::StringLiteralType const*>(&_type);
|
||||||
solAssert(stringLitType, "");
|
solAssert(stringLitType, "");
|
||||||
return make_shared<ArraySort>(SortProvider::intSort, SortProvider::intSort);
|
array = make_shared<ArraySort>(SortProvider::intSort, SortProvider::intSort);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -78,8 +80,25 @@ SortPointer smtSort(frontend::Type const& _type)
|
|||||||
solAssert(false, "");
|
solAssert(false, "");
|
||||||
|
|
||||||
solAssert(arrayType, "");
|
solAssert(arrayType, "");
|
||||||
return make_shared<ArraySort>(SortProvider::intSort, smtSortAbstractFunction(*arrayType->baseType()));
|
array = make_shared<ArraySort>(SortProvider::intSort, smtSortAbstractFunction(*arrayType->baseType()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string tupleName;
|
||||||
|
if (
|
||||||
|
auto arrayType = dynamic_cast<ArrayType const*>(&_type);
|
||||||
|
(arrayType && arrayType->isString()) ||
|
||||||
|
_type.category() == frontend::Type::Category::ArraySlice ||
|
||||||
|
_type.category() == frontend::Type::Category::StringLiteral
|
||||||
|
)
|
||||||
|
tupleName = "bytes_tuple";
|
||||||
|
else
|
||||||
|
tupleName = _type.toString(true) + "_tuple";
|
||||||
|
|
||||||
|
return make_shared<TupleSort>(
|
||||||
|
tupleName,
|
||||||
|
vector<string>{tupleName + "_accessor_array", tupleName + "_accessor_length"},
|
||||||
|
vector<SortPointer>{array, SortProvider::intSort}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
case Kind::Tuple:
|
case Kind::Tuple:
|
||||||
{
|
{
|
||||||
@ -219,9 +238,7 @@ pair<bool, shared_ptr<SymbolicVariable>> newSymbolicVariable(
|
|||||||
else
|
else
|
||||||
var = make_shared<SymbolicIntVariable>(type, type, _uniqueName, _context);
|
var = make_shared<SymbolicIntVariable>(type, type, _uniqueName, _context);
|
||||||
}
|
}
|
||||||
else if (isMapping(_type.category()))
|
else if (isMapping(_type.category()) || isArray(_type.category()))
|
||||||
var = make_shared<SymbolicMappingVariable>(type, _uniqueName, _context);
|
|
||||||
else if (isArray(_type.category()))
|
|
||||||
var = make_shared<SymbolicArrayVariable>(type, type, _uniqueName, _context);
|
var = make_shared<SymbolicArrayVariable>(type, type, _uniqueName, _context);
|
||||||
else if (isTuple(_type.category()))
|
else if (isTuple(_type.category()))
|
||||||
var = make_shared<SymbolicTupleVariable>(type, _uniqueName, _context);
|
var = make_shared<SymbolicTupleVariable>(type, _uniqueName, _context);
|
||||||
@ -349,11 +366,26 @@ Expression zeroValue(frontend::TypePointer const& _type)
|
|||||||
return Expression(false);
|
return Expression(false);
|
||||||
if (isArray(_type->category()) || isMapping(_type->category()))
|
if (isArray(_type->category()) || isMapping(_type->category()))
|
||||||
{
|
{
|
||||||
|
auto tupleSort = dynamic_pointer_cast<TupleSort>(smtSort(*_type));
|
||||||
|
solAssert(tupleSort, "");
|
||||||
|
auto sortSort = make_shared<SortSort>(tupleSort->components.front());
|
||||||
|
|
||||||
|
std::optional<Expression> zeroArray;
|
||||||
|
auto length = bigint(0);
|
||||||
if (auto arrayType = dynamic_cast<ArrayType const*>(_type))
|
if (auto arrayType = dynamic_cast<ArrayType const*>(_type))
|
||||||
return Expression::const_array(Expression(arrayType), zeroValue(arrayType->baseType()));
|
{
|
||||||
auto mappingType = dynamic_cast<MappingType const*>(_type);
|
zeroArray = Expression::const_array(Expression(sortSort), zeroValue(arrayType->baseType()));
|
||||||
solAssert(mappingType, "");
|
if (!arrayType->isDynamicallySized())
|
||||||
return Expression::const_array(Expression(mappingType), zeroValue(mappingType->valueType()));
|
length = bigint(arrayType->length());
|
||||||
|
}
|
||||||
|
else if (auto mappingType = dynamic_cast<MappingType const*>(_type))
|
||||||
|
zeroArray = Expression::const_array(Expression(sortSort), zeroValue(mappingType->valueType()));
|
||||||
|
else
|
||||||
|
solAssert(false, "");
|
||||||
|
|
||||||
|
solAssert(zeroArray, "");
|
||||||
|
return Expression::tuple_constructor(Expression(_type), vector<Expression>{*zeroArray, length});
|
||||||
|
|
||||||
}
|
}
|
||||||
solAssert(false, "");
|
solAssert(false, "");
|
||||||
}
|
}
|
||||||
|
@ -86,6 +86,12 @@ smt::Expression SymbolicVariable::resetIndex()
|
|||||||
return currentValue();
|
return currentValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
smt::Expression SymbolicVariable::setIndex(unsigned _index)
|
||||||
|
{
|
||||||
|
m_ssa->setIndex(_index);
|
||||||
|
return currentValue();
|
||||||
|
}
|
||||||
|
|
||||||
smt::Expression SymbolicVariable::increaseIndex()
|
smt::Expression SymbolicVariable::increaseIndex()
|
||||||
{
|
{
|
||||||
++(*m_ssa);
|
++(*m_ssa);
|
||||||
@ -179,6 +185,12 @@ smt::Expression SymbolicFunctionVariable::resetIndex()
|
|||||||
return m_abstract.resetIndex();
|
return m_abstract.resetIndex();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
smt::Expression SymbolicFunctionVariable::setIndex(unsigned _index)
|
||||||
|
{
|
||||||
|
SymbolicVariable::setIndex(_index);
|
||||||
|
return m_abstract.setIndex(_index);
|
||||||
|
}
|
||||||
|
|
||||||
smt::Expression SymbolicFunctionVariable::increaseIndex()
|
smt::Expression SymbolicFunctionVariable::increaseIndex()
|
||||||
{
|
{
|
||||||
++(*m_ssa);
|
++(*m_ssa);
|
||||||
@ -197,46 +209,6 @@ void SymbolicFunctionVariable::resetDeclaration()
|
|||||||
m_declaration = m_context.newVariable(currentName(), m_sort);
|
m_declaration = m_context.newVariable(currentName(), m_sort);
|
||||||
}
|
}
|
||||||
|
|
||||||
SymbolicMappingVariable::SymbolicMappingVariable(
|
|
||||||
frontend::TypePointer _type,
|
|
||||||
string _uniqueName,
|
|
||||||
EncodingContext& _context
|
|
||||||
):
|
|
||||||
SymbolicVariable(_type, _type, move(_uniqueName), _context)
|
|
||||||
{
|
|
||||||
solAssert(isMapping(m_type->category()), "");
|
|
||||||
}
|
|
||||||
|
|
||||||
SymbolicArrayVariable::SymbolicArrayVariable(
|
|
||||||
frontend::TypePointer _type,
|
|
||||||
frontend::TypePointer _originalType,
|
|
||||||
string _uniqueName,
|
|
||||||
EncodingContext& _context
|
|
||||||
):
|
|
||||||
SymbolicVariable(_type, _originalType, move(_uniqueName), _context)
|
|
||||||
{
|
|
||||||
solAssert(isArray(m_type->category()), "");
|
|
||||||
}
|
|
||||||
|
|
||||||
SymbolicArrayVariable::SymbolicArrayVariable(
|
|
||||||
SortPointer _sort,
|
|
||||||
string _uniqueName,
|
|
||||||
EncodingContext& _context
|
|
||||||
):
|
|
||||||
SymbolicVariable(move(_sort), move(_uniqueName), _context)
|
|
||||||
{
|
|
||||||
solAssert(m_sort->kind == Kind::Array, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
smt::Expression SymbolicArrayVariable::currentValue(frontend::TypePointer const& _targetType) const
|
|
||||||
{
|
|
||||||
optional<smt::Expression> conversion = symbolicTypeConversion(m_originalType, _targetType);
|
|
||||||
if (conversion)
|
|
||||||
return *conversion;
|
|
||||||
|
|
||||||
return SymbolicVariable::currentValue(_targetType);
|
|
||||||
}
|
|
||||||
|
|
||||||
SymbolicEnumVariable::SymbolicEnumVariable(
|
SymbolicEnumVariable::SymbolicEnumVariable(
|
||||||
frontend::TypePointer _type,
|
frontend::TypePointer _type,
|
||||||
string _uniqueName,
|
string _uniqueName,
|
||||||
@ -286,3 +258,62 @@ smt::Expression SymbolicTupleVariable::component(
|
|||||||
|
|
||||||
return smt::Expression::tuple_get(currentValue(), _index);
|
return smt::Expression::tuple_get(currentValue(), _index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SymbolicArrayVariable::SymbolicArrayVariable(
|
||||||
|
frontend::TypePointer _type,
|
||||||
|
frontend::TypePointer _originalType,
|
||||||
|
string _uniqueName,
|
||||||
|
EncodingContext& _context
|
||||||
|
):
|
||||||
|
SymbolicVariable(_type, _originalType, move(_uniqueName), _context),
|
||||||
|
m_pair(
|
||||||
|
smtSort(*_type),
|
||||||
|
m_uniqueName + "_length_pair",
|
||||||
|
m_context
|
||||||
|
)
|
||||||
|
{
|
||||||
|
solAssert(isArray(m_type->category()) || isMapping(m_type->category()), "");
|
||||||
|
}
|
||||||
|
|
||||||
|
SymbolicArrayVariable::SymbolicArrayVariable(
|
||||||
|
SortPointer _sort,
|
||||||
|
string _uniqueName,
|
||||||
|
EncodingContext& _context
|
||||||
|
):
|
||||||
|
SymbolicVariable(move(_sort), move(_uniqueName), _context),
|
||||||
|
m_pair(
|
||||||
|
std::make_shared<TupleSort>(
|
||||||
|
"array_length_pair",
|
||||||
|
std::vector<std::string>{"array", "length"},
|
||||||
|
std::vector<SortPointer>{m_sort, SortProvider::intSort}
|
||||||
|
),
|
||||||
|
m_uniqueName + "_array_length_pair",
|
||||||
|
m_context
|
||||||
|
)
|
||||||
|
{
|
||||||
|
solAssert(m_sort->kind == Kind::Array, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
smt::Expression SymbolicArrayVariable::currentValue(frontend::TypePointer const& _targetType) const
|
||||||
|
{
|
||||||
|
optional<smt::Expression> conversion = symbolicTypeConversion(m_originalType, _targetType);
|
||||||
|
if (conversion)
|
||||||
|
return *conversion;
|
||||||
|
|
||||||
|
return m_pair.currentValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
smt::Expression SymbolicArrayVariable::valueAtIndex(int _index) const
|
||||||
|
{
|
||||||
|
return m_pair.valueAtIndex(_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
smt::Expression SymbolicArrayVariable::elements()
|
||||||
|
{
|
||||||
|
return m_pair.component(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
smt::Expression SymbolicArrayVariable::length()
|
||||||
|
{
|
||||||
|
return m_pair.component(1);
|
||||||
|
}
|
||||||
|
@ -56,6 +56,7 @@ public:
|
|||||||
virtual Expression valueAtIndex(int _index) const;
|
virtual Expression valueAtIndex(int _index) const;
|
||||||
virtual std::string nameAtIndex(int _index) const;
|
virtual std::string nameAtIndex(int _index) const;
|
||||||
virtual Expression resetIndex();
|
virtual Expression resetIndex();
|
||||||
|
virtual Expression setIndex(unsigned _index);
|
||||||
virtual Expression increaseIndex();
|
virtual Expression increaseIndex();
|
||||||
virtual Expression operator()(std::vector<Expression> /*_arguments*/) const
|
virtual Expression operator()(std::vector<Expression> /*_arguments*/) const
|
||||||
{
|
{
|
||||||
@ -169,6 +170,7 @@ public:
|
|||||||
Expression functionValueAtIndex(int _index) const;
|
Expression functionValueAtIndex(int _index) const;
|
||||||
|
|
||||||
Expression resetIndex() override;
|
Expression resetIndex() override;
|
||||||
|
Expression setIndex(unsigned _index) override;
|
||||||
Expression increaseIndex() override;
|
Expression increaseIndex() override;
|
||||||
|
|
||||||
Expression operator()(std::vector<Expression> _arguments) const override;
|
Expression operator()(std::vector<Expression> _arguments) const override;
|
||||||
@ -189,42 +191,6 @@ private:
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Specialization of SymbolicVariable for Mapping
|
|
||||||
*/
|
|
||||||
class SymbolicMappingVariable: public SymbolicVariable
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
SymbolicMappingVariable(
|
|
||||||
frontend::TypePointer _type,
|
|
||||||
std::string _uniqueName,
|
|
||||||
EncodingContext& _context
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Specialization of SymbolicVariable for Array
|
|
||||||
*/
|
|
||||||
class SymbolicArrayVariable: public SymbolicVariable
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
SymbolicArrayVariable(
|
|
||||||
frontend::TypePointer _type,
|
|
||||||
frontend::TypePointer _originalTtype,
|
|
||||||
std::string _uniqueName,
|
|
||||||
EncodingContext& _context
|
|
||||||
);
|
|
||||||
SymbolicArrayVariable(
|
|
||||||
SortPointer _sort,
|
|
||||||
std::string _uniqueName,
|
|
||||||
EncodingContext& _context
|
|
||||||
);
|
|
||||||
|
|
||||||
SymbolicArrayVariable(SymbolicArrayVariable&&) = default;
|
|
||||||
|
|
||||||
Expression currentValue(frontend::TypePointer const& _targetType = TypePointer{}) const override;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specialization of SymbolicVariable for Enum
|
* Specialization of SymbolicVariable for Enum
|
||||||
*/
|
*/
|
||||||
@ -263,4 +229,38 @@ public:
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specialization of SymbolicVariable for Array
|
||||||
|
*/
|
||||||
|
class SymbolicArrayVariable: public SymbolicVariable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SymbolicArrayVariable(
|
||||||
|
frontend::TypePointer _type,
|
||||||
|
frontend::TypePointer _originalTtype,
|
||||||
|
std::string _uniqueName,
|
||||||
|
EncodingContext& _context
|
||||||
|
);
|
||||||
|
SymbolicArrayVariable(
|
||||||
|
SortPointer _sort,
|
||||||
|
std::string _uniqueName,
|
||||||
|
EncodingContext& _context
|
||||||
|
);
|
||||||
|
|
||||||
|
SymbolicArrayVariable(SymbolicArrayVariable&&) = default;
|
||||||
|
|
||||||
|
Expression currentValue(frontend::TypePointer const& _targetType = TypePointer{}) const override;
|
||||||
|
Expression valueAtIndex(int _index) const override;
|
||||||
|
Expression resetIndex() override { SymbolicVariable::resetIndex(); return m_pair.resetIndex(); }
|
||||||
|
Expression setIndex(unsigned _index) override { SymbolicVariable::setIndex(_index); return m_pair.setIndex(_index); }
|
||||||
|
Expression increaseIndex() override { SymbolicVariable::increaseIndex(); return m_pair.increaseIndex(); }
|
||||||
|
Expression elements();
|
||||||
|
Expression length();
|
||||||
|
|
||||||
|
SortPointer tupleSort() { return m_pair.sort(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
SymbolicTupleVariable m_pair;
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -198,6 +198,15 @@ z3::expr Z3Interface::toZ3Expr(Expression const& _expr)
|
|||||||
size_t index = std::stoi(_expr.arguments[1].name);
|
size_t index = std::stoi(_expr.arguments[1].name);
|
||||||
return z3::func_decl(m_context, Z3_get_tuple_sort_field_decl(m_context, z3Sort(*_expr.arguments[0].sort), index))(arguments[0]);
|
return z3::func_decl(m_context, Z3_get_tuple_sort_field_decl(m_context, z3Sort(*_expr.arguments[0].sort), index))(arguments[0]);
|
||||||
}
|
}
|
||||||
|
else if (n == "tuple_constructor")
|
||||||
|
{
|
||||||
|
auto constructor = z3::func_decl(m_context, Z3_get_tuple_sort_mk_decl(m_context, z3Sort(*_expr.sort)));
|
||||||
|
solAssert(constructor.arity() == arguments.size(), "");
|
||||||
|
z3::expr_vector args(m_context);
|
||||||
|
for (auto const& arg: arguments)
|
||||||
|
args.push_back(arg);
|
||||||
|
return constructor(args);
|
||||||
|
}
|
||||||
|
|
||||||
solAssert(false, "");
|
solAssert(false, "");
|
||||||
}
|
}
|
||||||
|
@ -733,11 +733,7 @@ Json::Value const& CompilerStack::contractABI(Contract const& _contract) const
|
|||||||
|
|
||||||
solAssert(_contract.contract, "");
|
solAssert(_contract.contract, "");
|
||||||
|
|
||||||
// caches the result
|
return _contract.abi.init([&]{ return ABI::generate(*_contract.contract); });
|
||||||
if (!_contract.abi)
|
|
||||||
_contract.abi = make_unique<Json::Value>(ABI::generate(*_contract.contract));
|
|
||||||
|
|
||||||
return *_contract.abi;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Json::Value const& CompilerStack::storageLayout(string const& _contractName) const
|
Json::Value const& CompilerStack::storageLayout(string const& _contractName) const
|
||||||
@ -755,11 +751,7 @@ Json::Value const& CompilerStack::storageLayout(Contract const& _contract) const
|
|||||||
|
|
||||||
solAssert(_contract.contract, "");
|
solAssert(_contract.contract, "");
|
||||||
|
|
||||||
// caches the result
|
return _contract.storageLayout.init([&]{ return StorageLayout().generate(*_contract.contract); });
|
||||||
if (!_contract.storageLayout)
|
|
||||||
_contract.storageLayout = make_unique<Json::Value>(StorageLayout().generate(*_contract.contract));
|
|
||||||
|
|
||||||
return *_contract.storageLayout;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Json::Value const& CompilerStack::natspecUser(string const& _contractName) const
|
Json::Value const& CompilerStack::natspecUser(string const& _contractName) const
|
||||||
@ -777,11 +769,7 @@ Json::Value const& CompilerStack::natspecUser(Contract const& _contract) const
|
|||||||
|
|
||||||
solAssert(_contract.contract, "");
|
solAssert(_contract.contract, "");
|
||||||
|
|
||||||
// caches the result
|
return _contract.userDocumentation.init([&]{ return Natspec::userDocumentation(*_contract.contract); });
|
||||||
if (!_contract.userDocumentation)
|
|
||||||
_contract.userDocumentation = make_unique<Json::Value>(Natspec::userDocumentation(*_contract.contract));
|
|
||||||
|
|
||||||
return *_contract.userDocumentation;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Json::Value const& CompilerStack::natspecDev(string const& _contractName) const
|
Json::Value const& CompilerStack::natspecDev(string const& _contractName) const
|
||||||
@ -799,11 +787,7 @@ Json::Value const& CompilerStack::natspecDev(Contract const& _contract) const
|
|||||||
|
|
||||||
solAssert(_contract.contract, "");
|
solAssert(_contract.contract, "");
|
||||||
|
|
||||||
// caches the result
|
return _contract.devDocumentation.init([&]{ return Natspec::devDocumentation(*_contract.contract); });
|
||||||
if (!_contract.devDocumentation)
|
|
||||||
_contract.devDocumentation = make_unique<Json::Value>(Natspec::devDocumentation(*_contract.contract));
|
|
||||||
|
|
||||||
return *_contract.devDocumentation;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Json::Value CompilerStack::methodIdentifiers(string const& _contractName) const
|
Json::Value CompilerStack::methodIdentifiers(string const& _contractName) const
|
||||||
@ -832,11 +816,7 @@ string const& CompilerStack::metadata(Contract const& _contract) const
|
|||||||
|
|
||||||
solAssert(_contract.contract, "");
|
solAssert(_contract.contract, "");
|
||||||
|
|
||||||
// cache the result
|
return _contract.metadata.init([&]{ return createMetadata(_contract); });
|
||||||
if (!_contract.metadata)
|
|
||||||
_contract.metadata = make_unique<string>(createMetadata(_contract));
|
|
||||||
|
|
||||||
return *_contract.metadata;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Scanner const& CompilerStack::scanner(string const& _sourceName) const
|
Scanner const& CompilerStack::scanner(string const& _sourceName) const
|
||||||
|
@ -37,6 +37,7 @@
|
|||||||
|
|
||||||
#include <libsolutil/Common.h>
|
#include <libsolutil/Common.h>
|
||||||
#include <libsolutil/FixedHash.h>
|
#include <libsolutil/FixedHash.h>
|
||||||
|
#include <libsolutil/LazyInit.h>
|
||||||
|
|
||||||
#include <boost/noncopyable.hpp>
|
#include <boost/noncopyable.hpp>
|
||||||
#include <json/json.h>
|
#include <json/json.h>
|
||||||
@ -342,11 +343,11 @@ private:
|
|||||||
std::string yulIROptimized; ///< Optimized experimental Yul IR code.
|
std::string yulIROptimized; ///< Optimized experimental Yul IR code.
|
||||||
std::string ewasm; ///< Experimental Ewasm text representation
|
std::string ewasm; ///< Experimental Ewasm text representation
|
||||||
evmasm::LinkerObject ewasmObject; ///< Experimental Ewasm code
|
evmasm::LinkerObject ewasmObject; ///< Experimental Ewasm code
|
||||||
mutable std::unique_ptr<std::string const> metadata; ///< The metadata json that will be hashed into the chain.
|
util::LazyInit<std::string const> metadata; ///< The metadata json that will be hashed into the chain.
|
||||||
mutable std::unique_ptr<Json::Value const> abi;
|
util::LazyInit<Json::Value const> abi;
|
||||||
mutable std::unique_ptr<Json::Value const> storageLayout;
|
util::LazyInit<Json::Value const> storageLayout;
|
||||||
mutable std::unique_ptr<Json::Value const> userDocumentation;
|
util::LazyInit<Json::Value const> userDocumentation;
|
||||||
mutable std::unique_ptr<Json::Value const> devDocumentation;
|
util::LazyInit<Json::Value const> devDocumentation;
|
||||||
mutable std::unique_ptr<std::string const> sourceMapping;
|
mutable std::unique_ptr<std::string const> sourceMapping;
|
||||||
mutable std::unique_ptr<std::string const> runtimeSourceMapping;
|
mutable std::unique_ptr<std::string const> runtimeSourceMapping;
|
||||||
};
|
};
|
||||||
|
@ -30,6 +30,7 @@
|
|||||||
#include <libevmasm/Instruction.h>
|
#include <libevmasm/Instruction.h>
|
||||||
#include <libsolutil/JSON.h>
|
#include <libsolutil/JSON.h>
|
||||||
#include <libsolutil/Keccak256.h>
|
#include <libsolutil/Keccak256.h>
|
||||||
|
#include <libsolutil/CommonData.h>
|
||||||
|
|
||||||
#include <boost/algorithm/string/predicate.hpp>
|
#include <boost/algorithm/string/predicate.hpp>
|
||||||
|
|
||||||
@ -1127,16 +1128,29 @@ Json::Value StandardCompiler::compileYul(InputsAndSettings _inputsAndSettings)
|
|||||||
|
|
||||||
stack.optimize();
|
stack.optimize();
|
||||||
|
|
||||||
MachineAssemblyObject object = stack.assemble(AssemblyStack::Machine::EVM);
|
MachineAssemblyObject object;
|
||||||
|
MachineAssemblyObject runtimeObject;
|
||||||
|
tie(object, runtimeObject) = stack.assembleAndGuessRuntime();
|
||||||
|
|
||||||
if (isArtifactRequested(
|
for (string const& objectKind: vector<string>{"bytecode", "deployedBytecode"})
|
||||||
_inputsAndSettings.outputSelection,
|
{
|
||||||
sourceName,
|
auto artifacts = util::applyMap(
|
||||||
contractName,
|
vector<string>{"", ".object", ".opcodes", ".sourceMap", ".linkReferences"},
|
||||||
{ "evm.bytecode", "evm.bytecode.object", "evm.bytecode.opcodes", "evm.bytecode.sourceMap", "evm.bytecode.linkReferences" },
|
[&](auto const& _s) { return "evm." + objectKind + _s; }
|
||||||
wildcardMatchesExperimental
|
);
|
||||||
))
|
if (isArtifactRequested(
|
||||||
output["contracts"][sourceName][contractName]["evm"]["bytecode"] = collectEVMObject(*object.bytecode, object.sourceMappings.get(), false);
|
_inputsAndSettings.outputSelection,
|
||||||
|
sourceName,
|
||||||
|
contractName,
|
||||||
|
artifacts,
|
||||||
|
wildcardMatchesExperimental
|
||||||
|
))
|
||||||
|
{
|
||||||
|
MachineAssemblyObject const& o = objectKind == "bytecode" ? object : runtimeObject;
|
||||||
|
if (o.bytecode)
|
||||||
|
output["contracts"][sourceName][contractName]["evm"][objectKind] = collectEVMObject(*o.bytecode, o.sourceMappings.get(), false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (isArtifactRequested(_inputsAndSettings.outputSelection, sourceName, contractName, "irOptimized", wildcardMatchesExperimental))
|
if (isArtifactRequested(_inputsAndSettings.outputSelection, sourceName, contractName, "irOptimized", wildcardMatchesExperimental))
|
||||||
output["contracts"][sourceName][contractName]["irOptimized"] = stack.print();
|
output["contracts"][sourceName][contractName]["irOptimized"] = stack.print();
|
||||||
|
@ -19,6 +19,7 @@ set(sources
|
|||||||
JSON.h
|
JSON.h
|
||||||
Keccak256.cpp
|
Keccak256.cpp
|
||||||
Keccak256.h
|
Keccak256.h
|
||||||
|
LazyInit.h
|
||||||
picosha2.h
|
picosha2.h
|
||||||
Result.h
|
Result.h
|
||||||
StringUtils.cpp
|
StringUtils.cpp
|
||||||
|
94
libsolutil/LazyInit.h
Normal file
94
libsolutil/LazyInit.h
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
/*
|
||||||
|
This file is part of solidity.
|
||||||
|
|
||||||
|
solidity is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
solidity is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <libsolutil/Assertions.h>
|
||||||
|
#include <libsolutil/Exceptions.h>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace solidity::util
|
||||||
|
{
|
||||||
|
|
||||||
|
DEV_SIMPLE_EXCEPTION(BadLazyInitAccess);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A value that is initialized at some point after construction of the LazyInit. The stored value can only be accessed
|
||||||
|
* while calling "init", which initializes the stored value (if it has not already been initialized).
|
||||||
|
*
|
||||||
|
* @tparam T the type of the stored value; may not be a function, reference, array, or void type; may be const-qualified.
|
||||||
|
*/
|
||||||
|
template<typename T>
|
||||||
|
class LazyInit
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using value_type = T;
|
||||||
|
|
||||||
|
static_assert(std::is_object_v<value_type>, "Function, reference, and void types are not supported");
|
||||||
|
static_assert(!std::is_array_v<value_type>, "Array types are not supported.");
|
||||||
|
static_assert(!std::is_volatile_v<value_type>, "Volatile-qualified types are not supported.");
|
||||||
|
|
||||||
|
LazyInit() = default;
|
||||||
|
|
||||||
|
LazyInit(LazyInit const&) = delete;
|
||||||
|
LazyInit& operator=(LazyInit const&) = delete;
|
||||||
|
|
||||||
|
// Move constructor must be overridden to ensure that moved-from object is left empty.
|
||||||
|
constexpr LazyInit(LazyInit&& _other) noexcept:
|
||||||
|
m_value(std::move(_other.m_value))
|
||||||
|
{
|
||||||
|
_other.m_value.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
LazyInit& operator=(LazyInit&& _other) noexcept
|
||||||
|
{
|
||||||
|
this->m_value.swap(_other.m_value);
|
||||||
|
_other.m_value.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename F>
|
||||||
|
value_type& init(F&& _fun)
|
||||||
|
{
|
||||||
|
doInit(std::forward<F>(_fun));
|
||||||
|
return m_value.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename F>
|
||||||
|
value_type const& init(F&& _fun) const
|
||||||
|
{
|
||||||
|
doInit(std::forward<F>(_fun));
|
||||||
|
return m_value.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// Although not quite logically const, this is marked const for pragmatic reasons. It doesn't change the platonic
|
||||||
|
/// value of the object (which is something that is initialized to some computed value on first use).
|
||||||
|
template<typename F>
|
||||||
|
void doInit(F&& _fun) const
|
||||||
|
{
|
||||||
|
if (!m_value.has_value())
|
||||||
|
m_value.emplace(std::forward<F>(_fun)());
|
||||||
|
}
|
||||||
|
|
||||||
|
mutable std::optional<value_type> m_value;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -45,22 +45,20 @@ using namespace solidity::langutil;
|
|||||||
|
|
||||||
bool AsmAnalyzer::analyze(Block const& _block)
|
bool AsmAnalyzer::analyze(Block const& _block)
|
||||||
{
|
{
|
||||||
m_success = true;
|
auto watcher = m_errorReporter.errorWatcher();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (!(ScopeFiller(m_info, m_errorReporter))(_block))
|
if (!(ScopeFiller(m_info, m_errorReporter))(_block))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
(*this)(_block);
|
(*this)(_block);
|
||||||
if (!m_success)
|
|
||||||
yulAssert(m_errorReporter.hasErrors(), "No success but no error.");
|
|
||||||
}
|
}
|
||||||
catch (FatalError const&)
|
catch (FatalError const&)
|
||||||
{
|
{
|
||||||
// This FatalError con occur if the errorReporter has too many errors.
|
// This FatalError con occur if the errorReporter has too many errors.
|
||||||
yulAssert(!m_errorReporter.errors().empty(), "Fatal error detected, but no error is reported.");
|
yulAssert(!watcher.ok(), "Fatal error detected, but no error is reported.");
|
||||||
}
|
}
|
||||||
return m_success && !m_errorReporter.hasErrors();
|
return watcher.ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
AsmAnalysisInfo AsmAnalyzer::analyzeStrictAssertCorrect(Dialect const& _dialect, Object const& _object)
|
AsmAnalysisInfo AsmAnalyzer::analyzeStrictAssertCorrect(Dialect const& _dialect, Object const& _object)
|
||||||
@ -105,7 +103,7 @@ vector<YulString> AsmAnalyzer::operator()(Literal const& _literal)
|
|||||||
vector<YulString> AsmAnalyzer::operator()(Identifier const& _identifier)
|
vector<YulString> AsmAnalyzer::operator()(Identifier const& _identifier)
|
||||||
{
|
{
|
||||||
yulAssert(!_identifier.name.empty(), "");
|
yulAssert(!_identifier.name.empty(), "");
|
||||||
size_t numErrorsBefore = m_errorReporter.errors().size();
|
auto watcher = m_errorReporter.errorWatcher();
|
||||||
YulString type = m_dialect.defaultType;
|
YulString type = m_dialect.defaultType;
|
||||||
|
|
||||||
if (m_currentScope->lookup(_identifier.name, GenericVisitor{
|
if (m_currentScope->lookup(_identifier.name, GenericVisitor{
|
||||||
@ -141,13 +139,9 @@ vector<YulString> AsmAnalyzer::operator()(Identifier const& _identifier)
|
|||||||
yulAssert(stackSize == 1, "Invalid stack size of external reference.");
|
yulAssert(stackSize == 1, "Invalid stack size of external reference.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!found)
|
if (!found && watcher.ok())
|
||||||
{
|
|
||||||
// Only add an error message if the callback did not do it.
|
// Only add an error message if the callback did not do it.
|
||||||
if (numErrorsBefore == m_errorReporter.errors().size())
|
declarationError(_identifier.location, "Identifier not found.");
|
||||||
declarationError(_identifier.location, "Identifier not found.");
|
|
||||||
m_success = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {type};
|
return {type};
|
||||||
@ -155,8 +149,9 @@ vector<YulString> AsmAnalyzer::operator()(Identifier const& _identifier)
|
|||||||
|
|
||||||
void AsmAnalyzer::operator()(ExpressionStatement const& _statement)
|
void AsmAnalyzer::operator()(ExpressionStatement const& _statement)
|
||||||
{
|
{
|
||||||
|
auto watcher = m_errorReporter.errorWatcher();
|
||||||
vector<YulString> types = std::visit(*this, _statement.expression);
|
vector<YulString> types = std::visit(*this, _statement.expression);
|
||||||
if (m_success && !types.empty())
|
if (watcher.ok() && !types.empty())
|
||||||
typeError(_statement.location,
|
typeError(_statement.location,
|
||||||
"Top-level expressions are not supposed to return values (this expression returns " +
|
"Top-level expressions are not supposed to return values (this expression returns " +
|
||||||
to_string(types.size()) +
|
to_string(types.size()) +
|
||||||
@ -253,6 +248,7 @@ void AsmAnalyzer::operator()(FunctionDefinition const& _funDef)
|
|||||||
vector<YulString> AsmAnalyzer::operator()(FunctionCall const& _funCall)
|
vector<YulString> AsmAnalyzer::operator()(FunctionCall const& _funCall)
|
||||||
{
|
{
|
||||||
yulAssert(!_funCall.functionName.name.empty(), "");
|
yulAssert(!_funCall.functionName.name.empty(), "");
|
||||||
|
auto watcher = m_errorReporter.errorWatcher();
|
||||||
vector<YulString> const* parameterTypes = nullptr;
|
vector<YulString> const* parameterTypes = nullptr;
|
||||||
vector<YulString> const* returnTypes = nullptr;
|
vector<YulString> const* returnTypes = nullptr;
|
||||||
vector<bool> const* needsLiteralArguments = nullptr;
|
vector<bool> const* needsLiteralArguments = nullptr;
|
||||||
@ -281,7 +277,7 @@ vector<YulString> AsmAnalyzer::operator()(FunctionCall const& _funCall)
|
|||||||
{
|
{
|
||||||
if (!warnOnInstructions(_funCall.functionName.name.str(), _funCall.functionName.location))
|
if (!warnOnInstructions(_funCall.functionName.name.str(), _funCall.functionName.location))
|
||||||
declarationError(_funCall.functionName.location, "Function not found.");
|
declarationError(_funCall.functionName.location, "Function not found.");
|
||||||
m_success = false;
|
yulAssert(!watcher.ok(), "Expected a reported error.");
|
||||||
}
|
}
|
||||||
if (parameterTypes && _funCall.arguments.size() != parameterTypes->size())
|
if (parameterTypes && _funCall.arguments.size() != parameterTypes->size())
|
||||||
typeError(
|
typeError(
|
||||||
@ -323,7 +319,7 @@ vector<YulString> AsmAnalyzer::operator()(FunctionCall const& _funCall)
|
|||||||
for (size_t i = 0; i < parameterTypes->size(); ++i)
|
for (size_t i = 0; i < parameterTypes->size(); ++i)
|
||||||
expectType((*parameterTypes)[i], argTypes[i], locationOf(_funCall.arguments[i]));
|
expectType((*parameterTypes)[i], argTypes[i], locationOf(_funCall.arguments[i]));
|
||||||
|
|
||||||
if (m_success)
|
if (watcher.ok())
|
||||||
{
|
{
|
||||||
yulAssert(parameterTypes && parameterTypes->size() == argTypes.size(), "");
|
yulAssert(parameterTypes && parameterTypes->size() == argTypes.size(), "");
|
||||||
yulAssert(returnTypes, "");
|
yulAssert(returnTypes, "");
|
||||||
@ -353,6 +349,8 @@ void AsmAnalyzer::operator()(Switch const& _switch)
|
|||||||
{
|
{
|
||||||
if (_case.value)
|
if (_case.value)
|
||||||
{
|
{
|
||||||
|
auto watcher = m_errorReporter.errorWatcher();
|
||||||
|
|
||||||
expectType(valueType, _case.value->type, _case.value->location);
|
expectType(valueType, _case.value->type, _case.value->location);
|
||||||
|
|
||||||
// We cannot use "expectExpression" here because *_case.value is not an
|
// We cannot use "expectExpression" here because *_case.value is not an
|
||||||
@ -360,7 +358,7 @@ void AsmAnalyzer::operator()(Switch const& _switch)
|
|||||||
(*this)(*_case.value);
|
(*this)(*_case.value);
|
||||||
|
|
||||||
/// Note: the parser ensures there is only one default case
|
/// Note: the parser ensures there is only one default case
|
||||||
if (m_success && !cases.insert(valueOfLiteral(*_case.value)).second)
|
if (watcher.ok() && !cases.insert(valueOfLiteral(*_case.value)).second)
|
||||||
declarationError(_case.location, "Duplicate case defined.");
|
declarationError(_case.location, "Duplicate case defined.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -432,7 +430,7 @@ void AsmAnalyzer::expectBoolExpression(Expression const& _expr)
|
|||||||
void AsmAnalyzer::checkAssignment(Identifier const& _variable, YulString _valueType)
|
void AsmAnalyzer::checkAssignment(Identifier const& _variable, YulString _valueType)
|
||||||
{
|
{
|
||||||
yulAssert(!_variable.name.empty(), "");
|
yulAssert(!_variable.name.empty(), "");
|
||||||
size_t numErrorsBefore = m_errorReporter.errors().size();
|
auto watcher = m_errorReporter.errorWatcher();
|
||||||
YulString const* variableType = nullptr;
|
YulString const* variableType = nullptr;
|
||||||
bool found = false;
|
bool found = false;
|
||||||
if (Scope::Identifier const* var = m_currentScope->lookup(_variable.name))
|
if (Scope::Identifier const* var = m_currentScope->lookup(_variable.name))
|
||||||
@ -461,13 +459,9 @@ void AsmAnalyzer::checkAssignment(Identifier const& _variable, YulString _valueT
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!found)
|
if (!found && watcher.ok())
|
||||||
{
|
|
||||||
m_success = false;
|
|
||||||
// Only add message if the callback did not.
|
// Only add message if the callback did not.
|
||||||
if (numErrorsBefore == m_errorReporter.errors().size())
|
declarationError(_variable.location, "Variable not found or variable not lvalue.");
|
||||||
declarationError(_variable.location, "Variable not found or variable not lvalue.");
|
|
||||||
}
|
|
||||||
if (variableType && *variableType != _valueType)
|
if (variableType && *variableType != _valueType)
|
||||||
typeError(_variable.location,
|
typeError(_variable.location,
|
||||||
"Assigning a value of type \"" +
|
"Assigning a value of type \"" +
|
||||||
@ -477,8 +471,7 @@ void AsmAnalyzer::checkAssignment(Identifier const& _variable, YulString _valueT
|
|||||||
"\"."
|
"\"."
|
||||||
);
|
);
|
||||||
|
|
||||||
if (m_success)
|
yulAssert(!watcher.ok() || variableType, "");
|
||||||
yulAssert(variableType, "");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Scope& AsmAnalyzer::scope(Block const* _block)
|
Scope& AsmAnalyzer::scope(Block const* _block)
|
||||||
@ -545,43 +538,28 @@ bool AsmAnalyzer::warnOnInstructions(evmasm::Instruction _instr, SourceLocation
|
|||||||
_instr == evmasm::Instruction::RETURNDATACOPY ||
|
_instr == evmasm::Instruction::RETURNDATACOPY ||
|
||||||
_instr == evmasm::Instruction::RETURNDATASIZE
|
_instr == evmasm::Instruction::RETURNDATASIZE
|
||||||
) && !m_evmVersion.supportsReturndata())
|
) && !m_evmVersion.supportsReturndata())
|
||||||
{
|
|
||||||
errorForVM("only available for Byzantium-compatible");
|
errorForVM("only available for Byzantium-compatible");
|
||||||
}
|
|
||||||
else if (_instr == evmasm::Instruction::STATICCALL && !m_evmVersion.hasStaticCall())
|
else if (_instr == evmasm::Instruction::STATICCALL && !m_evmVersion.hasStaticCall())
|
||||||
{
|
|
||||||
errorForVM("only available for Byzantium-compatible");
|
errorForVM("only available for Byzantium-compatible");
|
||||||
}
|
|
||||||
else if ((
|
else if ((
|
||||||
_instr == evmasm::Instruction::SHL ||
|
_instr == evmasm::Instruction::SHL ||
|
||||||
_instr == evmasm::Instruction::SHR ||
|
_instr == evmasm::Instruction::SHR ||
|
||||||
_instr == evmasm::Instruction::SAR
|
_instr == evmasm::Instruction::SAR
|
||||||
) && !m_evmVersion.hasBitwiseShifting())
|
) && !m_evmVersion.hasBitwiseShifting())
|
||||||
{
|
|
||||||
errorForVM("only available for Constantinople-compatible");
|
errorForVM("only available for Constantinople-compatible");
|
||||||
}
|
|
||||||
else if (_instr == evmasm::Instruction::CREATE2 && !m_evmVersion.hasCreate2())
|
else if (_instr == evmasm::Instruction::CREATE2 && !m_evmVersion.hasCreate2())
|
||||||
{
|
|
||||||
errorForVM("only available for Constantinople-compatible");
|
errorForVM("only available for Constantinople-compatible");
|
||||||
}
|
|
||||||
else if (_instr == evmasm::Instruction::EXTCODEHASH && !m_evmVersion.hasExtCodeHash())
|
else if (_instr == evmasm::Instruction::EXTCODEHASH && !m_evmVersion.hasExtCodeHash())
|
||||||
{
|
|
||||||
errorForVM("only available for Constantinople-compatible");
|
errorForVM("only available for Constantinople-compatible");
|
||||||
}
|
|
||||||
else if (_instr == evmasm::Instruction::CHAINID && !m_evmVersion.hasChainID())
|
else if (_instr == evmasm::Instruction::CHAINID && !m_evmVersion.hasChainID())
|
||||||
{
|
|
||||||
errorForVM("only available for Istanbul-compatible");
|
errorForVM("only available for Istanbul-compatible");
|
||||||
}
|
|
||||||
else if (_instr == evmasm::Instruction::SELFBALANCE && !m_evmVersion.hasSelfBalance())
|
else if (_instr == evmasm::Instruction::SELFBALANCE && !m_evmVersion.hasSelfBalance())
|
||||||
{
|
|
||||||
errorForVM("only available for Istanbul-compatible");
|
errorForVM("only available for Istanbul-compatible");
|
||||||
}
|
|
||||||
else if (
|
else if (
|
||||||
_instr == evmasm::Instruction::JUMP ||
|
_instr == evmasm::Instruction::JUMP ||
|
||||||
_instr == evmasm::Instruction::JUMPI ||
|
_instr == evmasm::Instruction::JUMPI ||
|
||||||
_instr == evmasm::Instruction::JUMPDEST
|
_instr == evmasm::Instruction::JUMPDEST
|
||||||
)
|
)
|
||||||
{
|
|
||||||
m_errorReporter.error(
|
m_errorReporter.error(
|
||||||
4316_error,
|
4316_error,
|
||||||
Error::Type::SyntaxError,
|
Error::Type::SyntaxError,
|
||||||
@ -590,8 +568,6 @@ bool AsmAnalyzer::warnOnInstructions(evmasm::Instruction _instr, SourceLocation
|
|||||||
"incorrect stack access. Because of that they are disallowed in strict assembly. "
|
"incorrect stack access. Because of that they are disallowed in strict assembly. "
|
||||||
"Use functions, \"switch\", \"if\" or \"for\" statements instead."
|
"Use functions, \"switch\", \"if\" or \"for\" statements instead."
|
||||||
);
|
);
|
||||||
m_success = false;
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -601,12 +577,9 @@ bool AsmAnalyzer::warnOnInstructions(evmasm::Instruction _instr, SourceLocation
|
|||||||
void AsmAnalyzer::typeError(SourceLocation const& _location, string const& _description)
|
void AsmAnalyzer::typeError(SourceLocation const& _location, string const& _description)
|
||||||
{
|
{
|
||||||
m_errorReporter.typeError(7569_error, _location, _description);
|
m_errorReporter.typeError(7569_error, _location, _description);
|
||||||
m_success = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsmAnalyzer::declarationError(SourceLocation const& _location, string const& _description)
|
void AsmAnalyzer::declarationError(SourceLocation const& _location, string const& _description)
|
||||||
{
|
{
|
||||||
m_errorReporter.declarationError(9595_error, _location, _description);
|
m_errorReporter.declarationError(9595_error, _location, _description);
|
||||||
m_success = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,8 +115,6 @@ private:
|
|||||||
void typeError(langutil::SourceLocation const& _location, std::string const& _description);
|
void typeError(langutil::SourceLocation const& _location, std::string const& _description);
|
||||||
void declarationError(langutil::SourceLocation const& _location, std::string const& _description);
|
void declarationError(langutil::SourceLocation const& _location, std::string const& _description);
|
||||||
|
|
||||||
/// Success-flag, can be set to false at any time.
|
|
||||||
bool m_success = true;
|
|
||||||
yul::ExternalIdentifierAccess::Resolver m_resolver;
|
yul::ExternalIdentifierAccess::Resolver m_resolver;
|
||||||
Scope* m_currentScope = nullptr;
|
Scope* m_currentScope = nullptr;
|
||||||
/// Variables that are active at the current point in assembly (as opposed to
|
/// Variables that are active at the current point in assembly (as opposed to
|
||||||
|
@ -198,22 +198,7 @@ MachineAssemblyObject AssemblyStack::assemble(Machine _machine) const
|
|||||||
switch (_machine)
|
switch (_machine)
|
||||||
{
|
{
|
||||||
case Machine::EVM:
|
case Machine::EVM:
|
||||||
{
|
return assembleAndGuessRuntime().first;
|
||||||
MachineAssemblyObject object;
|
|
||||||
evmasm::Assembly assembly;
|
|
||||||
EthAssemblyAdapter adapter(assembly);
|
|
||||||
compileEVM(adapter, false, m_optimiserSettings.optimizeStackAllocation);
|
|
||||||
object.bytecode = make_shared<evmasm::LinkerObject>(assembly.assemble());
|
|
||||||
yulAssert(object.bytecode->immutableReferences.empty(), "Leftover immutables.");
|
|
||||||
object.assembly = assembly.assemblyString();
|
|
||||||
object.sourceMappings = make_unique<string>(
|
|
||||||
evmasm::AssemblyItem::computeSourceMapping(
|
|
||||||
assembly.items(),
|
|
||||||
{{scanner().charStream() ? scanner().charStream()->name() : "", 0}}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
return object;
|
|
||||||
}
|
|
||||||
case Machine::EVM15:
|
case Machine::EVM15:
|
||||||
{
|
{
|
||||||
MachineAssemblyObject object;
|
MachineAssemblyObject object;
|
||||||
@ -240,6 +225,46 @@ MachineAssemblyObject AssemblyStack::assemble(Machine _machine) const
|
|||||||
return MachineAssemblyObject();
|
return MachineAssemblyObject();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pair<MachineAssemblyObject, MachineAssemblyObject> AssemblyStack::assembleAndGuessRuntime() const
|
||||||
|
{
|
||||||
|
yulAssert(m_analysisSuccessful, "");
|
||||||
|
yulAssert(m_parserResult, "");
|
||||||
|
yulAssert(m_parserResult->code, "");
|
||||||
|
yulAssert(m_parserResult->analysisInfo, "");
|
||||||
|
|
||||||
|
evmasm::Assembly assembly;
|
||||||
|
EthAssemblyAdapter adapter(assembly);
|
||||||
|
compileEVM(adapter, false, m_optimiserSettings.optimizeStackAllocation);
|
||||||
|
|
||||||
|
MachineAssemblyObject creationObject;
|
||||||
|
creationObject.bytecode = make_shared<evmasm::LinkerObject>(assembly.assemble());
|
||||||
|
yulAssert(creationObject.bytecode->immutableReferences.empty(), "Leftover immutables.");
|
||||||
|
creationObject.assembly = assembly.assemblyString();
|
||||||
|
creationObject.sourceMappings = make_unique<string>(
|
||||||
|
evmasm::AssemblyItem::computeSourceMapping(
|
||||||
|
assembly.items(),
|
||||||
|
{{scanner().charStream() ? scanner().charStream()->name() : "", 0}}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
MachineAssemblyObject runtimeObject;
|
||||||
|
// Heuristic: If there is a single sub-assembly, this is likely the runtime object.
|
||||||
|
if (assembly.numSubs() == 1)
|
||||||
|
{
|
||||||
|
evmasm::Assembly& runtimeAssembly = assembly.sub(0);
|
||||||
|
runtimeObject.bytecode = make_shared<evmasm::LinkerObject>(runtimeAssembly.assemble());
|
||||||
|
runtimeObject.assembly = runtimeAssembly.assemblyString();
|
||||||
|
runtimeObject.sourceMappings = make_unique<string>(
|
||||||
|
evmasm::AssemblyItem::computeSourceMapping(
|
||||||
|
runtimeAssembly.items(),
|
||||||
|
{{scanner().charStream() ? scanner().charStream()->name() : "", 0}}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return {std::move(creationObject), std::move(runtimeObject)};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
string AssemblyStack::print() const
|
string AssemblyStack::print() const
|
||||||
{
|
{
|
||||||
yulAssert(m_parserResult, "");
|
yulAssert(m_parserResult, "");
|
||||||
|
@ -88,6 +88,12 @@ public:
|
|||||||
/// Run the assembly step (should only be called after parseAndAnalyze).
|
/// Run the assembly step (should only be called after parseAndAnalyze).
|
||||||
MachineAssemblyObject assemble(Machine _machine) const;
|
MachineAssemblyObject assemble(Machine _machine) const;
|
||||||
|
|
||||||
|
/// Run the assembly step (should only be called after parseAndAnalyze).
|
||||||
|
/// In addition to the value returned by @a assemble, returns
|
||||||
|
/// a second object that is guessed to be the runtime code.
|
||||||
|
/// Only available for EVM.
|
||||||
|
std::pair<MachineAssemblyObject, MachineAssemblyObject> assembleAndGuessRuntime() const;
|
||||||
|
|
||||||
/// @returns the errors generated during parsing, analysis (and potentially assembly).
|
/// @returns the errors generated during parsing, analysis (and potentially assembly).
|
||||||
langutil::ErrorList const& errors() const { return m_errors; }
|
langutil::ErrorList const& errors() const { return m_errors; }
|
||||||
|
|
||||||
|
@ -34,7 +34,5 @@ else
|
|||||||
BUILD_DIR="$1"
|
BUILD_DIR="$1"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
docker run -v $(pwd):/root/project -w /root/project trzeci/emscripten:sdk-tag-1.39.3-64bit \
|
docker run -v $(pwd):/root/project -w /root/project ethereum/solidity-buildpack-deps:emsdk-1.39.15-1 \
|
||||||
./scripts/travis-emscripten/install_deps.sh
|
|
||||||
docker run -v $(pwd):/root/project -w /root/project trzeci/emscripten:sdk-tag-1.39.3-64bit \
|
|
||||||
./scripts/travis-emscripten/build_emscripten.sh $BUILD_DIR
|
./scripts/travis-emscripten/build_emscripten.sh $BUILD_DIR
|
||||||
|
@ -149,7 +149,7 @@ def main():
|
|||||||
print("No incorrect IDs found")
|
print("No incorrect IDs found")
|
||||||
else:
|
else:
|
||||||
fix_ids(used_ids, source_file_names)
|
fix_ids(used_ids, source_file_names)
|
||||||
print("Fixing compteted")
|
print("Fixing completed")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
@ -40,37 +40,8 @@ else
|
|||||||
BUILD_DIR="$1"
|
BUILD_DIR="$1"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! type git &>/dev/null; then
|
|
||||||
# We need git for extracting the commit hash
|
|
||||||
apt-get update
|
|
||||||
apt-get -y install git-core
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! type wget &>/dev/null; then
|
|
||||||
# We need wget to install cmake
|
|
||||||
apt-get update
|
|
||||||
apt-get -y install wget
|
|
||||||
fi
|
|
||||||
|
|
||||||
WORKSPACE=/root/project
|
WORKSPACE=/root/project
|
||||||
|
|
||||||
# Boost
|
|
||||||
echo -en 'travis_fold:start:compiling_boost\\r'
|
|
||||||
test -e "$WORKSPACE"/boost_1_70_0_install/include/boost/version.hpp || (
|
|
||||||
cd "$WORKSPACE"/boost_1_70_0
|
|
||||||
./b2 toolset=emscripten link=static variant=release threading=single runtime-link=static \
|
|
||||||
--with-system --with-filesystem --with-test --with-program_options cxxflags="-Wno-unused-local-typedef -Wno-variadic-macros -Wno-c99-extensions -Wno-all" \
|
|
||||||
--prefix="$WORKSPACE"/boost_1_70_0_install install
|
|
||||||
)
|
|
||||||
ln -sf "$WORKSPACE"/boost_1_70_0_install/lib/* /emsdk_portable/emscripten/sdk/system/lib
|
|
||||||
ln -sf "$WORKSPACE"/boost_1_70_0_install/include/* /emsdk_portable/emscripten/sdk/system/include
|
|
||||||
echo -en 'travis_fold:end:compiling_boost\\r'
|
|
||||||
|
|
||||||
echo -en 'travis_fold:start:install_cmake.sh\\r'
|
|
||||||
source $WORKSPACE/scripts/install_cmake.sh
|
|
||||||
echo -en 'travis_fold:end:install_cmake.sh\\r'
|
|
||||||
|
|
||||||
# Build dependent components and solidity itself
|
|
||||||
echo -en 'travis_fold:start:compiling_solidity\\r'
|
echo -en 'travis_fold:start:compiling_solidity\\r'
|
||||||
cd $WORKSPACE
|
cd $WORKSPACE
|
||||||
mkdir -p $BUILD_DIR
|
mkdir -p $BUILD_DIR
|
||||||
@ -82,7 +53,7 @@ cmake \
|
|||||||
-DBoost_USE_STATIC_RUNTIME=1 \
|
-DBoost_USE_STATIC_RUNTIME=1 \
|
||||||
-DTESTS=0 \
|
-DTESTS=0 \
|
||||||
..
|
..
|
||||||
make -j 4
|
make -j 4 soljson
|
||||||
|
|
||||||
cd ..
|
cd ..
|
||||||
mkdir -p upload
|
mkdir -p upload
|
||||||
|
@ -1,49 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
#------------------------------------------------------------------------------
|
|
||||||
# Bash script for installing pre-requisite packages for building solidity
|
|
||||||
# using Emscripten on Ubuntu Trusty.
|
|
||||||
#
|
|
||||||
# The documentation for solidity is hosted at:
|
|
||||||
#
|
|
||||||
# http://solidity.readthedocs.io/
|
|
||||||
#
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
# This file is part of solidity.
|
|
||||||
#
|
|
||||||
# solidity is free software: you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU General Public License as published by
|
|
||||||
# the Free Software Foundation, either version 3 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# solidity is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with solidity. If not, see <http://www.gnu.org/licenses/>
|
|
||||||
#
|
|
||||||
# (c) 2016 solidity contributors.
|
|
||||||
#------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
set -ev
|
|
||||||
|
|
||||||
SCRIPT_DIR="$(realpath $(dirname $0))"
|
|
||||||
|
|
||||||
echo -en 'travis_fold:start:installing_dependencies\\r'
|
|
||||||
test -e boost_1_70_0_install/include/boost/version.hpp || (
|
|
||||||
rm -rf boost_1_70_0
|
|
||||||
rm -f boost.tar.gz
|
|
||||||
wget -q 'https://sourceforge.net/projects/boost/files/boost/1.70.0/boost_1_70_0.tar.gz/download'\
|
|
||||||
-O boost.tar.gz
|
|
||||||
test "$(shasum boost.tar.gz)" = "7804c782deb00f36ac80b1000b71a3707eadb620 boost.tar.gz"
|
|
||||||
tar -xzf boost.tar.gz
|
|
||||||
rm boost.tar.gz
|
|
||||||
cd boost_1_70_0
|
|
||||||
./bootstrap.sh
|
|
||||||
cp "${SCRIPT_DIR}/emscripten.jam" .
|
|
||||||
echo "using emscripten : : em++ ;" >> project-config.jam
|
|
||||||
)
|
|
||||||
cd ..
|
|
||||||
echo -en 'travis_fold:end:installing_dependencies\\r'
|
|
@ -34,6 +34,7 @@ set(libsolutil_sources
|
|||||||
libsolutil/IterateReplacing.cpp
|
libsolutil/IterateReplacing.cpp
|
||||||
libsolutil/JSON.cpp
|
libsolutil/JSON.cpp
|
||||||
libsolutil/Keccak256.cpp
|
libsolutil/Keccak256.cpp
|
||||||
|
libsolutil/LazyInit.cpp
|
||||||
libsolutil/StringUtils.cpp
|
libsolutil/StringUtils.cpp
|
||||||
libsolutil/SwarmHash.cpp
|
libsolutil/SwarmHash.cpp
|
||||||
libsolutil/UTF8.cpp
|
libsolutil/UTF8.cpp
|
||||||
|
1
test/cmdlineTests/optimizer_BlockDeDuplicator/args
Normal file
1
test/cmdlineTests/optimizer_BlockDeDuplicator/args
Normal file
@ -0,0 +1 @@
|
|||||||
|
--optimize --asm --metadata-hash none
|
11
test/cmdlineTests/optimizer_BlockDeDuplicator/err
Normal file
11
test/cmdlineTests/optimizer_BlockDeDuplicator/err
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
Warning: Statement has no effect.
|
||||||
|
--> optimizer_BlockDeDuplicator/input.sol:7:27:
|
||||||
|
|
|
||||||
|
7 | function f() public { true ? 1 : 3;}
|
||||||
|
| ^^^^^^^^^^^^
|
||||||
|
|
||||||
|
Warning: Function state mutability can be restricted to pure
|
||||||
|
--> optimizer_BlockDeDuplicator/input.sol:7:5:
|
||||||
|
|
|
||||||
|
7 | function f() public { true ? 1 : 3;}
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
9
test/cmdlineTests/optimizer_BlockDeDuplicator/input.sol
Normal file
9
test/cmdlineTests/optimizer_BlockDeDuplicator/input.sol
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
|
pragma solidity >=0.0;
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
function fun_x() public {}
|
||||||
|
function fun_() public {}
|
||||||
|
function f() public { true ? 1 : 3;}
|
||||||
|
function() r = true ? fun_x : f;
|
||||||
|
}
|
107
test/cmdlineTests/optimizer_BlockDeDuplicator/output
Normal file
107
test/cmdlineTests/optimizer_BlockDeDuplicator/output
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
|
||||||
|
======= optimizer_BlockDeDuplicator/input.sol:C =======
|
||||||
|
EVM assembly:
|
||||||
|
/* "optimizer_BlockDeDuplicator/input.sol":60:213 contract C {... */
|
||||||
|
mstore(0x40, 0x80)
|
||||||
|
/* "optimizer_BlockDeDuplicator/input.sol":179:210 function() r = true ? fun_x : f */
|
||||||
|
0x00
|
||||||
|
dup1
|
||||||
|
sload
|
||||||
|
not(sub(shl(0x40, 0x01), 0x01))
|
||||||
|
and
|
||||||
|
/* "optimizer_BlockDeDuplicator/input.sol":201:206 fun_x */
|
||||||
|
or(tag_0_7, shl(0x20, tag_4))
|
||||||
|
sub(shl(0x40, 0x01), 0x01)
|
||||||
|
/* "optimizer_BlockDeDuplicator/input.sol":179:210 function() r = true ? fun_x : f */
|
||||||
|
and
|
||||||
|
or
|
||||||
|
swap1
|
||||||
|
sstore
|
||||||
|
/* "optimizer_BlockDeDuplicator/input.sol":60:213 contract C {... */
|
||||||
|
callvalue
|
||||||
|
/* "--CODEGEN--":2:4 */
|
||||||
|
dup1
|
||||||
|
iszero
|
||||||
|
tag_5
|
||||||
|
jumpi
|
||||||
|
/* "--CODEGEN--":27:28 */
|
||||||
|
0x00
|
||||||
|
/* "--CODEGEN--":24:25 */
|
||||||
|
dup1
|
||||||
|
/* "--CODEGEN--":17:29 */
|
||||||
|
revert
|
||||||
|
/* "--CODEGEN--":2:4 */
|
||||||
|
tag_5:
|
||||||
|
/* "optimizer_BlockDeDuplicator/input.sol":60:213 contract C {... */
|
||||||
|
pop
|
||||||
|
jump(tag_6)
|
||||||
|
/* "optimizer_BlockDeDuplicator/input.sol":77:103 function fun_x() public {} */
|
||||||
|
tag_4:
|
||||||
|
jump // out
|
||||||
|
/* "optimizer_BlockDeDuplicator/input.sol":60:213 contract C {... */
|
||||||
|
tag_6:
|
||||||
|
dataSize(sub_0)
|
||||||
|
dup1
|
||||||
|
dataOffset(sub_0)
|
||||||
|
0x00
|
||||||
|
codecopy
|
||||||
|
0x00
|
||||||
|
return
|
||||||
|
stop
|
||||||
|
|
||||||
|
sub_0: assembly {
|
||||||
|
/* "optimizer_BlockDeDuplicator/input.sol":60:213 contract C {... */
|
||||||
|
mstore(0x40, 0x80)
|
||||||
|
callvalue
|
||||||
|
/* "--CODEGEN--":5:14 */
|
||||||
|
dup1
|
||||||
|
/* "--CODEGEN--":2:4 */
|
||||||
|
iszero
|
||||||
|
tag_1
|
||||||
|
jumpi
|
||||||
|
/* "--CODEGEN--":27:28 */
|
||||||
|
0x00
|
||||||
|
/* "--CODEGEN--":24:25 */
|
||||||
|
dup1
|
||||||
|
/* "--CODEGEN--":17:29 */
|
||||||
|
revert
|
||||||
|
/* "--CODEGEN--":2:4 */
|
||||||
|
tag_1:
|
||||||
|
/* "optimizer_BlockDeDuplicator/input.sol":60:213 contract C {... */
|
||||||
|
pop
|
||||||
|
jumpi(tag_2, lt(calldatasize, 0x04))
|
||||||
|
shr(0xe0, calldataload(0x00))
|
||||||
|
dup1
|
||||||
|
0x26121ff0
|
||||||
|
eq
|
||||||
|
tag_3
|
||||||
|
jumpi
|
||||||
|
dup1
|
||||||
|
0x2e1fb2bc
|
||||||
|
eq
|
||||||
|
tag_3
|
||||||
|
jumpi
|
||||||
|
dup1
|
||||||
|
0x4753a67d
|
||||||
|
eq
|
||||||
|
tag_3
|
||||||
|
jumpi
|
||||||
|
tag_2:
|
||||||
|
/* "--CODEGEN--":12:13 */
|
||||||
|
0x00
|
||||||
|
/* "--CODEGEN--":9:10 */
|
||||||
|
dup1
|
||||||
|
/* "--CODEGEN--":2:14 */
|
||||||
|
revert
|
||||||
|
/* "optimizer_BlockDeDuplicator/input.sol":138:174 function f() public { true ? 1 : 3;} */
|
||||||
|
tag_3:
|
||||||
|
tag_6
|
||||||
|
tag_7
|
||||||
|
jump // in
|
||||||
|
tag_6:
|
||||||
|
stop
|
||||||
|
tag_7:
|
||||||
|
jump // out
|
||||||
|
|
||||||
|
auxdata: AUXDATA REMOVED
|
||||||
|
}
|
@ -22,7 +22,7 @@ sub_0: assembly {
|
|||||||
/* \"A\":137:149 */
|
/* \"A\":137:149 */
|
||||||
revert
|
revert
|
||||||
}
|
}
|
||||||
","bytecode":{"linkReferences":{},"object":"bytecode removed","opcodes":"opcodes removed","sourceMap":"sourceMap removed"}},"ir":"object \"NamedObject\" {
|
","bytecode":{"linkReferences":{},"object":"bytecode removed","opcodes":"opcodes removed","sourceMap":"sourceMap removed"},"deployedBytecode":{"linkReferences":{},"object":"bytecode removed","opcodes":"opcodes removed","sourceMap":"sourceMap removed"}},"ir":"object \"NamedObject\" {
|
||||||
code {
|
code {
|
||||||
let x := dataoffset(\"DataName\")
|
let x := dataoffset(\"DataName\")
|
||||||
sstore(add(x, 0), 0)
|
sstore(add(x, 0), 0)
|
||||||
|
@ -24,9 +24,12 @@
|
|||||||
#include <test/contracts/ContractInterface.h>
|
#include <test/contracts/ContractInterface.h>
|
||||||
#include <test/EVMHost.h>
|
#include <test/EVMHost.h>
|
||||||
|
|
||||||
|
#include <libsolutil/LazyInit.h>
|
||||||
|
|
||||||
#include <boost/test/unit_test.hpp>
|
#include <boost/test/unit_test.hpp>
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace solidity;
|
using namespace solidity;
|
||||||
@ -211,17 +214,18 @@ contract GlobalRegistrar is Registrar, AuctionSystem {
|
|||||||
}
|
}
|
||||||
)DELIMITER";
|
)DELIMITER";
|
||||||
|
|
||||||
static unique_ptr<bytes> s_compiledRegistrar;
|
static LazyInit<bytes> s_compiledRegistrar;
|
||||||
|
|
||||||
class AuctionRegistrarTestFramework: public SolidityExecutionFramework
|
class AuctionRegistrarTestFramework: public SolidityExecutionFramework
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
void deployRegistrar()
|
void deployRegistrar()
|
||||||
{
|
{
|
||||||
if (!s_compiledRegistrar)
|
bytes const& compiled = s_compiledRegistrar.init([&]{
|
||||||
s_compiledRegistrar = make_unique<bytes>(compileContract(registrarCode, "GlobalRegistrar"));
|
return compileContract(registrarCode, "GlobalRegistrar");
|
||||||
|
});
|
||||||
|
|
||||||
sendMessage(*s_compiledRegistrar, true);
|
sendMessage(compiled, true);
|
||||||
BOOST_REQUIRE(m_transactionSuccessful);
|
BOOST_REQUIRE(m_transactionSuccessful);
|
||||||
BOOST_REQUIRE(!m_output.empty());
|
BOOST_REQUIRE(!m_output.empty());
|
||||||
}
|
}
|
||||||
|
@ -20,8 +20,11 @@
|
|||||||
* Tests for a fixed fee registrar contract.
|
* Tests for a fixed fee registrar contract.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <libsolutil/LazyInit.h>
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
#if defined(_MSC_VER)
|
#if defined(_MSC_VER)
|
||||||
#pragma warning(push)
|
#pragma warning(push)
|
||||||
@ -122,17 +125,18 @@ contract FixedFeeRegistrar is Registrar {
|
|||||||
}
|
}
|
||||||
)DELIMITER";
|
)DELIMITER";
|
||||||
|
|
||||||
static unique_ptr<bytes> s_compiledRegistrar;
|
static LazyInit<bytes> s_compiledRegistrar;
|
||||||
|
|
||||||
class RegistrarTestFramework: public SolidityExecutionFramework
|
class RegistrarTestFramework: public SolidityExecutionFramework
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
void deployRegistrar()
|
void deployRegistrar()
|
||||||
{
|
{
|
||||||
if (!s_compiledRegistrar)
|
bytes const& compiled = s_compiledRegistrar.init([&]{
|
||||||
s_compiledRegistrar = make_unique<bytes>(compileContract(registrarCode, "FixedFeeRegistrar"));
|
return compileContract(registrarCode, "FixedFeeRegistrar");
|
||||||
|
});
|
||||||
|
|
||||||
sendMessage(*s_compiledRegistrar, true);
|
sendMessage(compiled, true);
|
||||||
BOOST_REQUIRE(m_transactionSuccessful);
|
BOOST_REQUIRE(m_transactionSuccessful);
|
||||||
BOOST_REQUIRE(!m_output.empty());
|
BOOST_REQUIRE(!m_output.empty());
|
||||||
}
|
}
|
||||||
|
@ -20,8 +20,11 @@
|
|||||||
* Tests for a (comparatively) complex multisig wallet contract.
|
* Tests for a (comparatively) complex multisig wallet contract.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <libsolutil/LazyInit.h>
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
#if defined(_MSC_VER)
|
#if defined(_MSC_VER)
|
||||||
#pragma warning(push)
|
#pragma warning(push)
|
||||||
@ -434,7 +437,7 @@ contract Wallet is multisig, multiowned, daylimit {
|
|||||||
}
|
}
|
||||||
)DELIMITER";
|
)DELIMITER";
|
||||||
|
|
||||||
static unique_ptr<bytes> s_compiledWallet;
|
static LazyInit<bytes> s_compiledWallet;
|
||||||
|
|
||||||
class WalletTestFramework: public SolidityExecutionFramework
|
class WalletTestFramework: public SolidityExecutionFramework
|
||||||
{
|
{
|
||||||
@ -446,11 +449,12 @@ protected:
|
|||||||
u256 _dailyLimit = 0
|
u256 _dailyLimit = 0
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
if (!s_compiledWallet)
|
bytes const& compiled = s_compiledWallet.init([&]{
|
||||||
s_compiledWallet = make_unique<bytes>(compileContract(walletCode, "Wallet"));
|
return compileContract(walletCode, "Wallet");
|
||||||
|
});
|
||||||
|
|
||||||
bytes args = encodeArgs(u256(0x60), _required, _dailyLimit, u256(_owners.size()), _owners);
|
bytes args = encodeArgs(u256(0x60), _required, _dailyLimit, u256(_owners.size()), _owners);
|
||||||
sendMessage(*s_compiledWallet + args, true, _value);
|
sendMessage(compiled + args, true, _value);
|
||||||
BOOST_REQUIRE(m_transactionSuccessful);
|
BOOST_REQUIRE(m_transactionSuccessful);
|
||||||
BOOST_REQUIRE(!m_output.empty());
|
BOOST_REQUIRE(!m_output.empty());
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
[
|
||||||
{
|
{
|
||||||
"absolutePath": "a",
|
"absolutePath": "a",
|
||||||
"exportedSymbols":
|
"exportedSymbols":
|
||||||
@ -218,3 +219,4 @@
|
|||||||
],
|
],
|
||||||
"src": "0:160:3"
|
"src": "0:160:3"
|
||||||
}
|
}
|
||||||
|
]
|
||||||
|
@ -140,6 +140,9 @@ TestCase::TestResult ASTJSONTest::run(ostream& _stream, string const& _linePrefi
|
|||||||
return TestResult::FatalError;
|
return TestResult::FatalError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_sources.size() > 1)
|
||||||
|
m_result += "[\n";
|
||||||
|
|
||||||
for (size_t i = 0; i < m_sources.size(); i++)
|
for (size_t i = 0; i < m_sources.size(); i++)
|
||||||
{
|
{
|
||||||
ostringstream result;
|
ostringstream result;
|
||||||
@ -150,6 +153,9 @@ TestCase::TestResult ASTJSONTest::run(ostream& _stream, string const& _linePrefi
|
|||||||
m_result += "\n";
|
m_result += "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_sources.size() > 1)
|
||||||
|
m_result += "]\n";
|
||||||
|
|
||||||
bool resultsMatch = true;
|
bool resultsMatch = true;
|
||||||
|
|
||||||
replaceTagWithVersion(m_expectation);
|
replaceTagWithVersion(m_expectation);
|
||||||
|
@ -12,6 +12,7 @@ contract c {
|
|||||||
l = data.length;
|
l = data.length;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// test() -> 2, 1, 1
|
// test() -> 2, 1, 1
|
||||||
|
@ -9,5 +9,7 @@ contract c {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// test() -> FAILURE
|
// test() -> FAILURE
|
||||||
|
@ -13,6 +13,7 @@ contract c {
|
|||||||
if (l != 0x03) return true;
|
if (l != 0x03) return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// test() -> false
|
// test() -> false
|
||||||
|
@ -13,6 +13,7 @@ contract c {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// test() -> 0
|
// test() -> 0
|
||||||
|
@ -0,0 +1,45 @@
|
|||||||
|
contract c {
|
||||||
|
bytes data;
|
||||||
|
function test_short() public returns (uint256 r) {
|
||||||
|
assembly {
|
||||||
|
sstore(data_slot, 0)
|
||||||
|
}
|
||||||
|
for (uint8 i = 0; i < 15; i++) {
|
||||||
|
data.push(bytes1(i));
|
||||||
|
}
|
||||||
|
assembly {
|
||||||
|
r := sload(data_slot)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_long() public returns (uint256 r) {
|
||||||
|
assembly {
|
||||||
|
sstore(data_slot, 0)
|
||||||
|
}
|
||||||
|
for (uint8 i = 0; i < 33; i++) {
|
||||||
|
data.push(bytes1(i));
|
||||||
|
}
|
||||||
|
assembly {
|
||||||
|
r := sload(data_slot)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_pop() public returns (uint256 r) {
|
||||||
|
assembly {
|
||||||
|
sstore(data_slot, 0)
|
||||||
|
}
|
||||||
|
for (uint8 i = 0; i < 32; i++) {
|
||||||
|
data.push(bytes1(i));
|
||||||
|
}
|
||||||
|
data.pop();
|
||||||
|
assembly {
|
||||||
|
r := sload(data_slot)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// test_short() -> 1780731860627700044960722568376587075150542249149356309979516913770823710
|
||||||
|
// test_long() -> 67
|
||||||
|
// test_pop() -> 1780731860627700044960722568376592200742329637303199754547598369979440702
|
@ -0,0 +1,21 @@
|
|||||||
|
// Tests transition between short and long encoding both ways
|
||||||
|
contract c {
|
||||||
|
bytes data;
|
||||||
|
|
||||||
|
function test() public returns (uint256) {
|
||||||
|
for (uint8 i = 0; i < 33; i++) {
|
||||||
|
data.push(bytes1(i));
|
||||||
|
}
|
||||||
|
for (uint8 i = 0; i < data.length; i++)
|
||||||
|
if (data[i] != bytes1(i)) return i;
|
||||||
|
data.pop();
|
||||||
|
data.pop();
|
||||||
|
for (uint8 i = 0; i < data.length; i++)
|
||||||
|
if (data[i] != bytes1(i)) return i;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// test() -> 0
|
@ -4,5 +4,7 @@ contract C {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// f() -> 0x9c1185a5c5e9fc54612808977ee8f548b2258d31000000000000000000000000
|
// f() -> 0x9c1185a5c5e9fc54612808977ee8f548b2258d31000000000000000000000000
|
||||||
|
@ -3,5 +3,7 @@ contract C {
|
|||||||
return sha256("");
|
return sha256("");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// f() -> 0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
|
// f() -> 0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
|
||||||
|
@ -3,6 +3,8 @@ contract test {
|
|||||||
return ecrecover(h, v, r, s);
|
return ecrecover(h, v, r, s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// a(bytes32,uint8,bytes32,bytes32):
|
// a(bytes32,uint8,bytes32,bytes32):
|
||||||
// 0x18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c,
|
// 0x18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c,
|
||||||
|
@ -4,6 +4,8 @@ contract test {
|
|||||||
return ecrecover(h, v, r, s);
|
return ecrecover(h, v, r, s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// a(bytes32,uint8,bytes32,bytes32):
|
// a(bytes32,uint8,bytes32,bytes32):
|
||||||
// 0x18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c,
|
// 0x18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c,
|
||||||
|
@ -6,5 +6,7 @@ contract C {
|
|||||||
return ecrecover(bytes32(uint(-1)), 1, bytes32(uint(2)), bytes32(uint(3)));
|
return ecrecover(bytes32(uint(-1)), 1, bytes32(uint(2)), bytes32(uint(3)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// f() -> 0
|
// f() -> 0
|
||||||
|
@ -11,5 +11,7 @@ contract C {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// f() -> 0
|
// f() -> 0
|
||||||
|
@ -16,5 +16,7 @@ contract C {
|
|||||||
return ecrecover(hash, v, r, s);
|
return ecrecover(hash, v, r, s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// f() -> 0
|
// f() -> 0
|
||||||
|
@ -0,0 +1,10 @@
|
|||||||
|
pragma experimental SMTChecker;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
function f(uint[][] memory arr) public pure {
|
||||||
|
uint[][] memory arr2 = arr;
|
||||||
|
assert(arr2[0].length == arr[0].length);
|
||||||
|
assert(arr.length == arr2.length);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
pragma experimental SMTChecker;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
uint[][] arr;
|
||||||
|
uint[][] arr2;
|
||||||
|
function f() public view {
|
||||||
|
assert(arr2[0].length == arr[0].length);
|
||||||
|
assert(arr2.length == arr.length);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
pragma experimental SMTChecker;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
uint[][] arr;
|
||||||
|
function f(uint[][] memory arr2) public {
|
||||||
|
arr = arr2;
|
||||||
|
assert(arr2[0].length == arr[0].length);
|
||||||
|
assert(arr2.length == arr.length);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
pragma experimental SMTChecker;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
uint[][] arr;
|
||||||
|
function f() public view {
|
||||||
|
uint[][] memory arr2 = arr;
|
||||||
|
assert(arr2[0].length == arr[0].length);
|
||||||
|
assert(arr2.length == arr.length);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
pragma experimental SMTChecker;
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
mapping (uint => uint[]) map;
|
||||||
|
function f() public view {
|
||||||
|
assert(map[0].length == map[1].length);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
pragma experimental SMTChecker;
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
mapping (uint => uint[]) map;
|
||||||
|
function f(uint x, uint y) public view {
|
||||||
|
require(x == y);
|
||||||
|
assert(map[x].length == map[y].length);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
pragma experimental SMTChecker;
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
mapping (uint => uint[][]) map;
|
||||||
|
function f(uint x, uint y) public view {
|
||||||
|
require(x == y);
|
||||||
|
assert(map[x][0].length == map[y][0].length);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
pragma experimental SMTChecker;
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
struct S {
|
||||||
|
uint[] arr;
|
||||||
|
}
|
||||||
|
S s1;
|
||||||
|
S s2;
|
||||||
|
function f() public view {
|
||||||
|
assert(s1.arr.length == s2.arr.length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// Warning: (76-80): Assertion checker does not yet support the type of this variable.
|
||||||
|
// Warning: (83-87): Assertion checker does not yet support the type of this variable.
|
||||||
|
// Warning: (126-132): Assertion checker does not yet support this expression.
|
||||||
|
// Warning: (126-128): Assertion checker does not yet implement type struct C.S storage ref
|
||||||
|
// Warning: (143-149): Assertion checker does not yet support this expression.
|
||||||
|
// Warning: (143-145): Assertion checker does not yet implement type struct C.S storage ref
|
||||||
|
// Warning: (119-157): Assertion violation happens here
|
@ -0,0 +1,22 @@
|
|||||||
|
pragma experimental SMTChecker;
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
struct S {
|
||||||
|
uint[][] arr;
|
||||||
|
}
|
||||||
|
S s1;
|
||||||
|
S s2;
|
||||||
|
function f() public view {
|
||||||
|
assert(s1.arr[0].length == s2.arr[0].length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// Warning: (78-82): Assertion checker does not yet support the type of this variable.
|
||||||
|
// Warning: (85-89): Assertion checker does not yet support the type of this variable.
|
||||||
|
// Warning: (128-134): Assertion checker does not yet support this expression.
|
||||||
|
// Warning: (128-130): Assertion checker does not yet implement type struct C.S storage ref
|
||||||
|
// Warning: (128-137): Assertion checker does not yet implement this expression.
|
||||||
|
// Warning: (148-154): Assertion checker does not yet support this expression.
|
||||||
|
// Warning: (148-150): Assertion checker does not yet implement type struct C.S storage ref
|
||||||
|
// Warning: (148-157): Assertion checker does not yet implement this expression.
|
||||||
|
// Warning: (121-165): Assertion violation happens here
|
@ -0,0 +1,9 @@
|
|||||||
|
pragma experimental SMTChecker;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
function f(uint[][] memory arr) public pure {
|
||||||
|
uint[][] memory arr2 = arr;
|
||||||
|
assert(arr2.length == arr.length);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
pragma experimental SMTChecker;
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
function f(uint[] memory arr) public pure {
|
||||||
|
uint[] memory arr2 = arr;
|
||||||
|
assert(arr2.length == arr.length);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
pragma experimental SMTChecker;
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
uint[] arr;
|
||||||
|
uint[] arr2;
|
||||||
|
function f() public {
|
||||||
|
arr2 = arr;
|
||||||
|
assert(arr2.length == arr.length);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
pragma experimental SMTChecker;
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
uint[] arr;
|
||||||
|
function f() public view {
|
||||||
|
uint x = arr.length;
|
||||||
|
uint y = x;
|
||||||
|
assert(arr.length == y);
|
||||||
|
assert(arr.length != y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// Warning: (153-176): Assertion violation happens here
|
@ -0,0 +1,9 @@
|
|||||||
|
pragma experimental SMTChecker;
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
uint[] arr;
|
||||||
|
function f(uint[] memory marr) public {
|
||||||
|
arr = marr;
|
||||||
|
assert(marr.length == arr.length);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
pragma experimental SMTChecker;
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
uint[] arr;
|
||||||
|
function f() public view {
|
||||||
|
uint[] memory marr = arr;
|
||||||
|
assert(marr.length == arr.length);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
pragma experimental SMTChecker;
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
uint[] arr;
|
||||||
|
function f() public view {
|
||||||
|
assert(arr.length == g().length);
|
||||||
|
}
|
||||||
|
function g() internal pure returns (uint[] memory) {
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
pragma experimental SMTChecker;
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
uint[] arr;
|
||||||
|
function f() public view {
|
||||||
|
uint[] memory arr2 = arr;
|
||||||
|
arr2[2] = 3;
|
||||||
|
assert(arr.length == arr2.length);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
pragma experimental SMTChecker;
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
uint[][] arr;
|
||||||
|
uint[][] arr2;
|
||||||
|
function f() public {
|
||||||
|
uint x = arr[2].length;
|
||||||
|
uint y = arr[3].length;
|
||||||
|
uint z = arr.length;
|
||||||
|
arr[2][333] = 444;
|
||||||
|
assert(arr[2].length == x);
|
||||||
|
assert(arr[3].length == y);
|
||||||
|
assert(arr.length == z);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
pragma experimental SMTChecker;
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
uint[][] arr;
|
||||||
|
uint[][] arr2;
|
||||||
|
function f() public {
|
||||||
|
uint x = arr[2].length;
|
||||||
|
uint y = arr[3].length;
|
||||||
|
uint z = arr.length;
|
||||||
|
arr[2][333] = 444;
|
||||||
|
assert(arr[2].length != x);
|
||||||
|
assert(arr[3].length != y);
|
||||||
|
assert(arr.length != z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// Warning: (198-224): Assertion violation happens here
|
||||||
|
// Warning: (228-254): Assertion violation happens here
|
||||||
|
// Warning: (258-281): Assertion violation happens here
|
@ -0,0 +1,17 @@
|
|||||||
|
pragma experimental SMTChecker;
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
uint[][] arr;
|
||||||
|
uint[][] arr2;
|
||||||
|
function f() public {
|
||||||
|
uint x = arr[2].length;
|
||||||
|
uint y = arr[3].length;
|
||||||
|
uint z = arr.length;
|
||||||
|
uint t = arr[5].length;
|
||||||
|
arr[5] = arr[8];
|
||||||
|
assert(arr[2].length == x);
|
||||||
|
assert(arr[3].length == y);
|
||||||
|
assert(arr.length == z);
|
||||||
|
assert(arr[5].length == t);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
pragma experimental SMTChecker;
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
uint[][] arr;
|
||||||
|
uint[][] arr2;
|
||||||
|
function f() public {
|
||||||
|
uint x = arr[2].length;
|
||||||
|
uint y = arr[3].length;
|
||||||
|
uint z = arr.length;
|
||||||
|
uint t = arr[5].length;
|
||||||
|
arr[5] = arr[8];
|
||||||
|
assert(arr[2].length != x);
|
||||||
|
assert(arr[3].length != y);
|
||||||
|
assert(arr.length != z);
|
||||||
|
assert(arr[5].length != t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// Warning: (222-248): Assertion violation happens here
|
||||||
|
// Warning: (252-278): Assertion violation happens here
|
||||||
|
// Warning: (282-305): Assertion violation happens here
|
||||||
|
// Warning: (309-335): Assertion violation happens here
|
@ -0,0 +1,9 @@
|
|||||||
|
pragma experimental SMTChecker;
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
uint[] a;
|
||||||
|
function f() public {
|
||||||
|
a.push();
|
||||||
|
a.pop();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
pragma experimental SMTChecker;
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
uint[] a;
|
||||||
|
function f() public {
|
||||||
|
a.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// Warning: (82-89): Empty array "pop" detected here.
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user