Merge remote-tracking branch 'origin/develop' into breaking

This commit is contained in:
chriseth 2020-06-10 11:30:50 +02:00
commit 6b3171c38b
60 changed files with 889 additions and 391 deletions

View File

@ -598,7 +598,7 @@ jobs:
b_ems:
docker:
- image: ethereum/solidity-buildpack-deps:emsdk-1.39.15-1
- image: ethereum/solidity-buildpack-deps:emsdk-1.39.15-2
environment:
TERM: xterm
steps:

View File

@ -26,11 +26,9 @@
# 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
# make version=1.39.15 build
#
# TODO: switch to the upstream backend by removing "-fastcomp".
#
FROM emscripten/emsdk:1.39.15-fastcomp AS base
FROM emscripten/emsdk:1.39.15 AS base
ADD emscripten.jam /usr/src
RUN set -ex; \
@ -47,6 +45,7 @@ RUN set -ex; \
-DZ3_BUILD_TEST_EXECUTABLES=OFF \
-DZ3_BUILD_EXECUTABLE=OFF \
-DZ3_SINGLE_THREADED=ON \
-DCMAKE_CXX_FLAGS="-s DISABLE_EXCEPTION_CATCHING=0" \
..; \
make; make install; \
rm -r /usr/src/z3; \
@ -61,6 +60,6 @@ RUN set -ex; \
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" \
cxxflags="-s DISABLE_EXCEPTION_CATCHING=0 -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

View File

@ -19,39 +19,56 @@ Bugfixes:
* NatSpec: Constructors and functions have consistent userdoc output.
### 0.6.9 (unreleased)
### 0.6.10 (unreleased)
Language Features:
Compiler Features:
* Yul: Raise warning for switch statements that only have a default and no other cases.
Bugfixes:
* SMTChecker: Fix internal error when encoding tuples of tuples.
* SMTChecker: Fix aliasing soundness after pushing to an array pointer.
### 0.6.9 (2020-06-04)
Language Features:
* Permit calldata location for all variables.
* NatSpec: Support NatSpec comments on state variables.
* Yul: EVM instruction `pc()` is marked deprecated and will be removed in the next breaking release.
Compiler Features:
* Code Generator: Do not introduce new source references for small internal routines.
* Build system: Update the soljson.js build to emscripten 1.39.15 and boost 1.73.0 and include Z3 for integrated SMTChecker support without the callback mechanism.
* Commandline Interface: Adds new option ``--base-path PATH`` to use the given path as the root of the source tree instead of the root of the filesystem.
* Build system: Switch the emscripten build from the fastcomp backend to the upstream backend.
* Code Generator: Do not introduce new internal source references for small compiler routines.
* Commandline Interface: Adds new option ``--base-path PATH`` to use the given path as the root of the source tree (defaults to the root of the filesystem).
* SMTChecker: Support array ``length``.
* SMTChecker: Support array ``push`` and ``pop``.
* SMTChecker: General support to BitVectors and the bitwise ``and`` operator.
* Add support for natspec comments on state variables.
Bugfixes:
* Code Generator: Trigger proper unimplemented errors on certain array copy operations.
* Commandline Interface: Fix internal error when using ``--assemble`` or ``--yul`` options with ``--machine ewasm`` but without specifying ``--yul-dialect``.
* NatSpec: DocString block is terminated when encountering an empty line.
* Optimizer: Fixed a bug in BlockDeDuplicator.
* Scanner: Fix bug when two empty NatSpec comments lead to scanning past EOL.
* SMTChecker: Fix internal error on try/catch clauses with parameters.
* SMTChecker: Fix internal error when applying arithmetic operators to fixed point variables.
* SMTChecker: Fix internal error when assigning to index access inside branches.
* SMTChecker: Fix internal error when short circuiting Boolean expressions with function calls in state variable initialization.
* Type Checker: Disallow assignments to storage variables of type ``mapping``.
* Type Checker: Disallow inline arrays of non-nameable types.
* Type Checker: Disallow usage of override with non-public state variables.
* Type Checker: Fix internal compiler error when accessing members of array slices.
* Type Checker: Fix internal compiler error when forward referencing non-literal constants from inline assembly.
* Type Checker: Fix internal compiler error when trying to decode too large static arrays.
* Type Checker: Fix wrong compiler error when referencing an overridden function without calling it.
* Type Checker: Fix internal compiler error when forward referencing non-literal constants from inline assembly.
* Type Checker: Disallow usage of override with non-public state variables.
* NatSpec: DocString block is terminated when encountering an empty line.
* Scanner: Fix bug when two empty NatSpec comments lead to scanning past EOL.
* Code Generator: Trigger proper unimplemented errors on certain array copy operations.
* SMTChecker: Fix internal error when applying arithmetic operators to fixed point variables.
* SMTChecker: Fix internal error when short circuiting Boolean expressions with function calls in state variable initialization.
* SMTChecker: Fix internal error when assigning to index access inside branches.
* SMTChecker: Fix internal error on try/catch clauses with parameters.
### 0.6.8 (2020-05-14)

View File

@ -6,9 +6,8 @@
- [ ] Readthedocs account, access to the Solidity project
- [ ] Write access to https://github.com/ethereum/homebrew-ethereum
### Pre-release
- [ ] Ensure that a Github project exists for the release.
- [ ] Check that all issues and pull requests from the Github project to be released are merged to ``develop``.
### Blog Post
- [ ] Create a post on https://github.com/ethereum/solidity-blog and explain some of the new features or concepts.
### Changelog
- [ ] Sort the changelog entries alphabetically and correct any errors you notice.
@ -33,8 +32,9 @@
- [ ] Wait for the ``~ethereum/ubuntu/ethereum-static`` PPA build to be finished and published for *all platforms*. SERIOUSLY: DO NOT PROCEED EARLIER!!! *After* the static builds are *published*, copy the static package to the ``~ethereum/ubuntu/ethereum`` PPA for the destination series ``Trusty`` and ``Xenial`` while selecting ``Copy existing binaries``.
- [ ] Check that the Docker release was pushed to Docker Hub (this still seems to have problems, run ``./scripts/docker_deploy_manual.sh v0.x.x``).
### Homebrew
### Homebrew and MacOS
- [ ] Update the version and the hash (``sha256sum solidity_x.x.x.tar.gz``) in https://github.com/ethereum/homebrew-ethereum/blob/master/solidity.rb
- [ ] Take the binary from the ``b_osx`` run of the released commit in circle-ci and add it to the release page as ``solc-macos``.
### Documentation
- [ ] Build the new version on https://readthedocs.org/projects/solidity/ (select `latest` on the bottom of the page and click `BUILD`)

View File

@ -16,10 +16,12 @@
include(EthCheckCXXCompilerFlag)
if(NOT EMSCRIPTEN)
eth_add_cxx_compiler_flag_if_supported(-fstack-protector-strong have_stack_protector_strong_support)
if(NOT have_stack_protector_strong_support)
eth_add_cxx_compiler_flag_if_supported(-fstack-protector)
endif()
endif()
eth_add_cxx_compiler_flag_if_supported(-Wimplicit-fallthrough)
@ -109,15 +111,13 @@ if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MA
# Re-enable exception catching (optimisations above -O1 disable it)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s DISABLE_EXCEPTION_CATCHING=0")
# Remove any code related to exit (such as atexit)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s NO_EXIT_RUNTIME=1")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s EXIT_RUNTIME=0")
# Remove any code related to filesystem access
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s NO_FILESYSTEM=1")
# Remove variables even if it needs to be duplicated (can improve speed at the cost of size)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s AGGRESSIVE_VARIABLE_ELIMINATION=1")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s FILESYSTEM=0")
# Allow memory growth, but disable some optimisations
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s ALLOW_MEMORY_GROWTH=1")
# Disable eval()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s NO_DYNAMIC_EXECUTION=1")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s DYNAMIC_EXECUTION=0")
# Disable greedy exception catcher
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s NODEJS_CATCH_EXIT=0")
# Abort if linking results in any undefined symbols

View File

@ -42,7 +42,8 @@ For most of the topics the compiler will provide suggestions.
storage arrays.
* The new keyword ``abstract`` can be used to mark contracts as abstract. It has to be used
if a contract does not implement all its functions.
if a contract does not implement all its functions. Abstract contracts cannot be created using the ``new`` operator,
and it is not possible to generate bytecode for them during compilation.
* Libraries have to implement all their functions, not only the internal ones.

View File

@ -124,7 +124,11 @@ userDefinedTypeName
: identifier ( '.' identifier )* ;
mapping
: 'mapping' '(' (elementaryTypeName | userDefinedTypeName) '=>' typeName ')' ;
: 'mapping' '(' mappingKey '=>' typeName ')' ;
mappingKey
: elementaryTypeName
| userDefinedTypeName ;
functionTypeName
: 'function' parameterList modifierList returnParameters? ;
@ -470,7 +474,7 @@ SingleQuotedStringCharacter
: ~['\r\n\\] | ('\\' .) ;
VersionLiteral
: [0-9]+ '.' [0-9]+ ('.' [0-9]+)? ;
: [0-9]+ ( '.' [0-9]+ ('.' [0-9]+)? )? ;
WS
: [ \t\r\n\u000C]+ -> skip ;

View File

@ -24,7 +24,7 @@ Function Selector
=================
The first four bytes of the call data for a function call specifies the function to be called. It is the
first (left, high-order in big-endian) four bytes of the Keccak-256 (SHA-3) hash of the signature of
first (left, high-order in big-endian) four bytes of the Keccak-256 hash of the signature of
the function. The signature is defined as the canonical expression of the basic prototype without data
location specifier, i.e.
the function name with the parenthesised list of parameter types. Parameter types are split by a single

View File

@ -1163,5 +1163,9 @@
"0.6.8": {
"bugs": [],
"released": "2020-05-14"
},
"0.6.9": {
"bugs": [],
"released": "2020-06-04"
}
}

View File

@ -194,10 +194,10 @@ not known in the context of the class where it is used,
although its type is known. This is similar for ordinary
virtual method lookup.
.. _function-overriding:
.. index:: ! overriding;function
.. _function-overriding:
Function Overriding
===================
@ -317,10 +317,10 @@ of the variable:
While public state variables can override external functions, they themselves cannot
be overridden.
.. _modifier-overriding:
.. index:: ! overriding;modifier
.. _modifier-overriding:
Modifier Overriding
===================

View File

@ -82,7 +82,7 @@ TypePointer const& TypeChecker::type(VariableDeclaration const& _variable) const
bool TypeChecker::visit(ContractDefinition const& _contract)
{
m_scope = &_contract;
m_currentContract = &_contract;
ASTNode::listAccept(_contract.baseContracts(), *this);
@ -266,7 +266,7 @@ void TypeChecker::endVisit(InheritanceSpecifier const& _inheritance)
auto base = dynamic_cast<ContractDefinition const*>(&dereference(_inheritance.name()));
solAssert(base, "Base contract not available.");
if (m_scope->isInterface() && !base->isInterface())
if (m_currentContract->isInterface() && !base->isInterface())
m_errorReporter.typeError(6536_error, _inheritance.location(), "Interfaces can only inherit from other interfaces.");
if (base->isLibrary())
@ -328,8 +328,6 @@ void TypeChecker::endVisit(ModifierDefinition const& _modifier)
bool TypeChecker::visit(FunctionDefinition const& _function)
{
bool isLibraryFunction = _function.inContractKind() == ContractKind::Library;
if (_function.markedVirtual())
{
if (_function.annotation().contract->isInterface())
@ -342,7 +340,7 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
if (_function.isPayable())
{
if (isLibraryFunction)
if (_function.libraryFunction())
m_errorReporter.typeError(7708_error, _function.location(), "Library functions cannot be payable.");
if (_function.isOrdinary() && !_function.isPartOfExternalInterface())
m_errorReporter.typeError(5587_error, _function.location(), "Internal functions cannot be payable.");
@ -352,15 +350,13 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
{
if (var.referenceLocation() != VariableDeclaration::Location::Storage)
{
if (!isLibraryFunction && _function.isPublic())
if (!_function.libraryFunction() && _function.isPublic())
m_errorReporter.typeError(3442_error, var.location(), "Mapping types can only have a data location of \"storage\" and thus only be parameters or return variables for internal or library functions.");
else
m_errorReporter.typeError(5380_error, var.location(), "Mapping types can only have a data location of \"storage\"." );
}
else
{
solAssert(isLibraryFunction || !_function.isPublic(), "Mapping types for parameters or return variables can only be used in internal or library functions.");
}
solAssert(_function.libraryFunction() || !_function.isPublic(), "Mapping types for parameters or return variables can only be used in internal or library functions.");
}
else
{
@ -368,7 +364,7 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
m_errorReporter.typeError(3312_error, var.location(), "Type is required to live outside storage.");
if (_function.isPublic())
{
auto iType = type(var)->interfaceType(isLibraryFunction);
auto iType = type(var)->interfaceType(_function.libraryFunction());
if (!iType)
{
@ -380,7 +376,7 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
if (
_function.isPublic() &&
!_function.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::ABIEncoderV2) &&
!typeSupportedByOldABIEncoder(*type(var), isLibraryFunction)
!typeSupportedByOldABIEncoder(*type(var), _function.libraryFunction())
)
m_errorReporter.typeError(
4957_error,
@ -419,7 +415,7 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
else
modifiers.insert(decl);
}
if (m_scope->isInterface())
if (m_currentContract->isInterface())
{
if (_function.isImplemented())
m_errorReporter.typeError(4726_error, _function.location(), "Functions in interfaces cannot have an implementation.");
@ -430,14 +426,14 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
if (_function.isConstructor())
m_errorReporter.typeError(6482_error, _function.location(), "Constructor cannot be defined in interfaces.");
}
else if (m_scope->contractKind() == ContractKind::Library)
else if (m_currentContract->contractKind() == ContractKind::Library)
if (_function.isConstructor())
m_errorReporter.typeError(7634_error, _function.location(), "Constructor cannot be defined in libraries.");
if (_function.isImplemented())
_function.body().accept(*this);
else if (_function.isConstructor())
m_errorReporter.typeError(5700_error, _function.location(), "Constructor must be implemented if declared.");
else if (isLibraryFunction)
else if (_function.libraryFunction())
m_errorReporter.typeError(9231_error, _function.location(), "Library functions must be implemented if declared.");
else if (!_function.virtualSemantics())
m_errorReporter.typeError(5424_error, _function.location(), "Functions without implementation must be marked virtual.");
@ -1796,7 +1792,7 @@ void TypeChecker::typeCheckFunctionCall(
if (_functionType->kind() == FunctionType::Kind::Declaration)
{
if (
m_scope->derivesFrom(*_functionType->declaration().annotation().contract) &&
m_currentContract->derivesFrom(*_functionType->declaration().annotation().contract) &&
!dynamic_cast<FunctionDefinition const&>(_functionType->declaration()).isImplemented()
)
m_errorReporter.typeError(
@ -1832,7 +1828,7 @@ void TypeChecker::typeCheckFallbackFunction(FunctionDefinition const& _function)
{
solAssert(_function.isFallback(), "");
if (_function.inContractKind() == ContractKind::Library)
if (_function.libraryFunction())
m_errorReporter.typeError(5982_error, _function.location(), "Libraries cannot have fallback functions.");
if (_function.stateMutability() != StateMutability::NonPayable && _function.stateMutability() != StateMutability::Payable)
m_errorReporter.typeError(
@ -1859,7 +1855,7 @@ void TypeChecker::typeCheckReceiveFunction(FunctionDefinition const& _function)
{
solAssert(_function.isReceive(), "");
if (_function.inContractKind() == ContractKind::Library)
if (_function.libraryFunction())
m_errorReporter.typeError(4549_error, _function.location(), "Libraries cannot have receive ether functions.");
if (_function.stateMutability() != StateMutability::Payable)
@ -1918,7 +1914,7 @@ void TypeChecker::typeCheckABIEncodeFunctions(
bool const isPacked = _functionType->kind() == FunctionType::Kind::ABIEncodePacked;
solAssert(_functionType->padArguments() != isPacked, "ABI function with unexpected padding");
bool const abiEncoderV2 = m_scope->sourceUnit().annotation().experimentalFeatures.count(
bool const abiEncoderV2 = m_currentContract->sourceUnit().annotation().experimentalFeatures.count(
ExperimentalFeature::ABIEncoderV2
);
@ -2318,7 +2314,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
case FunctionType::Kind::ABIDecode:
{
bool const abiEncoderV2 =
m_scope->sourceUnit().annotation().experimentalFeatures.count(
m_currentContract->sourceUnit().annotation().experimentalFeatures.count(
ExperimentalFeature::ABIEncoderV2
);
returnTypes = typeCheckABIDecodeAndRetrieveReturnType(_functionCall, abiEncoderV2);
@ -2514,13 +2510,13 @@ void TypeChecker::endVisit(NewExpression const& _newExpression)
if (contract->abstract())
m_errorReporter.typeError(4614_error, _newExpression.location(), "Cannot instantiate an abstract contract.");
solAssert(!!m_scope, "");
m_scope->annotation().contractDependencies.insert(contract);
solAssert(!!m_currentContract, "");
m_currentContract->annotation().contractDependencies.insert(contract);
solAssert(
!contract->annotation().linearizedBaseContracts.empty(),
"Linearized base contracts not yet available."
);
if (contractDependenciesAreCyclic(*m_scope))
if (contractDependenciesAreCyclic(*m_currentContract))
m_errorReporter.typeError(
4579_error,
_newExpression.location(),
@ -2567,7 +2563,7 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
// Retrieve the types of the arguments if this is used to call a function.
auto const& arguments = _memberAccess.annotation().arguments;
MemberList::MemberMap possibleMembers = exprType->members(m_scope).membersByName(memberName);
MemberList::MemberMap possibleMembers = exprType->members(m_currentContract).membersByName(memberName);
size_t const initialMemberCount = possibleMembers.size();
if (initialMemberCount > 1 && arguments)
{
@ -2593,7 +2589,7 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
DataLocation::Storage,
exprType
);
if (!storageType->members(m_scope).membersByName(memberName).empty())
if (!storageType->members(m_currentContract).membersByName(memberName).empty())
m_errorReporter.fatalTypeError(
4994_error,
_memberAccess.location(),
@ -2749,7 +2745,7 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
{
annotation.isPure = true;
ContractType const& accessedContractType = dynamic_cast<ContractType const&>(*magicType->typeArgument());
m_scope->annotation().contractDependencies.insert(&accessedContractType.contractDefinition());
m_currentContract->annotation().contractDependencies.insert(&accessedContractType.contractDefinition());
if (
memberName == "runtimeCode" &&
@ -2761,7 +2757,7 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
"\"runtimeCode\" is not available for contracts containing immutable variables."
);
if (contractDependenciesAreCyclic(*m_scope))
if (contractDependenciesAreCyclic(*m_currentContract))
m_errorReporter.typeError(
4224_error,
_memberAccess.location(),

View File

@ -165,7 +165,7 @@ private:
/// Runs type checks on @a _expression to infer its type and then checks that it is an LValue.
void requireLValue(Expression const& _expression, bool _ordinaryAssignment);
ContractDefinition const* m_scope = nullptr;
ContractDefinition const* m_currentContract = nullptr;
langutil::EVMVersion m_evmVersion;

View File

@ -276,7 +276,7 @@ void ViewPureChecker::reportMutability(
{
// We do not warn for library functions because they cannot be payable anyway.
// Also internal functions should be allowed to use `msg.value`.
if (m_currentFunction->isPublic() && m_currentFunction->inContractKind() != ContractKind::Library)
if (m_currentFunction->isPublic() && !m_currentFunction->libraryFunction())
{
if (_nestedLocation)
m_errorReporter.typeError(

View File

@ -287,11 +287,11 @@ TypeDeclarationAnnotation& EnumDefinition::annotation() const
return initAnnotation<TypeDeclarationAnnotation>();
}
ContractKind FunctionDefinition::inContractKind() const
bool FunctionDefinition::libraryFunction() const
{
auto contractDef = dynamic_cast<ContractDefinition const*>(scope());
solAssert(contractDef, "Enclosing Scope of FunctionDefinition was not set.");
return contractDef->contractKind();
if (auto const* contractDef = dynamic_cast<ContractDefinition const*>(scope()))
return contractDef->isLibrary();
return false;
}
FunctionTypePointer FunctionDefinition::functionType(bool _internal) const

View File

@ -799,6 +799,7 @@ public:
void accept(ASTConstVisitor& _visitor) const override;
StateMutability stateMutability() const { return m_stateMutability; }
bool libraryFunction() const;
bool isOrdinary() const { return m_kind == Token::Function; }
bool isConstructor() const { return m_kind == Token::Constructor; }
bool isFallback() const { return m_kind == Token::Fallback; }
@ -825,8 +826,6 @@ public:
/// @returns the external identifier of this function (the hash of the signature) as a hex string.
std::string externalIdentifierHex() const;
ContractKind inContractKind() const;
TypePointer type() const override;
TypePointer typeViaContractName() const override;

View File

@ -1730,7 +1730,7 @@ void IRGeneratorForStatements::endVisit(IndexAccess const& _indexAccess)
setLValue(_indexAccess, IRLValue{
*arrayType.baseType(),
IRLValue::Memory{memAddress}
IRLValue::Memory{memAddress, arrayType.isByteArray()}
});
break;
}
@ -1763,7 +1763,23 @@ void IRGeneratorForStatements::endVisit(IndexAccess const& _indexAccess)
}
}
else if (baseType.category() == Type::Category::FixedBytes)
solUnimplementedAssert(false, "");
{
auto const& fixedBytesType = dynamic_cast<FixedBytesType const&>(baseType);
solAssert(_indexAccess.indexExpression(), "Index expression expected.");
IRVariable index{m_context.newYulVariable(), *TypeProvider::uint256()};
define(index, *_indexAccess.indexExpression());
m_code << Whiskers(R"(
if iszero(lt(<index>, <length>)) { invalid() }
let <result> := <shl248>(byte(<index>, <array>))
)")
("index", index.name())
("length", to_string(fixedBytesType.numBytes()))
("array", IRVariable(_indexAccess.baseExpression()).name())
("shl248", m_utils.shiftLeftFunction(256 - 8))
("result", IRVariable(_indexAccess).name())
.render();
}
else if (baseType.category() == Type::Category::TypeType)
{
solAssert(baseType.sizeOnStack() == 0, "");

View File

@ -604,7 +604,11 @@ void BMC::checkUnderflow(BMCVerificationTarget& _target, smtutil::Expression con
_target.type == VerificationTarget::Type::UnderOverflow,
""
);
auto intType = dynamic_cast<IntegerType const*>(_target.expression->annotation().type);
IntegerType const* intType = nullptr;
if (auto const* type = dynamic_cast<IntegerType const*>(_target.expression->annotation().type))
intType = type;
else
intType = TypeProvider::uint256();
solAssert(intType, "");
checkCondition(
_target.constraints && _constraints && _target.value < smt::minValue(*intType),
@ -626,7 +630,12 @@ void BMC::checkOverflow(BMCVerificationTarget& _target, smtutil::Expression cons
_target.type == VerificationTarget::Type::UnderOverflow,
""
);
auto intType = dynamic_cast<IntegerType const*>(_target.expression->annotation().type);
IntegerType const* intType = nullptr;
if (auto const* type = dynamic_cast<IntegerType const*>(_target.expression->annotation().type))
intType = type;
else
intType = TypeProvider::uint256();
solAssert(intType, "");
checkCondition(
_target.constraints && _constraints && _target.value > smt::maxValue(*intType),

View File

@ -385,46 +385,24 @@ void SMTEncoder::endVisit(Assignment const& _assignment)
}
else
{
auto const& type = _assignment.annotation().type;
vector<smtutil::Expression> rightArguments;
if (auto const* tupleTypeRight = dynamic_cast<TupleType const*>(_assignment.rightHandSide().annotation().type))
{
auto symbTupleLeft = dynamic_pointer_cast<smt::SymbolicTupleVariable>(m_context.expression(_assignment.leftHandSide()));
solAssert(symbTupleLeft, "");
auto symbTupleRight = dynamic_pointer_cast<smt::SymbolicTupleVariable>(m_context.expression(_assignment.rightHandSide()));
solAssert(symbTupleRight, "");
auto const& leftComponents = symbTupleLeft->components();
auto const& rightComponents = symbTupleRight->components();
solAssert(leftComponents.size() == rightComponents.size(), "");
auto tupleTypeLeft = dynamic_cast<TupleType const*>(_assignment.leftHandSide().annotation().type);
solAssert(tupleTypeLeft, "");
solAssert(tupleTypeLeft->components().size() == leftComponents.size(), "");
auto const& typesLeft = tupleTypeLeft->components();
solAssert(tupleTypeRight->components().size() == rightComponents.size(), "");
auto const& typesRight = tupleTypeRight->components();
for (unsigned i = 0; i < rightComponents.size(); ++i)
rightArguments.push_back(symbTupleRight->component(i, typesRight.at(i), typesLeft.at(i)));
}
if (dynamic_cast<TupleType const*>(_assignment.rightHandSide().annotation().type))
tupleAssignment(_assignment.leftHandSide(), _assignment.rightHandSide());
else
{
auto const& type = _assignment.annotation().type;
auto rightHandSide = compoundOps.count(op) ?
compoundAssignment(_assignment) :
expr(_assignment.rightHandSide(), type);
defineExpr(_assignment, rightHandSide);
rightArguments.push_back(expr(_assignment, type));
}
assignment(
_assignment.leftHandSide(),
rightArguments,
expr(_assignment, type),
type,
_assignment.location()
);
}
}
}
void SMTEncoder::endVisit(TupleExpression const& _tuple)
{
@ -1023,38 +1001,7 @@ void SMTEncoder::arrayIndexAssignment(Expression const& _expr, smtutil::Expressi
solAssert(varDecl, "");
if (varDecl->hasReferenceOrMappingType())
m_context.resetVariables([&](VariableDeclaration const& _var) {
if (_var == *varDecl)
return false;
// If both are state variables no need to clear knowledge.
if (_var.isStateVariable() && varDecl->isStateVariable())
return false;
TypePointer prefix = _var.type();
TypePointer originalType = typeWithoutPointer(varDecl->type());
while (
prefix->category() == Type::Category::Mapping ||
prefix->category() == Type::Category::Array
)
{
if (*originalType == *typeWithoutPointer(prefix))
return true;
if (prefix->category() == Type::Category::Mapping)
{
auto mapPrefix = dynamic_cast<MappingType const*>(prefix);
solAssert(mapPrefix, "");
prefix = mapPrefix->valueType();
}
else
{
auto arrayPrefix = dynamic_cast<ArrayType const*>(prefix);
solAssert(arrayPrefix, "");
prefix = arrayPrefix->baseType();
}
}
return false;
});
resetReferences(*varDecl);
auto symbArray = dynamic_pointer_cast<smt::SymbolicArrayVariable>(m_context.variable(*varDecl));
smtutil::Expression store = smtutil::Expression::store(
@ -1160,6 +1107,8 @@ void SMTEncoder::arrayPushPopAssign(Expression const& _expr, smtutil::Expression
{
auto varDecl = identifierToVariable(*id);
solAssert(varDecl, "");
if (varDecl->hasReferenceOrMappingType())
resetReferences(*varDecl);
m_context.addAssertion(m_context.newValue(*varDecl) == _array);
}
else if (auto const* indexAccess = dynamic_cast<IndexAccess const*>(&_expr))
@ -1213,7 +1162,7 @@ void SMTEncoder::arithmeticOperation(BinaryOperation const& _op)
{
auto type = _op.annotation().commonType;
solAssert(type, "");
if (type->category() == Type::Category::Integer)
if (type->category() == Type::Category::Integer || type->category() == Type::Category::FixedPoint)
{
switch (_op.getOperator())
{
@ -1266,13 +1215,21 @@ pair<smtutil::Expression, smtutil::Expression> SMTEncoder::arithmeticOperation(
};
solAssert(validOperators.count(_op), "");
solAssert(_commonType, "");
solAssert(_commonType->category() == Type::Category::Integer, "");
solAssert(
_commonType->category() == Type::Category::Integer || _commonType->category() == Type::Category::FixedPoint,
""
);
IntegerType const* intType = nullptr;
if (auto type = dynamic_cast<IntegerType const*>(_commonType))
intType = type;
else
intType = TypeProvider::uint256();
auto const& intType = dynamic_cast<IntegerType const&>(*_commonType);
smtutil::Expression valueNoMod(
_op == Token::Add ? _left + _right :
_op == Token::Sub ? _left - _right :
_op == Token::Div ? division(_left, _right, intType) :
_op == Token::Div ? division(_left, _right, *intType) :
_op == Token::Mul ? _left * _right :
/*_op == Token::Mod*/ _left % _right
);
@ -1280,20 +1237,23 @@ pair<smtutil::Expression, smtutil::Expression> SMTEncoder::arithmeticOperation(
if (_op == Token::Div || _op == Token::Mod)
m_context.addAssertion(_right != 0);
smtutil::Expression intValueRange = (0 - smt::minValue(intType)) + smt::maxValue(intType) + 1;
auto symbMin = smt::minValue(*intType);
auto symbMax = smt::maxValue(*intType);
smtutil::Expression intValueRange = (0 - symbMin) + symbMax + 1;
auto value = smtutil::Expression::ite(
valueNoMod > smt::maxValue(intType),
valueNoMod > symbMax,
valueNoMod % intValueRange,
smtutil::Expression::ite(
valueNoMod < smt::minValue(intType),
valueNoMod < symbMin,
valueNoMod % intValueRange,
valueNoMod
)
);
if (intType.isSigned())
if (intType->isSigned())
value = smtutil::Expression::ite(
value > smt::maxValue(intType),
value > symbMax,
value - intValueRange,
value
);
@ -1422,11 +1382,16 @@ smtutil::Expression SMTEncoder::division(smtutil::Expression _left, smtutil::Exp
void SMTEncoder::assignment(
Expression const& _left,
vector<smtutil::Expression> const& _right,
smtutil::Expression const& _right,
TypePointer const& _type,
langutil::SourceLocation const& _location
)
{
solAssert(
_left.annotation().type->category() != Type::Category::Tuple,
"Tuple assignments should be handled by tupleAssignment."
);
if (!smt::isSupportedType(_type->category()))
{
// Give it a new index anyway to keep the SSA scheme sound.
@ -1440,26 +1405,9 @@ void SMTEncoder::assignment(
);
}
else if (auto varDecl = identifierToVariable(_left))
{
solAssert(_right.size() == 1, "");
assignment(*varDecl, _right.front());
}
assignment(*varDecl, _right);
else if (dynamic_cast<IndexAccess const*>(&_left))
{
solAssert(_right.size() == 1, "");
arrayIndexAssignment(_left, _right.front());
}
else if (auto tuple = dynamic_cast<TupleExpression const*>(&_left))
{
auto const& components = tuple->components();
if (!_right.empty())
{
solAssert(_right.size() == components.size(), "");
for (unsigned i = 0; i < _right.size(); ++i)
if (auto component = components.at(i))
assignment(*component, {_right.at(i)}, component->annotation().type, component->location());
}
}
arrayIndexAssignment(_left, _right);
else
m_errorReporter.warning(
8182_error,
@ -1468,6 +1416,52 @@ void SMTEncoder::assignment(
);
}
void SMTEncoder::tupleAssignment(Expression const& _left, Expression const& _right)
{
auto lTuple = dynamic_cast<TupleExpression const*>(&_left);
solAssert(lTuple, "");
auto const& lComponents = lTuple->components();
// If both sides are tuple expressions, we individually and potentially
// recursively assign each pair of components.
// This is because of potential type conversion.
if (auto rTuple = dynamic_cast<TupleExpression const*>(&_right))
{
auto const& rComponents = rTuple->components();
solAssert(lComponents.size() == rComponents.size(), "");
for (unsigned i = 0; i < lComponents.size(); ++i)
{
if (!lComponents.at(i) || !rComponents.at(i))
continue;
auto const& lExpr = *lComponents.at(i);
auto const& rExpr = *rComponents.at(i);
if (lExpr.annotation().type->category() == Type::Category::Tuple)
tupleAssignment(lExpr, rExpr);
else
{
auto type = lExpr.annotation().type;
assignment(lExpr, expr(rExpr, type), type, lExpr.location());
}
}
}
else
{
auto rType = dynamic_cast<TupleType const*>(_right.annotation().type);
solAssert(rType, "");
auto const& rComponents = rType->components();
solAssert(lComponents.size() == rComponents.size(), "");
auto symbRight = expr(_right);
solAssert(symbRight.sort->kind == smtutil::Kind::Tuple, "");
for (unsigned i = 0; i < lComponents.size(); ++i)
if (auto component = lComponents.at(i); component && rComponents.at(i))
assignment(*component, smtutil::Expression::tuple_get(symbRight, i), component->annotation().type, component->location());
}
}
smtutil::Expression SMTEncoder::compoundAssignment(Assignment const& _assignment)
{
static map<Token, Token> const compoundToArithmetic{
@ -1618,6 +1612,42 @@ void SMTEncoder::resetStateVariables()
m_context.resetVariables([&](VariableDeclaration const& _variable) { return _variable.isStateVariable(); });
}
void SMTEncoder::resetReferences(VariableDeclaration const& _varDecl)
{
m_context.resetVariables([&](VariableDeclaration const& _var) {
if (_var == _varDecl)
return false;
// If both are state variables no need to clear knowledge.
if (_var.isStateVariable() && _varDecl.isStateVariable())
return false;
TypePointer prefix = _var.type();
TypePointer originalType = typeWithoutPointer(_varDecl.type());
while (
prefix->category() == Type::Category::Mapping ||
prefix->category() == Type::Category::Array
)
{
if (*originalType == *typeWithoutPointer(prefix))
return true;
if (prefix->category() == Type::Category::Mapping)
{
auto mapPrefix = dynamic_cast<MappingType const*>(prefix);
solAssert(mapPrefix, "");
prefix = mapPrefix->valueType();
}
else
{
auto arrayPrefix = dynamic_cast<ArrayType const*>(prefix);
solAssert(arrayPrefix, "");
prefix = arrayPrefix->baseType();
}
}
return false;
});
}
TypePointer SMTEncoder::typeWithoutPointer(TypePointer const& _type)
{
if (auto refType = dynamic_cast<ReferenceType const*>(_type))

View File

@ -157,10 +157,12 @@ protected:
/// Will also be used for assignments of tuple components.
void assignment(
Expression const& _left,
std::vector<smtutil::Expression> const& _right,
smtutil::Expression const& _right,
TypePointer const& _type,
langutil::SourceLocation const& _location
);
/// Handle assignments between tuples.
void tupleAssignment(Expression const& _left, Expression const& _right);
/// Computes the right hand side of a compound assignment.
smtutil::Expression compoundAssignment(Assignment const& _assignment);
@ -181,6 +183,9 @@ protected:
void initializeLocalVariables(FunctionDefinition const& _function);
void initializeFunctionCallParameters(CallableDeclaration const& _function, std::vector<smtutil::Expression> const& _callArgs);
void resetStateVariables();
/// Resets all references/pointers that have the same type or have
/// a subexpression of the same type as _varDecl.
void resetReferences(VariableDeclaration const& _varDecl);
/// @returns the type without storage pointer information if it has it.
TypePointer typeWithoutPointer(TypePointer const& _type);

View File

@ -53,9 +53,6 @@ string solidity::util::toHex(uint8_t _data, HexCase _case)
string solidity::util::toHex(bytes const& _data, HexPrefix _prefix, HexCase _case)
{
if (_data.empty())
return {};
std::string ret(_data.size() * 2 + (_prefix == HexPrefix::Add ? 2 : 0), 0);
size_t i = 0;

View File

@ -359,6 +359,13 @@ void AsmAnalyzer::operator()(Switch const& _switch)
{
yulAssert(_switch.expression, "");
if (_switch.cases.size() == 1 && !_switch.cases[0].value)
m_errorReporter.warning(
9592_error,
_switch.location,
"\"switch\" statement with only a default case."
);
YulString valueType = expectExpression(*_switch.expression);
set<u256> cases;

View File

@ -24,6 +24,7 @@
#include <libsolutil/CommonData.h>
#include <boost/range/adaptor/reversed.hpp>
#include <boost/range/adaptor/map.hpp>
using namespace std;
using namespace solidity;
@ -130,7 +131,7 @@ bytes toBytes(Opcode _o)
return toBytes(uint8_t(_o));
}
static std::map<string, uint8_t> const builtins = {
static map<string, uint8_t> const builtins = {
{"i32.load", 0x28},
{"i64.load", 0x29},
{"i32.load8_s", 0x2c},
@ -240,46 +241,56 @@ bytes lebEncodeSigned(int64_t _n)
bytes prefixSize(bytes _data)
{
size_t size = _data.size();
return lebEncode(size) + std::move(_data);
return lebEncode(size) + move(_data);
}
bytes makeSection(Section _section, bytes _data)
{
return toBytes(_section) + prefixSize(std::move(_data));
return toBytes(_section) + prefixSize(move(_data));
}
}
bytes BinaryTransform::run(Module const& _module)
{
BinaryTransform bt;
map<Type, vector<string>> const types = typeToFunctionMap(_module.imports, _module.functions);
for (size_t i = 0; i < _module.globals.size(); ++i)
bt.m_globals[_module.globals[i].variableName] = i;
map<string, size_t> const globalIDs = enumerateGlobals(_module);
map<string, size_t> const functionIDs = enumerateFunctions(_module);
map<string, size_t> const functionTypes = enumerateFunctionTypes(types);
size_t funID = 0;
for (FunctionImport const& fun: _module.imports)
bt.m_functions[fun.internalName] = funID++;
for (FunctionDefinition const& fun: _module.functions)
bt.m_functions[fun.name] = funID++;
yulAssert(globalIDs.size() == _module.globals.size(), "");
yulAssert(functionIDs.size() == _module.imports.size() + _module.functions.size(), "");
yulAssert(functionTypes.size() == functionIDs.size(), "");
yulAssert(functionTypes.size() >= types.size(), "");
bytes ret{0, 'a', 's', 'm'};
// version
ret += bytes{1, 0, 0, 0};
ret += bt.typeSection(_module.imports, _module.functions);
ret += bt.importSection(_module.imports);
ret += bt.functionSection(_module.functions);
ret += bt.memorySection();
ret += bt.globalSection();
ret += bt.exportSection();
ret += typeSection(types);
ret += importSection(_module.imports, functionTypes);
ret += functionSection(_module.functions, functionTypes);
ret += memorySection();
ret += globalSection(_module.globals);
ret += exportSection(functionIDs);
map<string, pair<size_t, size_t>> subModulePosAndSize;
for (auto const& sub: _module.subModules)
{
// TODO should we prefix and / or shorten the name?
bytes data = BinaryTransform::run(sub.second);
size_t length = data.size();
ret += bt.customSection(sub.first, std::move(data));
bt.m_subModulePosAndSize[sub.first] = {ret.size() - length, length};
ret += customSection(sub.first, move(data));
subModulePosAndSize[sub.first] = {ret.size() - length, length};
}
BinaryTransform bt(
move(globalIDs),
move(functionIDs),
move(functionTypes),
move(subModulePosAndSize)
);
ret += bt.codeSection(_module.functions);
return ret;
}
@ -302,7 +313,7 @@ bytes BinaryTransform::operator()(LocalVariable const& _variable)
bytes BinaryTransform::operator()(GlobalVariable const& _variable)
{
return toBytes(Opcode::GlobalGet) + lebEncode(m_globals.at(_variable.name));
return toBytes(Opcode::GlobalGet) + lebEncode(m_globalIDs.at(_variable.name));
}
bytes BinaryTransform::operator()(BuiltinCall const& _call)
@ -311,12 +322,12 @@ bytes BinaryTransform::operator()(BuiltinCall const& _call)
// they are references to object names that should not end up in the code.
if (_call.functionName == "dataoffset")
{
string name = std::get<StringLiteral>(_call.arguments.at(0)).value;
string name = get<StringLiteral>(_call.arguments.at(0)).value;
return toBytes(Opcode::I64Const) + lebEncodeSigned(m_subModulePosAndSize.at(name).first);
}
else if (_call.functionName == "datasize")
{
string name = std::get<StringLiteral>(_call.arguments.at(0)).value;
string name = get<StringLiteral>(_call.arguments.at(0)).value;
return toBytes(Opcode::I64Const) + lebEncodeSigned(m_subModulePosAndSize.at(name).second);
}
@ -331,20 +342,24 @@ bytes BinaryTransform::operator()(BuiltinCall const& _call)
else
{
yulAssert(builtins.count(_call.functionName), "Builtin " + _call.functionName + " not found");
bytes ret = std::move(args) + toBytes(builtins.at(_call.functionName));
bytes ret = move(args) + toBytes(builtins.at(_call.functionName));
if (
_call.functionName.find(".load") != string::npos ||
_call.functionName.find(".store") != string::npos
)
// alignment and offset
ret += bytes{{3, 0}};
// Alignment hint and offset. Interpreters ignore the alignment. JITs/AOTs can take it
// into account to generate more efficient code but if the hint is invalid it could
// actually be more expensive. It's best to hint at 1-byte alignment if we don't plan
// to control the memory layout accordingly.
ret += bytes{{0, 0}}; // 2^0 == 1-byte alignment
return ret;
}
}
bytes BinaryTransform::operator()(FunctionCall const& _call)
{
return visit(_call.arguments) + toBytes(Opcode::Call) + lebEncode(m_functions.at(_call.functionName));
return visit(_call.arguments) + toBytes(Opcode::Call) + lebEncode(m_functionIDs.at(_call.functionName));
}
bytes BinaryTransform::operator()(LocalAssignment const& _assignment)
@ -360,7 +375,7 @@ bytes BinaryTransform::operator()(GlobalAssignment const& _assignment)
return
std::visit(*this, *_assignment.value) +
toBytes(Opcode::GlobalSet) +
lebEncode(m_globals.at(_assignment.variableName));
lebEncode(m_globalIDs.at(_assignment.variableName));
}
bytes BinaryTransform::operator()(If const& _if)
@ -430,9 +445,14 @@ bytes BinaryTransform::operator()(FunctionDefinition const& _function)
// This is a kind of run-length-encoding of local types. Has to be adapted once
// we have locals of different types.
if (_function.locals.size() == 0)
ret += lebEncode(0); // number of locals groups
else
{
ret += lebEncode(1); // number of locals groups
ret += lebEncode(_function.locals.size());
ret += toBytes(ValueType::I64);
}
m_locals.clear();
size_t varIdx = 0;
@ -448,7 +468,7 @@ bytes BinaryTransform::operator()(FunctionDefinition const& _function)
yulAssert(m_labels.empty(), "Stray labels.");
return prefixSize(std::move(ret));
return prefixSize(move(ret));
}
BinaryTransform::Type BinaryTransform::typeOf(FunctionImport const& _import)
@ -487,9 +507,9 @@ vector<uint8_t> BinaryTransform::encodeTypes(vector<string> const& _typeNames)
return result;
}
bytes BinaryTransform::typeSection(
vector<FunctionImport> const& _imports,
vector<FunctionDefinition> const& _functions
map<BinaryTransform::Type, vector<string>> BinaryTransform::typeToFunctionMap(
vector<wasm::FunctionImport> const& _imports,
vector<wasm::FunctionDefinition> const& _functions
)
{
map<Type, vector<string>> types;
@ -498,12 +518,50 @@ bytes BinaryTransform::typeSection(
for (auto const& fun: _functions)
types[typeOf(fun)].emplace_back(fun.name);
bytes result;
size_t index = 0;
for (auto const& [type, funNames]: types)
return types;
}
map<string, size_t> BinaryTransform::enumerateGlobals(Module const& _module)
{
map<string, size_t> globals;
for (size_t i = 0; i < _module.globals.size(); ++i)
globals[_module.globals[i].variableName] = i;
return globals;
}
map<string, size_t> BinaryTransform::enumerateFunctions(Module const& _module)
{
map<string, size_t> functions;
size_t funID = 0;
for (FunctionImport const& fun: _module.imports)
functions[fun.internalName] = funID++;
for (FunctionDefinition const& fun: _module.functions)
functions[fun.name] = funID++;
return functions;
}
map<string, size_t> BinaryTransform::enumerateFunctionTypes(map<Type, vector<string>> const& _typeToFunctionMap)
{
map<string, size_t> functionTypes;
size_t typeID = 0;
for (vector<string> const& funNames: _typeToFunctionMap | boost::adaptors::map_values)
{
for (string const& name: funNames)
m_functionTypes[name] = index;
functionTypes[name] = typeID;
++typeID;
}
return functionTypes;
}
bytes BinaryTransform::typeSection(map<BinaryTransform::Type, vector<string>> const& _typeToFunctionMap)
{
bytes result;
size_t index = 0;
for (Type const& type: _typeToFunctionMap | boost::adaptors::map_keys)
{
result += toBytes(ValueType::Function);
result += lebEncode(type.first.size()) + type.first;
result += lebEncode(type.second.size()) + type.second;
@ -511,11 +569,12 @@ bytes BinaryTransform::typeSection(
index++;
}
return makeSection(Section::TYPE, lebEncode(index) + std::move(result));
return makeSection(Section::TYPE, lebEncode(index) + move(result));
}
bytes BinaryTransform::importSection(
vector<FunctionImport> const& _imports
vector<FunctionImport> const& _imports,
map<string, size_t> const& _functionTypes
)
{
bytes result = lebEncode(_imports.size());
@ -526,17 +585,20 @@ bytes BinaryTransform::importSection(
encodeName(import.module) +
encodeName(import.externalName) +
toBytes(importKind) +
lebEncode(m_functionTypes[import.internalName]);
lebEncode(_functionTypes.at(import.internalName));
}
return makeSection(Section::IMPORT, std::move(result));
return makeSection(Section::IMPORT, move(result));
}
bytes BinaryTransform::functionSection(vector<FunctionDefinition> const& _functions)
bytes BinaryTransform::functionSection(
vector<FunctionDefinition> const& _functions,
map<string, size_t> const& _functionTypes
)
{
bytes result = lebEncode(_functions.size());
for (auto const& fun: _functions)
result += lebEncode(m_functionTypes.at(fun.name));
return makeSection(Section::FUNCTION, std::move(result));
result += lebEncode(_functionTypes.at(fun.name));
return makeSection(Section::FUNCTION, move(result));
}
bytes BinaryTransform::memorySection()
@ -544,13 +606,13 @@ bytes BinaryTransform::memorySection()
bytes result = lebEncode(1);
result.push_back(static_cast<uint8_t>(LimitsKind::Min));
result.push_back(1); // initial length
return makeSection(Section::MEMORY, std::move(result));
return makeSection(Section::MEMORY, move(result));
}
bytes BinaryTransform::globalSection()
bytes BinaryTransform::globalSection(vector<wasm::GlobalVariableDeclaration> const& _globals)
{
bytes result = lebEncode(m_globals.size());
for (size_t i = 0; i < m_globals.size(); ++i)
bytes result = lebEncode(_globals.size());
for (size_t i = 0; i < _globals.size(); ++i)
result +=
toBytes(ValueType::I64) +
lebEncode(static_cast<uint8_t>(Mutability::Var)) +
@ -558,21 +620,21 @@ bytes BinaryTransform::globalSection()
lebEncodeSigned(0) +
toBytes(Opcode::End);
return makeSection(Section::GLOBAL, std::move(result));
return makeSection(Section::GLOBAL, move(result));
}
bytes BinaryTransform::exportSection()
bytes BinaryTransform::exportSection(map<string, size_t> const& _functionIDs)
{
bytes result = lebEncode(2);
result += encodeName("memory") + toBytes(Export::Memory) + lebEncode(0);
result += encodeName("main") + toBytes(Export::Function) + lebEncode(m_functions.at("main"));
return makeSection(Section::EXPORT, std::move(result));
result += encodeName("main") + toBytes(Export::Function) + lebEncode(_functionIDs.at("main"));
return makeSection(Section::EXPORT, move(result));
}
bytes BinaryTransform::customSection(string const& _name, bytes _data)
{
bytes result = encodeName(_name) + std::move(_data);
return makeSection(Section::CUSTOM, std::move(result));
bytes result = encodeName(_name) + move(_data);
return makeSection(Section::CUSTOM, move(result));
}
bytes BinaryTransform::codeSection(vector<wasm::FunctionDefinition> const& _functions)
@ -580,7 +642,7 @@ bytes BinaryTransform::codeSection(vector<wasm::FunctionDefinition> const& _func
bytes result = lebEncode(_functions.size());
for (FunctionDefinition const& fun: _functions)
result += (*this)(fun);
return makeSection(Section::CODE, std::move(result));
return makeSection(Section::CODE, move(result));
}
bytes BinaryTransform::visit(vector<Expression> const& _expressions)
@ -611,7 +673,7 @@ bytes BinaryTransform::encodeLabelIdx(string const& _label) const
yulAssert(false, "Label not found.");
}
bytes BinaryTransform::encodeName(std::string const& _name)
bytes BinaryTransform::encodeName(string const& _name)
{
// UTF-8 is allowed here by the Wasm spec, but since all names here should stem from
// Solidity or Yul identifiers or similar, non-ascii characters ending up here

View File

@ -55,23 +55,49 @@ public:
bytes operator()(wasm::FunctionDefinition const& _function);
private:
BinaryTransform(
std::map<std::string, size_t> _globalIDs,
std::map<std::string, size_t> _functionIDs,
std::map<std::string, size_t> _functionTypes,
std::map<std::string, std::pair<size_t, size_t>> _subModulePosAndSize
):
m_globalIDs(std::move(_globalIDs)),
m_functionIDs(std::move(_functionIDs)),
m_functionTypes(std::move(_functionTypes)),
m_subModulePosAndSize(std::move(_subModulePosAndSize))
{}
using Type = std::pair<std::vector<std::uint8_t>, std::vector<std::uint8_t>>;
static Type typeOf(wasm::FunctionImport const& _import);
static Type typeOf(wasm::FunctionDefinition const& _funDef);
static uint8_t encodeType(std::string const& _typeName);
static std::vector<uint8_t> encodeTypes(std::vector<std::string> const& _typeNames);
bytes typeSection(
static std::map<Type, std::vector<std::string>> typeToFunctionMap(
std::vector<wasm::FunctionImport> const& _imports,
std::vector<wasm::FunctionDefinition> const& _functions
);
bytes importSection(std::vector<wasm::FunctionImport> const& _imports);
bytes functionSection(std::vector<wasm::FunctionDefinition> const& _functions);
bytes memorySection();
bytes globalSection();
bytes exportSection();
bytes customSection(std::string const& _name, bytes _data);
static std::map<std::string, size_t> enumerateGlobals(Module const& _module);
static std::map<std::string, size_t> enumerateFunctions(Module const& _module);
static std::map<std::string, size_t> enumerateFunctionTypes(
std::map<Type, std::vector<std::string>> const& _typeToFunctionMap
);
static bytes typeSection(std::map<Type, std::vector<std::string>> const& _typeToFunctionMap);
static bytes importSection(
std::vector<wasm::FunctionImport> const& _imports,
std::map<std::string, size_t> const& _functionTypes
);
static bytes functionSection(
std::vector<wasm::FunctionDefinition> const& _functions,
std::map<std::string, size_t> const& _functionTypes
);
static bytes memorySection();
static bytes globalSection(std::vector<wasm::GlobalVariableDeclaration> const& _globals);
static bytes exportSection(std::map<std::string, size_t> const& _functionIDs);
static bytes customSection(std::string const& _name, bytes _data);
bytes codeSection(std::vector<wasm::FunctionDefinition> const& _functions);
bytes visit(std::vector<wasm::Expression> const& _expressions);
@ -81,12 +107,13 @@ private:
static bytes encodeName(std::string const& _name);
std::map<std::string, size_t> const m_globalIDs;
std::map<std::string, size_t> const m_functionIDs;
std::map<std::string, size_t> const m_functionTypes;
std::map<std::string, std::pair<size_t, size_t>> const m_subModulePosAndSize;
std::map<std::string, size_t> m_locals;
std::map<std::string, size_t> m_globals;
std::map<std::string, size_t> m_functions;
std::map<std::string, size_t> m_functionTypes;
std::vector<std::string> m_labels;
std::map<std::string, std::pair<size_t, size_t>> m_subModulePosAndSize;
};
}

View File

@ -33,16 +33,16 @@ using namespace solidity::yul;
VarNameCleaner::VarNameCleaner(
Block const& _ast,
Dialect const& _dialect,
set<YulString> _blacklist
set<YulString> _namesToKeep
):
m_dialect{_dialect},
m_blacklist{std::move(_blacklist)},
m_namesToKeep{std::move(_namesToKeep)},
m_translatedNames{}
{
for (auto const& statement: _ast.statements)
if (holds_alternative<FunctionDefinition>(statement))
m_blacklist.insert(std::get<FunctionDefinition>(statement).name);
m_usedNames = m_blacklist;
m_namesToKeep.insert(std::get<FunctionDefinition>(statement).name);
m_usedNames = m_namesToKeep;
}
void VarNameCleaner::operator()(FunctionDefinition& _funDef)
@ -51,7 +51,7 @@ void VarNameCleaner::operator()(FunctionDefinition& _funDef)
m_insideFunction = true;
set<YulString> globalUsedNames = std::move(m_usedNames);
m_usedNames = m_blacklist;
m_usedNames = m_namesToKeep;
map<YulString, YulString> globalTranslatedNames;
swap(globalTranslatedNames, m_translatedNames);

View File

@ -63,7 +63,7 @@ private:
VarNameCleaner(
Block const& _ast,
Dialect const& _dialect,
std::set<YulString> _blacklist = {}
std::set<YulString> _namesToKeep = {}
);
/// Tries to rename a list of variables.
@ -77,11 +77,13 @@ private:
YulString findCleanName(YulString const& name) const;
/// Tests whether a given name was already used within this pass
/// or is on the blacklist.
/// or was set to be kept.
bool isUsedName(YulString const& _name) const;
Dialect const& m_dialect;
std::set<YulString> m_blacklist;
/// These names will not be modified.
std::set<YulString> m_namesToKeep;
/// Set of names that are in use.
std::set<YulString> m_usedNames;

View File

@ -54,6 +54,10 @@ cmake \
-DTESTS=0 \
..
make -j 4 soljson
# Patch soljson.js for backwards compatibility.
# TODO: remove this with 0.7.
# "viiiii" encodes the signature of the callback function.
sed -i -e 's/addFunction(func,sig){/addFunction(func,sig){sig=sig||"viiiii";/' libsolc/soljson.js
cd ..
mkdir -p upload

View File

@ -740,7 +740,7 @@ remap paths using the context:prefix=path syntax.
Example:
solc --)" + g_argBinary + R"( -o /tmp/solcoutput dapp-bin=/usr/local/lib/dapp-bin contract.sol
Allowed options)").c_str(),
General Information)").c_str(),
po::options_description::m_default_line_length,
po::options_description::m_default_line_length - 23
);
@ -748,47 +748,67 @@ Allowed options)").c_str(),
(g_argHelp.c_str(), "Show help message and exit.")
(g_argVersion.c_str(), "Show version and exit.")
(g_strLicense.c_str(), "Show licensing information and exit.")
;
po::options_description inputOptions("Input Options");
inputOptions.add_options()
(
g_argBasePath.c_str(),
po::value<string>()->value_name("path"),
"Use the given path as the root of the source tree instead of the root of the filesystem."
)
(
g_argAllowPaths.c_str(),
po::value<string>()->value_name("path(s)"),
"Allow a given path for imports. A list of paths can be supplied by separating them with a comma."
)
(
g_argIgnoreMissingFiles.c_str(),
"Ignore missing files."
)
(
g_argErrorRecovery.c_str(),
"Enables additional parser error recovery."
)
;
desc.add(inputOptions);
po::options_description outputOptions("Output Options");
outputOptions.add_options()
(
(g_argOutputDir + ",o").c_str(),
po::value<string>()->value_name("path"),
"If given, creates one file per component and contract/file at the specified directory."
)
(
g_strOverwrite.c_str(),
"Overwrite existing files (used together with -o)."
)
(
g_strEVMVersion.c_str(),
po::value<string>()->value_name("version"),
"Select desired EVM version. Either homestead, tangerineWhistle, spuriousDragon, "
"byzantium, constantinople, petersburg, istanbul (default) or berlin."
)
(g_argPrettyJson.c_str(), "Output JSON in pretty format. Currently it only works with the combined JSON output.")
(
g_argLibraries.c_str(),
po::value<vector<string>>()->value_name("libs"),
"Direct string or file containing library addresses. Syntax: "
"<libraryName>:<address> [, or whitespace] ...\n"
"Address is interpreted as a hex string optionally prefixed by 0x."
)
(
g_strRevertStrings.c_str(),
po::value<string>()->value_name(boost::join(g_revertStringsArgs, ",")),
"Strip revert (and require) reason strings or add additional debugging information."
)
(
(g_argOutputDir + ",o").c_str(),
po::value<string>()->value_name("path"),
"If given, creates one file per component and contract/file at the specified directory."
)
(g_strOverwrite.c_str(), "Overwrite existing files (used together with -o).")
(
g_argCombinedJson.c_str(),
po::value<string>()->value_name(boost::join(g_combinedJsonArgs, ",")),
"Output a single json document containing the specified information."
)
(g_argGas.c_str(), "Print an estimate of the maximal gas usage for each function.")
;
desc.add(outputOptions);
po::options_description alternativeInputModes("Alternative Input Modes");
alternativeInputModes.add_options()
(
g_argStandardJSON.c_str(),
"Switch to Standard JSON input / output mode, ignoring all options. "
"It reads from standard input, if no input file was given, otherwise it reads from the provided input file. The result will be written to standard output."
)
(
g_argImportAst.c_str(),
("Import ASTs to be compiled, assumes input holds the AST in compact JSON format. "
"Supported Inputs is the output of the --" + g_argStandardJSON + " or the one produced by "
"--" + g_argCombinedJson + " " + g_strAst + "," + g_strCompactJSON).c_str()
g_argLink.c_str(),
("Switch to linker mode, ignoring all options apart from --" + g_argLibraries + " "
"and modify binaries in place.").c_str()
)
(
g_argAssemble.c_str(),
@ -809,58 +829,62 @@ Allowed options)").c_str(),
"and assumes input is strict assembly.").c_str()
)
(
g_strYulDialect.c_str(),
po::value<string>()->value_name(boost::join(g_yulDialectArgs, ",")),
"Input dialect to use in assembly or yul mode."
g_argImportAst.c_str(),
("Import ASTs to be compiled, assumes input holds the AST in compact JSON format. "
"Supported Inputs is the output of the --" + g_argStandardJSON + " or the one produced by "
"--" + g_argCombinedJson + " " + g_strAst + "," + g_strCompactJSON).c_str()
)
;
desc.add(alternativeInputModes);
po::options_description assemblyModeOptions("Assembly Mode Options");
assemblyModeOptions.add_options()
(
g_argMachine.c_str(),
po::value<string>()->value_name(boost::join(g_machineArgs, ",")),
"Target machine in assembly or Yul mode."
)
(
g_argLink.c_str(),
("Switch to linker mode, ignoring all options apart from --" + g_argLibraries + " "
"and modify binaries in place.").c_str()
g_strYulDialect.c_str(),
po::value<string>()->value_name(boost::join(g_yulDialectArgs, ",")),
"Input dialect to use in assembly or yul mode."
)
;
desc.add(assemblyModeOptions);
po::options_description linkerModeOptions("Linker Mode Options");
linkerModeOptions.add_options()
(
g_argLibraries.c_str(),
po::value<vector<string>>()->value_name("libs"),
"Direct string or file containing library addresses. Syntax: "
"<libraryName>:<address> [, or whitespace] ...\n"
"Address is interpreted as a hex string optionally prefixed by 0x."
)
;
desc.add(linkerModeOptions);
po::options_description outputFormatting("Output Formatting");
outputFormatting.add_options()
(
g_argPrettyJson.c_str(),
"Output JSON in pretty format. Currently it only works with the combined JSON output."
)
(
g_argMetadataHash.c_str(),
po::value<string>()->value_name(boost::join(g_metadataHashArgs, ",")),
"Choose hash method for the bytecode metadata or disable it."
)
(g_argMetadataLiteral.c_str(), "Store referenced sources as literal data in the metadata output.")
(
g_argAllowPaths.c_str(),
po::value<string>()->value_name("path(s)"),
"Allow a given path for imports. A list of paths can be supplied by separating them with a comma."
g_argColor.c_str(),
"Force colored output."
)
(
g_argBasePath.c_str(),
po::value<string>()->value_name("path"),
"Use the given path as the root of the source tree instead of the root of the filesystem."
g_argNoColor.c_str(),
"Explicitly disable colored output, disabling terminal auto-detection."
)
(g_argColor.c_str(), "Force colored output.")
(g_argNoColor.c_str(), "Explicitly disable colored output, disabling terminal auto-detection.")
(g_argOldReporter.c_str(), "Enables old diagnostics reporter.")
(g_argErrorRecovery.c_str(), "Enables additional parser error recovery.")
(g_argIgnoreMissingFiles.c_str(), "Ignore missing files.");
po::options_description optimizerOptions("Optimizer options");
optimizerOptions.add_options()
(g_argOptimize.c_str(), "Enable bytecode optimizer.")
(
g_argOptimizeRuns.c_str(),
po::value<unsigned>()->value_name("n")->default_value(200),
"Set for how many contract runs to optimize. "
"Lower values will optimize more for initial deployment cost, higher values will optimize more for high-frequency usage."
g_argOldReporter.c_str(),
"Enables old diagnostics reporter (legacy option, will be removed)."
)
(g_strOptimizeYul.c_str(), ("Legacy option, ignored. Use the general --" + g_argOptimize + " to enable Yul optimizer.").c_str())
(g_strNoOptimizeYul.c_str(), "Disable Yul optimizer in Solidity.")
(
g_strYulOptimizations.c_str(),
po::value<string>()->value_name("steps"),
"Forces yul optimizer to use the specified sequence of optimization steps instead of the built-in one."
);
desc.add(optimizerOptions);
;
desc.add(outputFormatting);
po::options_description outputComponents("Output Components");
outputComponents.add_options()
(g_argAstJson.c_str(), "AST of all source files in JSON format.")
@ -878,9 +902,66 @@ Allowed options)").c_str(),
(g_argNatspecUser.c_str(), "Natspec user documentation of all contracts.")
(g_argNatspecDev.c_str(), "Natspec developer documentation of all contracts.")
(g_argMetadata.c_str(), "Combined Metadata JSON whose Swarm hash is stored on-chain.")
(g_argStorageLayout.c_str(), "Slots, offsets and types of the contract's state variables.");
(g_argStorageLayout.c_str(), "Slots, offsets and types of the contract's state variables.")
;
desc.add(outputComponents);
po::options_description extraOutput("Extra Output");
extraOutput.add_options()
(
g_argGas.c_str(),
"Print an estimate of the maximal gas usage for each function."
)
(
g_argCombinedJson.c_str(),
po::value<string>()->value_name(boost::join(g_combinedJsonArgs, ",")),
"Output a single json document containing the specified information."
)
;
desc.add(extraOutput);
po::options_description metadataOptions("Metadata Options");
metadataOptions.add_options()
(
g_argMetadataHash.c_str(),
po::value<string>()->value_name(boost::join(g_metadataHashArgs, ",")),
"Choose hash method for the bytecode metadata or disable it."
)
(
g_argMetadataLiteral.c_str(),
"Store referenced sources as literal data in the metadata output."
)
;
desc.add(metadataOptions);
po::options_description optimizerOptions("Optimizer Options");
optimizerOptions.add_options()
(
g_argOptimize.c_str(),
"Enable bytecode optimizer."
)
(
g_argOptimizeRuns.c_str(),
po::value<unsigned>()->value_name("n")->default_value(200),
"Set for how many contract runs to optimize. "
"Lower values will optimize more for initial deployment cost, higher values will optimize more for high-frequency usage."
)
(
g_strOptimizeYul.c_str(),
("Legacy option, ignored. Use the general --" + g_argOptimize + " to enable Yul optimizer.").c_str()
)
(
g_strNoOptimizeYul.c_str(),
"Disable Yul optimizer in Solidity."
)
(
g_strYulOptimizations.c_str(),
po::value<string>()->value_name("steps"),
"Forces yul optimizer to use the specified sequence of optimization steps instead of the built-in one."
)
;
desc.add(optimizerOptions);
po::options_description allOptions = desc;
allOptions.add_options()(g_argInputFile.c_str(), po::value<vector<string>>(), "input file");

View File

@ -45,7 +45,7 @@ object "object" {
Binary representation:
0061736d0100000001160460000060017e017e60057e7e7e7e7e0060027f7f0002190108657468657265756d0c73746f7261676553746f7265000303060500010101020503010001060100071102066d656d6f72790200046d61696e00010acd01052b01017e0240420021004200200020002000200010054220200020002000420110054200a74220a710000b0b1f01017e024020004208864280fe0383200042088842ff01838421010b20010b1e01027e02402000100242108621022002200042108810028421010b20010b1e01027e02402000100342208621022002200042208810038421010b20010b4101007e02402000a7200110043703002000a74208a76aada7200210043703002000a74210a76aada7200310043703002000a74218a76aada7200410043703000b0b
0061736d0100000001160460000060017e017e60057e7e7e7e7e0060027f7f0002190108657468657265756d0c73746f7261676553746f7265000303060500010101020503010001060100071102066d656d6f72790200046d61696e00010acb01052b01017e0240420021004200200020002000200010054220200020002000420110054200a74220a710000b0b1f01017e024020004208864280fe0383200042088842ff01838421010b20010b1e01027e02402000100242108621022002200042108810028421010b20010b1e01027e02402000100342208621022002200042208810038421010b20010b3f0002402000a7200110043700002000a74208a76aada7200210043700002000a74210a76aada7200310043700002000a74218a76aada7200410043700000b0b
Text representation:
(module

View File

@ -154,7 +154,7 @@ object "object" {
Binary representation:
0061736d0100000001480a60000060017e017e60027e7e017e60037e7e7e017e60047e7e7e7e017e60057e7e7e7e7e0060087e7e7e7e7e7e7e7e0060087e7e7e7e7e7e7e7e017e60027f7f0060037f7f7f0002310208657468657265756d0c73746f7261676553746f7265000808657468657265756d0c63616c6c44617461436f70790009030e0d0003070407020704010101050605030100010610037e0142000b7e0142000b7e0142000b071102066d656d6f72790200046d61696e00020aa9090df302011f7e02404200210002402000200020002000100921012300210223012103230221040b2001210520022106200321072004210842012109200020008420002009848450ada745ada745ad210a02400340200aa745ad500d01024002402005200620072008200020002000420a1008210b2300210c2301210d2302210e0b0240200b200c200d200e1005210f2300211023012111230221120b200f20108420112012848450ada745ad42005204400c030b024020052006200720082000200020004202100621132300211423012115230221160b201320148420152016848450ada745ad42005204400c030b0240200520062007200820002000200042041006211723002118230121192302211a0b20172018842019201a848450ada745ad42005204400c010b0b0240200520062007200820002000200020091004211b2300211c2301211d2302211e0b201b2105201c2106201d2107201e21080c000b0b20002000200020002005200620072008100e0b0b2f01037e0240200020017c2105200520027c21032005200054ada72003200554ada772ada7ad21040b2004240020030b72010b7e0240200320077c210c200c42007c210b024020022006200c200354ada7200b200c54ada772ada7ad1003210d2300210e0b200d210a024020012005200e1003210f230021100b200f2109024020002004201010032111230021120b201121080b20092400200a2401200b240220080b2601047e0240200020018420022003848450ada7ad21070b20052400200624012007240220040b4901047e02402000200451ad42005204402001200551ad42005204402002200651ad42005204402003200751ad42005204404201210b0b0b0b0b0b20092400200a2401200b240220080b2d01027e024002402000200154ad21032003420151044042ffffffff0f2102052000200152ad21020b0b0b20020b960101087e02404200210c0240200020041007210d200d42005104400240200120051007210e200e42005104400240200220061007210f200f42005104402003200754ad210c05200f42015104404200210c054201210c0b0b0b05200e42015104404200210c054201210c0b0b0b05200d42015104404200210c054201210c0b0b0b200ca7ad210b0b20092400200a2401200b240220080b8f0101087e02404200200020018420028452ad4200520440000b4200200342208852ad4200520440000b4200a72003a7ada74220a710014200a7290300100c21084200a74208a76aada7290300100c21094200a74210a76aada7290300100c210a4200a74218a76aada7290300100c210b2008210420092105200a2106200b21070b20052400200624012007240220040b1f01017e024020004208864280fe0383200042088842ff01838421010b20010b1e01027e02402000100a421086210220022000421088100a8421010b20010b1e01027e02402000100b422086210220022000422088100b8421010b20010b4101007e02402000a72001100c3703002000a74208a76aada72002100c3703002000a74210a76aada72003100c3703002000a74218a76aada72004100c3703000b0b2701007e024042002000200120022003100d42202004200520062007100d4200a74220a710000b0b
0061736d0100000001480a60000060017e017e60027e7e017e60037e7e7e017e60047e7e7e7e017e60057e7e7e7e7e0060087e7e7e7e7e7e7e7e0060087e7e7e7e7e7e7e7e017e60027f7f0060037f7f7f0002310208657468657265756d0c73746f7261676553746f7265000808657468657265756d0c63616c6c44617461436f70790009030e0d0003070407020704010101050605030100010610037e0142000b7e0142000b7e0142000b071102066d656d6f72790200046d61696e00020aa5090df302011f7e02404200210002402000200020002000100921012300210223012103230221040b2001210520022106200321072004210842012109200020008420002009848450ada745ada745ad210a02400340200aa745ad500d01024002402005200620072008200020002000420a1008210b2300210c2301210d2302210e0b0240200b200c200d200e1005210f2300211023012111230221120b200f20108420112012848450ada745ad42005204400c030b024020052006200720082000200020004202100621132300211423012115230221160b201320148420152016848450ada745ad42005204400c030b0240200520062007200820002000200042041006211723002118230121192302211a0b20172018842019201a848450ada745ad42005204400c010b0b0240200520062007200820002000200020091004211b2300211c2301211d2302211e0b201b2105201c2106201d2107201e21080c000b0b20002000200020002005200620072008100e0b0b2f01037e0240200020017c2105200520027c21032005200054ada72003200554ada772ada7ad21040b2004240020030b72010b7e0240200320077c210c200c42007c210b024020022006200c200354ada7200b200c54ada772ada7ad1003210d2300210e0b200d210a024020012005200e1003210f230021100b200f2109024020002004201010032111230021120b201121080b20092400200a2401200b240220080b2601047e0240200020018420022003848450ada7ad21070b20052400200624012007240220040b4901047e02402000200451ad42005204402001200551ad42005204402002200651ad42005204402003200751ad42005204404201210b0b0b0b0b0b20092400200a2401200b240220080b2d01027e024002402000200154ad21032003420151044042ffffffff0f2102052000200152ad21020b0b0b20020b960101087e02404200210c0240200020041007210d200d42005104400240200120051007210e200e42005104400240200220061007210f200f42005104402003200754ad210c05200f42015104404200210c054201210c0b0b0b05200e42015104404200210c054201210c0b0b0b05200d42015104404200210c054201210c0b0b0b200ca7ad210b0b20092400200a2401200b240220080b8f0101087e02404200200020018420028452ad4200520440000b4200200342208852ad4200520440000b4200a72003a7ada74220a710014200a7290000100c21084200a74208a76aada7290000100c21094200a74210a76aada7290000100c210a4200a74218a76aada7290000100c210b2008210420092105200a2106200b21070b20052400200624012007240220040b1f01017e024020004208864280fe0383200042088842ff01838421010b20010b1e01027e02402000100a421086210220022000421088100a8421010b20010b1e01027e02402000100b422086210220022000422088100b8421010b20010b3f0002402000a72001100c3700002000a74208a76aada72002100c3700002000a74210a76aada72003100c3700002000a74218a76aada72004100c3700000b0b2500024042002000200120022003100d42202004200520062007100d4200a74220a710000b0b
Text representation:
(module

View File

@ -0,0 +1 @@
--yul --yul-dialect ewasm --machine ewasm

View File

@ -0,0 +1 @@
Warning: Yul is still experimental. Please use the output with care.

View File

@ -0,0 +1,8 @@
object "object" {
code {
function main()
{
i64.store8(0x01:i32, 42:i64)
}
}
}

View File

@ -0,0 +1,27 @@
======= wasm_to_wasm_memory_instructions_alignment/input.yul (Ewasm) =======
Pretty printed source:
object "object" {
code {
function main()
{ i64.store8(0x01:i32, 42) }
}
}
Binary representation:
0061736d01000000010401600000020100030201000503010001060100071102066d656d6f72790200046d61696e00000a0f010d0002404201a7422a3c00000b0b
Text representation:
(module
(memory $memory (export "memory") 1)
(export "main" (func $main))
(func $main
(block $label_
(i64.store8 (i32.wrap_i64 (i64.const 1)) (i64.const 42))
)
)
)

View File

@ -31,14 +31,14 @@
{
"id": 4,
"nodeType": "Block",
"src": "42:48:1",
"src": "42:58:1",
"statements":
[
{
"AST":
{
"nodeType": "YulBlock",
"src": "61:23:1",
"src": "61:33:1",
"statements":
[
{
@ -48,11 +48,29 @@
"body":
{
"nodeType": "YulBlock",
"src": "80:2:1",
"src": "79:2:1",
"statements": []
},
"nodeType": "YulCase",
"src": "72:10:1",
"src": "72:9:1",
"value":
{
"kind": "number",
"nodeType": "YulLiteral",
"src": "77:1:1",
"type": "",
"value": "0"
}
},
{
"body":
{
"nodeType": "YulBlock",
"src": "90:2:1",
"statements": []
},
"nodeType": "YulCase",
"src": "82:10:1",
"value": "default"
}
],
@ -65,7 +83,7 @@
"value": "0"
},
"nodeType": "YulSwitch",
"src": "63:19:1"
"src": "63:29:1"
}
]
},
@ -73,7 +91,7 @@
"externalReferences": [],
"id": 3,
"nodeType": "InlineAssembly",
"src": "52:32:1"
"src": "52:42:1"
}
]
},
@ -99,15 +117,15 @@
"src": "42:0:1"
},
"scope": 6,
"src": "17:73:1",
"src": "17:83:1",
"stateMutability": "view",
"virtual": false,
"visibility": "public"
}
],
"scope": 7,
"src": "0:92:1"
"src": "0:102:1"
}
],
"src": "0:93:1"
"src": "0:103:1"
}

View File

@ -1,6 +1,6 @@
contract C {
function g() view public {
assembly { switch 0 default {} }
assembly { switch 0 case 0 {} default {} }
}
}

View File

@ -91,30 +91,30 @@
[
null
],
"operations": "{\n switch 0\n default { }\n}"
"operations": "{\n switch 0\n case 0 { }\n default { }\n}"
},
"children": [],
"id": 3,
"name": "InlineAssembly",
"src": "52:32:1"
"src": "52:42:1"
}
],
"id": 4,
"name": "Block",
"src": "42:48:1"
"src": "42:58:1"
}
],
"id": 5,
"name": "FunctionDefinition",
"src": "17:73:1"
"src": "17:83:1"
}
],
"id": 6,
"name": "ContractDefinition",
"src": "0:92:1"
"src": "0:102:1"
}
],
"id": 7,
"name": "SourceUnit",
"src": "0:93:1"
"src": "0:103:1"
}

View File

@ -314,9 +314,17 @@ BOOST_AUTO_TEST_CASE(switch_duplicate_case)
BOOST_AUTO_TEST_CASE(switch_invalid_expression)
{
CHECK_PARSE_ERROR("{ switch {} default {} }", ParserError, "Literal or identifier expected.");
CHECK_PARSE_ERROR("{ switch mload default {} }", ParserError, "Expected '(' but got reserved keyword 'default'");
CHECK_PARSE_ERROR("{ switch mstore(1, 1) default {} }", TypeError, "Expected expression to evaluate to one value, but got 0 values instead.");
CHECK_PARSE_ERROR("{ switch {} case 1 {} default {} }", ParserError, "Literal or identifier expected.");
CHECK_PARSE_ERROR(
"{ switch mload case 1 {} default {} }",
ParserError,
"Expected '(' but got reserved keyword 'case'"
);
CHECK_PARSE_ERROR(
"{ switch mstore(1, 1) case 1 {} default {} }",
TypeError,
"Expected expression to evaluate to one value, but got 0 values instead."
);
}
BOOST_AUTO_TEST_CASE(switch_default_before_case)

View File

@ -0,0 +1,20 @@
contract C {
function to_little_endian_64(uint64 value) public pure returns (bytes memory ret) {
ret = new bytes(8);
bytes8 bytesValue = bytes8(value);
// Byteswapping during copying to bytes.
ret[0] = bytesValue[7];
ret[1] = bytesValue[6];
ret[2] = bytesValue[5];
ret[3] = bytesValue[4];
ret[4] = bytesValue[3];
ret[5] = bytesValue[2];
ret[6] = bytesValue[1];
ret[7] = bytesValue[0];
}
}
// ====
// compileViaYul: also
// ----
// to_little_endian_64(uint64): 0 -> 0x20, 8, 0x00
// to_little_endian_64(uint64): 0x0102030405060708 -> 0x20, 8, 0x0807060504030201000000000000000000000000000000000000000000000000

View File

@ -0,0 +1,29 @@
contract C {
struct Y {
uint a;
uint b;
}
mapping(uint256 => Y)[] public m;
mapping(uint256 => Y)[3] public n;
constructor() public {
m.push();
m.push();
m[1][0].a = 1;
m[1][0].b = 2;
m[1][1].a = 3;
m[1][1].b = 4;
n[1][0].a = 7;
n[1][0].b = 8;
n[1][1].a = 9;
n[1][1].b = 10;
}
}
// ----
// m(uint256,uint256): 0, 0 -> 0x00, 0x00
// m(uint256,uint256): 1, 0 -> 1, 2
// m(uint256,uint256): 1, 1 -> 3, 4
// m(uint256,uint256): 1, 2 -> 0x00, 0x00
// n(uint256,uint256): 0, 0 -> 0x00, 0x00
// n(uint256,uint256): 1, 0 -> 7, 8
// n(uint256,uint256): 1, 1 -> 9, 0x0a
// n(uint256,uint256): 1, 2 -> 0x00, 0x00

View File

@ -0,0 +1,27 @@
contract C {
struct Y {
uint a;
uint b;
}
mapping(uint256 => Y[]) public m;
mapping(uint256 => Y[3]) public n;
constructor() public {
m[1].push().a = 1;
m[1][0].b = 2;
m[1].push().a = 3;
m[1][1].b = 4;
n[1][0].a = 7;
n[1][0].b = 8;
n[1][1].a = 9;
n[1][1].b = 10;
}
}
// ----
// m(uint256,uint256): 0, 0 -> FAILURE
// m(uint256,uint256): 1, 0 -> 1, 2
// m(uint256,uint256): 1, 1 -> 3, 4
// m(uint256,uint256): 1, 2 -> FAILURE
// n(uint256,uint256): 0, 0 -> 0x00, 0x00
// n(uint256,uint256): 1, 0 -> 7, 8
// n(uint256,uint256): 1, 1 -> 9, 0x0a
// n(uint256,uint256): 1, 2 -> 0x00, 0x00

View File

@ -0,0 +1,18 @@
contract C {
mapping(string => uint8[3]) public x;
constructor() public {
x["abc"][0] = 1;
x["abc"][2] = 3;
x["abc"][1] = 2;
x["def"][1] = 9;
}
}
// ====
// compileViaYul: also
// ----
// x(string,uint256): 0x40, 0, 3, "abc" -> 1
// x(string,uint256): 0x40, 1, 3, "abc" -> 2
// x(string,uint256): 0x40, 2, 3, "abc" -> 3
// x(string,uint256): 0x40, 0, 3, "def" -> 0x00
// x(string,uint256): 0x40, 1, 3, "def" -> 9
// x(string,uint256): 0x40, 2, 3, "def" -> 0x00

View File

@ -0,0 +1,18 @@
contract C {
struct S {
uint a;
bytes b;
mapping(uint => uint) c;
uint[] d;
}
uint shifter;
S public s;
constructor() public {
s.a = 7;
s.b = "abc";
s.c[0] = 9;
s.d.push(10);
}
}
// ----
// s() -> 7, 0x40, 3, 0x6162630000000000000000000000000000000000000000000000000000000000

View File

@ -0,0 +1,23 @@
pragma experimental SMTChecker;
contract C {
uint[][] a;
uint[][][] c;
uint[] d;
function f() public {
a.push();
uint[] storage b = a[0];
c[0][0][0] = 12;
d[5] = 7;
b.push(8);
assert(a[0].length == 0);
// Safe but knowledge about `c` is erased because `b` could be pointing to `c[x][y]`.
assert(c[0][0][0] == 12);
// Safe but knowledge about `d` is erased because `b` could be pointing to `d`.
assert(d[5] == 7);
}
}
// ----
// Warning: (193-217): Assertion violation happens here
// Warning: (309-333): Assertion violation happens here
// Warning: (419-436): Assertion violation happens here

View File

@ -0,0 +1,12 @@
pragma experimental SMTChecker;
contract test {
function f() internal pure {
ufixed a = uint64(1) + ufixed(2);
}
}
// ----
// Warning: (80-88): Unused local variable.
// Warning: (91-100): Type conversion is not yet fully supported and might yield false positives.
// Warning: (103-112): Type conversion is not yet fully supported and might yield false positives.
// Warning: (91-112): Underflow (resulting value less than 0) happens here
// Warning: (91-112): Overflow (resulting value larger than 2**256 - 1) happens here

View File

@ -0,0 +1,8 @@
pragma experimental SMTChecker;
contract C {
fixed[] b;
function f() internal { b[0] += 1; }
}
// ----
// Warning: (84-93): Underflow (resulting value less than 0) happens here
// Warning: (84-93): Overflow (resulting value larger than 2**256 - 1) happens here

View File

@ -0,0 +1,6 @@
pragma experimental SMTChecker;
contract C {
function f3() public pure {
((, ), ) = ((7, 8), 9);
}
}

View File

@ -1,15 +1,7 @@
contract C {
struct S { bool f; }
S s;
function f(uint256 a) internal pure {
S storage c;
assembly {
switch a
default { c_slot := s_slot }
}
c;
}
function g(bool flag) internal pure {
function f(bool flag) internal pure {
S storage c;
assembly {
switch flag
@ -18,7 +10,7 @@ contract C {
}
c;
}
function h(uint256 a) internal pure {
function g(uint256 a) internal pure {
S storage c;
assembly {
switch a

View File

@ -1,20 +1,14 @@
contract C {
struct S { bool f; }
S s;
function f(uint256 a) internal pure returns (S storage c) {
assembly {
switch a
default { c_slot := s_slot }
}
}
function g(bool flag) internal pure returns (S storage c) {
function f(bool flag) internal pure returns (S storage c) {
assembly {
switch flag
case 0 { c_slot := s_slot }
default { c_slot := s_slot }
}
}
function h(uint256 a) internal pure returns (S storage c) {
function g(uint256 a) internal pure returns (S storage c) {
assembly {
switch a
case 0 { revert(0, 0) }

View File

@ -0,0 +1,12 @@
contract C {
struct S { bool f; }
S s;
function f(uint256 a) internal pure returns (S storage c) {
assembly {
switch a
default { c_slot := s_slot }
}
}
}
// ----
// Warning: (142-195): "switch" statement with only a default case.

View File

@ -87,12 +87,12 @@ pair<shared_ptr<Block>, shared_ptr<yul::AsmAnalysisInfo>> yul::test::parse(
shared_ptr<Object> parserResult = yul::ObjectParser(errorReporter, _dialect).parse(scanner, false);
if (!parserResult)
return {};
if (!parserResult->code || !errorReporter.errors().empty())
if (!parserResult->code || errorReporter.hasErrors())
return {};
shared_ptr<AsmAnalysisInfo> analysisInfo = make_shared<AsmAnalysisInfo>();
AsmAnalyzer analyzer(*analysisInfo, errorReporter, _dialect, {}, parserResult->dataNames());
// TODO this should be done recursively.
if (!analyzer.analyze(*parserResult->code) || !errorReporter.errors().empty())
if (!analyzer.analyze(*parserResult->code) || errorReporter.hasErrors())
return {};
return {std::move(parserResult->code), std::move(analysisInfo)};
}

View File

@ -332,7 +332,7 @@ BOOST_FIXTURE_TEST_CASE(if_statement_custom_weights, CustomWeightFixture)
BOOST_AUTO_TEST_CASE(switch_statement_tiny)
{
BOOST_CHECK_EQUAL(codeSize(
"{ switch calldatasize() default {} }"
"{ switch calldatasize() case 0 {} }"
), 4);
}

View File

@ -362,7 +362,7 @@ bool YulOptimizerTest::parse(ostream& _stream, string const& _linePrefix, bool c
ErrorList errors;
soltestAssert(m_dialect, "");
std::tie(m_ast, m_analysisInfo) = yul::test::parse(m_source, *m_dialect, errors);
if (!m_ast || !m_analysisInfo || !errors.empty())
if (!m_ast || !m_analysisInfo || !Error::containsOnlyWarnings(errors))
{
AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Error parsing source." << endl;
printErrors(_stream, errors);

View File

@ -118,7 +118,7 @@ BOOST_AUTO_TEST_CASE(geneAddition_should_be_able_to_insert_before_first_position
BOOST_TEST(mutatedChromosome.length() > chromosome.length());
vector<string> suffix(
mutatedChromosome.optimisationSteps().end() - chromosome.length(),
mutatedChromosome.optimisationSteps().end() - static_cast<ptrdiff_t>(chromosome.length()),
mutatedChromosome.optimisationSteps().end()
);
BOOST_TEST(suffix == chromosome.optimisationSteps());
@ -135,7 +135,7 @@ BOOST_AUTO_TEST_CASE(geneAddition_should_be_able_to_insert_after_last_position)
vector<string> prefix(
mutatedChromosome.optimisationSteps().begin(),
mutatedChromosome.optimisationSteps().begin() + chromosome.length()
mutatedChromosome.optimisationSteps().begin() + static_cast<ptrdiff_t>(chromosome.length())
);
BOOST_TEST(prefix == chromosome.optimisationSteps());
}
@ -179,8 +179,8 @@ BOOST_AUTO_TEST_CASE(alternativeMutations_should_choose_between_mutations_with_g
for (size_t i = 0; i < 10; ++i)
{
Chromosome mutatedChromosome = mutation(chromosome);
cCount += static_cast<int>(mutatedChromosome == Chromosome("c"));
fCount += static_cast<int>(mutatedChromosome == Chromosome("f"));
cCount += (mutatedChromosome == Chromosome("c") ? 1 : 0);
fCount += (mutatedChromosome == Chromosome("f") ? 1 : 0);
}
// This particular seed results in 7 "c"s out of 10 which looks plausible given the 80% chance.

View File

@ -135,7 +135,7 @@ BOOST_FIXTURE_TEST_CASE(makeRandom_should_get_chromosome_lengths_from_specified_
size_t maxLength = 5;
assert(chromosomeCount % maxLength == 0);
auto nextLength = [counter = 0, maxLength]() mutable { return counter++ % maxLength; };
auto nextLength = [counter = 0ul, maxLength]() mutable { return counter++ % maxLength; };
auto population = Population::makeRandom(m_fitnessMetric, chromosomeCount, nextLength);
// We can't rely on the order since the population sorts its chromosomes immediately but

View File

@ -72,9 +72,13 @@ size_t phaser::test::countDifferences(Chromosome const& _chromosome1, Chromosome
{
size_t count = 0;
for (size_t i = 0; i < min(_chromosome1.length(), _chromosome2.length()); ++i)
count += static_cast<int>(_chromosome1.optimisationSteps()[i] != _chromosome2.optimisationSteps()[i]);
if (_chromosome1.optimisationSteps()[i] != _chromosome2.optimisationSteps()[i])
++count;
return count + abs(static_cast<int>(_chromosome1.length() - _chromosome2.length()));
return count + static_cast<size_t>(abs(
static_cast<long>(_chromosome1.length()) -
static_cast<long>(_chromosome2.length())
));
}
TemporaryDirectory::TemporaryDirectory(std::string const& _prefix):

View File

@ -155,7 +155,7 @@ Population ClassicGeneticAlgorithm::select(Population _population, size_t _selec
vector<Individual> selectedIndividuals;
for (size_t i = 0; i < _selectionSize; ++i)
{
uint32_t ball = SimulationRNG::uniformInt(0, rouletteRange - 1);
size_t ball = SimulationRNG::uniformInt(0, rouletteRange - 1);
size_t cumulativeFitness = 0;
for (auto const& individual: _population.individuals())

View File

@ -126,12 +126,12 @@ ChromosomePair fixedPointSwap(
return {
Chromosome(
vector<string>(begin1, begin1 + _crossoverPoint) +
vector<string>(begin2 + _crossoverPoint, end2)
vector<string>(begin1, begin1 + static_cast<ptrdiff_t>(_crossoverPoint)) +
vector<string>(begin2 + static_cast<ptrdiff_t>(_crossoverPoint), end2)
),
Chromosome(
vector<string>(begin2, begin2 + _crossoverPoint) +
vector<string>(begin1 + _crossoverPoint, end1)
vector<string>(begin2, begin2 + static_cast<ptrdiff_t>(_crossoverPoint)) +
vector<string>(begin1 + static_cast<ptrdiff_t>(_crossoverPoint), end1)
),
};
}
@ -196,8 +196,8 @@ ChromosomePair fixedTwoPointSwap(
assert(_crossoverPoint2 <= _chromosome1.length());
assert(_crossoverPoint2 <= _chromosome2.length());
size_t lowPoint = min(_crossoverPoint1, _crossoverPoint2);
size_t highPoint = max(_crossoverPoint1, _crossoverPoint2);
auto lowPoint = static_cast<ptrdiff_t>(min(_crossoverPoint1, _crossoverPoint2));
auto highPoint = static_cast<ptrdiff_t>(max(_crossoverPoint1, _crossoverPoint2));
auto begin1 = _chromosome1.optimisationSteps().begin();
auto begin2 = _chromosome2.optimisationSteps().begin();
@ -282,17 +282,17 @@ ChromosomePair uniformSwap(Chromosome const& _chromosome1, Chromosome const& _ch
if (_chromosome1.length() > minLength)
{
if (swapTail)
steps2.insert(steps2.end(), begin1 + minLength, end1);
steps2.insert(steps2.end(), begin1 + static_cast<ptrdiff_t>(minLength), end1);
else
steps1.insert(steps1.end(), begin1 + minLength, end1);
steps1.insert(steps1.end(), begin1 + static_cast<ptrdiff_t>(minLength), end1);
}
if (_chromosome2.length() > minLength)
{
if (swapTail)
steps1.insert(steps1.end(), begin2 + minLength, end2);
steps1.insert(steps1.end(), begin2 + static_cast<ptrdiff_t>(minLength), end2);
else
steps2.insert(steps2.end(), begin2 + minLength, end2);
steps2.insert(steps2.end(), begin2 + static_cast<ptrdiff_t>(minLength), end2);
}
return {Chromosome(steps1), Chromosome(steps2)};

View File

@ -30,7 +30,7 @@ vector<tuple<size_t, size_t>> RandomPairSelection::materialise(size_t _poolSize)
if (_poolSize < 2)
return {};
size_t count = static_cast<size_t>(round(_poolSize * m_selectionSize));
auto count = static_cast<size_t>(round(_poolSize * m_selectionSize));
vector<tuple<size_t, size_t>> selection;
for (size_t i = 0; i < count; ++i)
@ -64,7 +64,10 @@ vector<tuple<size_t, size_t>> PairsFromRandomSubset::materialise(size_t _poolSiz
} while (selectedIndices.size() % 2 != 0);
}
else
selectedIndices.erase(selectedIndices.begin() + SimulationRNG::uniformInt(0, selectedIndices.size() - 1));
selectedIndices.erase(
selectedIndices.begin() +
static_cast<ptrdiff_t>(SimulationRNG::uniformInt(0, selectedIndices.size() - 1))
);
}
assert(selectedIndices.size() % 2 == 0);
@ -73,14 +76,14 @@ vector<tuple<size_t, size_t>> PairsFromRandomSubset::materialise(size_t _poolSiz
{
size_t position1 = SimulationRNG::uniformInt(0, selectedIndices.size() - 1);
size_t value1 = selectedIndices[position1];
selectedIndices.erase(selectedIndices.begin() + position1);
selectedIndices.erase(selectedIndices.begin() + static_cast<ptrdiff_t>(position1));
size_t position2 = SimulationRNG::uniformInt(0, selectedIndices.size() - 1);
size_t value2 = selectedIndices[position2];
selectedIndices.erase(selectedIndices.begin() + position2);
selectedIndices.erase(selectedIndices.begin() + static_cast<ptrdiff_t>(position2));
selectedPairs.push_back({value1, value2});
selectedPairs.emplace_back(value1, value2);
}
assert(selectedIndices.size() == 0);
assert(selectedIndices.empty());
return selectedPairs;
}

View File

@ -17,12 +17,17 @@
#include <tools/yulPhaser/SimulationRNG.h>
// NOTE: The code would work with std::random but the results for a given seed would not be reproducible
// across different STL implementations. Boost does not guarantee this either but at least it has only one
// implementation. Reproducibility is not a hard requirement for yul-phaser but it's nice to have.
#include <boost/random/bernoulli_distribution.hpp>
#include <boost/random/binomial_distribution.hpp>
#include <boost/random/uniform_int_distribution.hpp>
#include <ctime>
#include <limits>
using namespace std;
using namespace solidity;
using namespace solidity::phaser;
@ -30,23 +35,27 @@ thread_local boost::random::mt19937 SimulationRNG::s_generator(SimulationRNG::ge
bool SimulationRNG::bernoulliTrial(double _successProbability)
{
boost::random::bernoulli_distribution<> distribution(_successProbability);
boost::random::bernoulli_distribution<double> distribution(_successProbability);
return static_cast<bool>(distribution(s_generator));
}
uint32_t SimulationRNG::uniformInt(uint32_t _min, uint32_t _max)
{
boost::random::uniform_int_distribution<> distribution(_min, _max);
return distribution(s_generator);
}
uint32_t SimulationRNG::binomialInt(uint32_t _numTrials, double _successProbability)
size_t SimulationRNG::uniformInt(size_t _min, size_t _max)
{
boost::random::binomial_distribution<> distribution(_numTrials, _successProbability);
boost::random::uniform_int_distribution<size_t> distribution(_min, _max);
return distribution(s_generator);
}
size_t SimulationRNG::binomialInt(size_t _numTrials, double _successProbability)
{
// NOTE: binomial_distribution<size_t> would not work because it internally tries to use abs()
// and fails to compile due to ambiguous conversion.
assert(_numTrials <= static_cast<size_t>(numeric_limits<long>::max()));
boost::random::binomial_distribution<long> distribution(static_cast<long>(_numTrials), _successProbability);
return static_cast<size_t>(distribution(s_generator));
}
uint32_t SimulationRNG::generateSeed()
{
// This is not a secure way to seed the generator but it's good enough for simulation purposes.

View File

@ -38,8 +38,8 @@ class SimulationRNG
{
public:
static bool bernoulliTrial(double _successProbability);
static uint32_t uniformInt(uint32_t _min, uint32_t _max);
static uint32_t binomialInt(uint32_t _numTrials, double _successProbability);
static size_t uniformInt(size_t _min, size_t _max);
static size_t binomialInt(size_t _numTrials, double _successProbability);
/// Resets generator to a known state given by the @a seed. Given the same seed, a fixed
/// sequence of calls to the members generating random values is guaranteed to produce the