Merge pull request #7650 from ethereum/develop

Merge develop into develop_060
This commit is contained in:
chriseth 2019-11-06 21:56:55 +01:00 committed by GitHub
commit 21e65076b3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 1081 additions and 135 deletions

View File

@ -40,6 +40,8 @@ Compiler Features:
* Code Generator: Use SELFBALANCE for ``address(this).balance`` if using Istanbul EVM * Code Generator: Use SELFBALANCE for ``address(this).balance`` if using Istanbul EVM
* SMTChecker: Add break/continue support to the CHC engine. * SMTChecker: Add break/continue support to the CHC engine.
* SMTChecker: Support assignments to multi-dimensional arrays and mappings. * SMTChecker: Support assignments to multi-dimensional arrays and mappings.
* SMTChecker: Support inheritance and function overriding.
* EWasm: Experimental EWasm binary output.
Bugfixes: Bugfixes:

View File

@ -60,6 +60,8 @@ Please follow the
[Developers Guide](https://solidity.readthedocs.io/en/latest/contributing.html) [Developers Guide](https://solidity.readthedocs.io/en/latest/contributing.html)
if you want to help. if you want to help.
You can find our current feature and bug priorities for forthcoming releases [in the projects section](https://github.com/ethereum/solidity/projects).
## Maintainers ## Maintainers
* [@axic](https://github.com/axic) * [@axic](https://github.com/axic)
* [@chriseth](https://github.com/chriseth) * [@chriseth](https://github.com/chriseth)

View File

@ -4,7 +4,8 @@
Mapping Types Mapping Types
============= =============
You declare mapping types with the syntax ``mapping(_KeyType => _ValueType)``. Mapping types use the syntax ``mapping(_KeyType => _ValueType)`` and variables
are declared as a mapping type using the syntax ``mapping (_KeyType => _ValueType) _VariableModifiers _VariableName``.
The ``_KeyType`` can be any elementary type. This means it can be any of The ``_KeyType`` can be any elementary type. This means it can be any of
the built-in value types plus ``bytes`` and ``string``. User-defined the built-in value types plus ``bytes`` and ``string``. User-defined
or complex types like contract types, enums, mappings, structs and any array type or complex types like contract types, enums, mappings, structs and any array type
@ -27,11 +28,16 @@ They cannot be used as parameters or return parameters
of contract functions that are publicly visible. of contract functions that are publicly visible.
You can mark state variables of mapping type as ``public`` and Solidity creates a You can mark state variables of mapping type as ``public`` and Solidity creates a
:ref:`getter <visibility-and-getters>` for you. The ``_KeyType`` becomes a :ref:`getter <visibility-and-getters>` for you. The ``_KeyType`` becomes a parameter for the getter.
parameter for the getter. If ``_ValueType`` is a value type or a struct, If ``_ValueType`` is a value type or a struct, the getter returns ``_ValueType``.
the getter returns ``_ValueType``.
If ``_ValueType`` is an array or a mapping, the getter has one parameter for If ``_ValueType`` is an array or a mapping, the getter has one parameter for
each ``_KeyType``, recursively. For example with a mapping: each ``_KeyType``, recursively.
In the example below, the ``MappingExample`` contract defines a public ``balances``
mapping, with the key type an ``address``, and a value type a ``uint``, mapping
an Ethereum address to an unsigned integer value. As ``uint`` is a value type, the getter
returns a value that matches the type, which you can see in the ``MappingUser``
contract that returns the value at the specified address.
:: ::
@ -53,7 +59,146 @@ each ``_KeyType``, recursively. For example with a mapping:
} }
} }
The example below is a simplified version of an `ERC20 token <https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol>`_.
``_allowances`` is an example of a mapping type inside another mapping type.
The example below uses ``_allowances`` to record the amount someone else is allowed to withdraw from your account.
.. note:: ::
Mappings are not iterable, but it is possible to implement a data structure
on top of them. For an example, see `iterable mapping <https://github.com/ethereum/dapp-bin/blob/master/library/iterable_mapping.sol>`_. pragma solidity >=0.4.0 <0.7.0;
contract MappingExample {
mapping (address => uint256) private _balances;
mapping (address => mapping (address => uint256)) private _allowances;
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
function allowance(address owner, address spender) public view returns (uint256) {
return _allowances[owner][spender];
}
function transferFrom(address sender, address recipient, uint256 amount) public returns (bool) {
_transfer(sender, recipient, amount);
approve(sender, msg.sender, amount);
return true;
}
function approve(address owner, address spender, uint256 amount) public returns (bool) {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);
return true;
}
function _transfer(address sender, address recipient, uint256 amount) internal {
require(sender != address(0), "ERC20: transfer from the zero address");
require(recipient != address(0), "ERC20: transfer to the zero address");
_balances[sender] -= amount;
_balances[recipient] += amount;
emit Transfer(sender, recipient, amount);
}
}
.. index:: !iterable mappings
.. _iterable-mappings:
Iterable Mappings
-----------------
Mappings are not iterable, but it is possible to implement a data structure on
top of them and iterate over that. For example, the code below implements an
``IterableMapping`` library that the ``User`` contract then adds data too, and
the ``sum`` function iterates over to sum all the values.
::
pragma solidity >=0.4.0 <0.7.0;
library IterableMapping {
struct itmap {
mapping(uint => IndexValue) data;
KeyFlag[] keys;
uint size;
}
struct IndexValue { uint keyIndex; uint value; }
struct KeyFlag { uint key; bool deleted; }
function insert(itmap storage self, uint key, uint value) internal returns (bool replaced) {
uint keyIndex = self.data[key].keyIndex;
self.data[key].value = value;
if (keyIndex > 0)
return true;
else {
keyIndex = self.keys.length++;
self.data[key].keyIndex = keyIndex + 1;
self.keys[keyIndex].key = key;
self.size++;
return false;
}
}
function remove(itmap storage self, uint key) internal returns (bool success) {
uint keyIndex = self.data[key].keyIndex;
if (keyIndex == 0)
return false;
delete self.data[key];
self.keys[keyIndex - 1].deleted = true;
self.size --;
}
function contains(itmap storage self, uint key) internal view returns (bool) {
return self.data[key].keyIndex > 0;
}
function iterate_start(itmap storage self) internal view returns (uint keyIndex) {
return iterate_next(self, uint(-1));
}
function iterate_valid(itmap storage self, uint keyIndex) internal view returns (bool) {
return keyIndex < self.keys.length;
}
function iterate_next(itmap storage self, uint keyIndex) internal view returns (uint r_keyIndex) {
keyIndex++;
while (keyIndex < self.keys.length && self.keys[keyIndex].deleted)
keyIndex++;
return keyIndex;
}
function iterate_get(itmap storage self, uint keyIndex) internal view returns (uint key, uint value) {
key = self.keys[keyIndex].key;
value = self.data[key].value;
}
}
// How to use it
contract User {
// Just a struct holding our data.
IterableMapping.itmap data;
// Insert something
function insert(uint k, uint v) public returns (uint size) {
// Actually calls itmap_impl.insert, auto-supplying the first parameter for us.
IterableMapping.insert(data, k, v);
// We can still access members of the struct - but we should take care not to mess with them.
return data.size;
}
// Computes the sum of all stored data.
function sum() public view returns (uint s) {
for (uint i = IterableMapping.iterate_start(data);
IterableMapping.iterate_valid(data, i);
i = IterableMapping.iterate_next(data, i)) {
(, uint value) = IterableMapping.iterate_get(data, i);
s += value;
}
}
}

View File

@ -17,7 +17,7 @@ equivalent to ``a = 0``, but it can also be used on arrays, where it assigns a d
array of length zero or a static array of the same length with all elements set to their array of length zero or a static array of the same length with all elements set to their
initial value. ``delete a[x]`` deletes the item at index ``x`` of the array and leaves initial value. ``delete a[x]`` deletes the item at index ``x`` of the array and leaves
all other elements and the length of the array untouched. This especially means that it leaves all other elements and the length of the array untouched. This especially means that it leaves
a gap in the array. If you plan to remove items, a mapping is probably a better choice. a gap in the array. If you plan to remove items, a :ref:`mapping <mapping-types>` is probably a better choice.
For structs, it assigns a struct with all members reset. In other words, the value of ``a`` after ``delete a`` is the same as if ``a`` would be declared without assignment, with the following caveat: For structs, it assigns a struct with all members reset. In other words, the value of ``a`` after ``delete a`` is the same as if ``a`` would be declared without assignment, with the following caveat:

View File

@ -108,14 +108,16 @@ bool BMC::shouldInlineFunctionCall(FunctionCall const& _funCall)
bool BMC::visit(ContractDefinition const& _contract) bool BMC::visit(ContractDefinition const& _contract)
{ {
SMTEncoder::visit(_contract); initContract(_contract);
/// Check targets created by state variable initialization. /// Check targets created by state variable initialization.
smt::Expression constraints = m_context.assertions(); smt::Expression constraints = m_context.assertions();
checkVerificationTargets(constraints); checkVerificationTargets(constraints);
m_verificationTargets.clear(); m_verificationTargets.clear();
return true; SMTEncoder::visit(_contract);
return false;
} }
void BMC::endVisit(ContractDefinition const& _contract) void BMC::endVisit(ContractDefinition const& _contract)

View File

@ -62,8 +62,7 @@ bool CHC::visit(ContractDefinition const& _contract)
reset(); reset();
if (!SMTEncoder::visit(_contract)) initContract(_contract);
return false;
m_stateVariables = _contract.stateVariablesIncludingInherited(); m_stateVariables = _contract.stateVariablesIncludingInherited();
@ -89,7 +88,6 @@ bool CHC::visit(ContractDefinition const& _contract)
// If the contract has a constructor it is handled as a function. // If the contract has a constructor it is handled as a function.
// Otherwise we zero-initialize all state vars. // Otherwise we zero-initialize all state vars.
// TODO take into account state vars init values.
if (!_contract.constructor()) if (!_contract.constructor())
{ {
string constructorName = "constructor_" + _contract.name() + "_" + to_string(_contract.id()); string constructorName = "constructor_" + _contract.name() + "_" + to_string(_contract.id());
@ -108,7 +106,8 @@ bool CHC::visit(ContractDefinition const& _contract)
connectBlocks(constructorPred, interface()); connectBlocks(constructorPred, interface());
} }
return true; SMTEncoder::visit(_contract);
return false;
} }
void CHC::endVisit(ContractDefinition const& _contract) void CHC::endVisit(ContractDefinition const& _contract)

View File

@ -50,8 +50,7 @@ void CVC4Interface::pop()
void CVC4Interface::declareVariable(string const& _name, Sort const& _sort) void CVC4Interface::declareVariable(string const& _name, Sort const& _sort)
{ {
if (!m_variables.count(_name)) m_variables[_name] = m_context.mkVar(_name.c_str(), cvc4Sort(_sort));
m_variables.insert({_name, m_context.mkVar(_name.c_str(), cvc4Sort(_sort))});
} }
void CVC4Interface::addAssertion(Expression const& _expr) void CVC4Interface::addAssertion(Expression const& _expr)

View File

@ -36,12 +36,37 @@ SMTEncoder::SMTEncoder(smt::EncodingContext& _context):
bool SMTEncoder::visit(ContractDefinition const& _contract) bool SMTEncoder::visit(ContractDefinition const& _contract)
{ {
solAssert(m_currentContract == nullptr, ""); solAssert(m_currentContract, "");
m_currentContract = &_contract;
initializeStateVariables(_contract); for (auto const& node: _contract.subNodes())
if (!dynamic_pointer_cast<FunctionDefinition>(node))
node->accept(*this);
return true; vector<FunctionDefinition const*> resolvedFunctions = _contract.definedFunctions();
for (auto const& base: _contract.annotation().linearizedBaseContracts)
for (auto const& baseFunction: base->definedFunctions())
{
if (baseFunction->isConstructor())
continue;
bool overridden = false;
for (auto const& function: resolvedFunctions)
if (
function->name() == baseFunction->name() &&
FunctionType(*function).asCallableFunction(false)->
hasEqualParameterTypes(*FunctionType(*baseFunction).asCallableFunction(false))
)
{
overridden = true;
break;
}
if (!overridden)
resolvedFunctions.push_back(baseFunction);
}
for (auto const& function: resolvedFunctions)
function->accept(*this);
return false;
} }
void SMTEncoder::endVisit(ContractDefinition const& _contract) void SMTEncoder::endVisit(ContractDefinition const& _contract)
@ -512,6 +537,14 @@ void SMTEncoder::endVisit(FunctionCall const& _funCall)
} }
} }
void SMTEncoder::initContract(ContractDefinition const& _contract)
{
solAssert(m_currentContract == nullptr, "");
m_currentContract = &_contract;
initializeStateVariables(_contract);
}
void SMTEncoder::initFunction(FunctionDefinition const& _function) void SMTEncoder::initFunction(FunctionDefinition const& _function)
{ {
solAssert(m_callStack.empty(), ""); solAssert(m_callStack.empty(), "");

View File

@ -108,6 +108,7 @@ protected:
void compareOperation(BinaryOperation const& _op); void compareOperation(BinaryOperation const& _op);
void booleanOperation(BinaryOperation const& _op); void booleanOperation(BinaryOperation const& _op);
void initContract(ContractDefinition const& _contract);
void initFunction(FunctionDefinition const& _function); void initFunction(FunctionDefinition const& _function);
void visitAssert(FunctionCall const& _funCall); void visitAssert(FunctionCall const& _funCall);
void visitRequire(FunctionCall const& _funCall); void visitRequire(FunctionCall const& _funCall);

View File

@ -54,18 +54,20 @@ void Z3Interface::declareVariable(string const& _name, Sort const& _sort)
{ {
if (_sort.kind == Kind::Function) if (_sort.kind == Kind::Function)
declareFunction(_name, _sort); declareFunction(_name, _sort);
else if (!m_constants.count(_name)) else if (m_constants.count(_name))
m_constants.insert({_name, m_context.constant(_name.c_str(), z3Sort(_sort))}); m_constants.at(_name) = m_context.constant(_name.c_str(), z3Sort(_sort));
else
m_constants.emplace(_name, m_context.constant(_name.c_str(), z3Sort(_sort)));
} }
void Z3Interface::declareFunction(string const& _name, Sort const& _sort) void Z3Interface::declareFunction(string const& _name, Sort const& _sort)
{ {
solAssert(_sort.kind == smt::Kind::Function, ""); solAssert(_sort.kind == smt::Kind::Function, "");
if (!m_functions.count(_name))
{
FunctionSort fSort = dynamic_cast<FunctionSort const&>(_sort); FunctionSort fSort = dynamic_cast<FunctionSort const&>(_sort);
m_functions.insert({_name, m_context.function(_name.c_str(), z3Sort(fSort.domain), z3Sort(*fSort.codomain))}); if (m_functions.count(_name))
} m_functions.at(_name) = m_context.function(_name.c_str(), z3Sort(fSort.domain), z3Sort(*fSort.codomain));
else
m_functions.emplace(_name, m_context.function(_name.c_str(), z3Sort(fSort.domain), z3Sort(*fSort.codomain)));
} }
void Z3Interface::addAssertion(Expression const& _expr) void Z3Interface::addAssertion(Expression const& _expr)

View File

@ -586,6 +586,14 @@ string const& CompilerStack::eWasm(string const& _contractName) const
return contract(_contractName).eWasm; return contract(_contractName).eWasm;
} }
eth::LinkerObject const& CompilerStack::eWasmObject(string const& _contractName) const
{
if (m_stackState != CompilationSuccessful)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
return contract(_contractName).eWasmObject;
}
eth::LinkerObject const& CompilerStack::object(string const& _contractName) const eth::LinkerObject const& CompilerStack::object(string const& _contractName) const
{ {
if (m_stackState != CompilationSuccessful) if (m_stackState != CompilationSuccessful)
@ -1055,7 +1063,9 @@ void CompilerStack::generateEWasm(ContractDefinition const& _contract)
//cout << yul::AsmPrinter{}(*ewasmStack.parserResult()->code) << endl; //cout << yul::AsmPrinter{}(*ewasmStack.parserResult()->code) << endl;
// Turn into eWasm text representation. // Turn into eWasm text representation.
compiledContract.eWasm = ewasmStack.assemble(yul::AssemblyStack::Machine::eWasm).assembly; auto result = ewasmStack.assemble(yul::AssemblyStack::Machine::eWasm);
compiledContract.eWasm = std::move(result.assembly);
compiledContract.eWasmObject = std::move(*result.bytecode);
} }
CompilerStack::Contract const& CompilerStack::contract(string const& _contractName) const CompilerStack::Contract const& CompilerStack::contract(string const& _contractName) const

View File

@ -243,9 +243,12 @@ public:
/// @returns the optimized IR representation of a contract. /// @returns the optimized IR representation of a contract.
std::string const& yulIROptimized(std::string const& _contractName) const; std::string const& yulIROptimized(std::string const& _contractName) const;
/// @returns the eWasm (text) representation of a contract. /// @returns the eWasm text representation of a contract.
std::string const& eWasm(std::string const& _contractName) const; std::string const& eWasm(std::string const& _contractName) const;
/// @returns the eWasm representation of a contract.
eth::LinkerObject const& eWasmObject(std::string const& _contractName) const;
/// @returns the assembled object for a contract. /// @returns the assembled object for a contract.
eth::LinkerObject const& object(std::string const& _contractName) const; eth::LinkerObject const& object(std::string const& _contractName) const;
@ -323,7 +326,8 @@ private:
eth::LinkerObject runtimeObject; ///< Runtime object. eth::LinkerObject runtimeObject; ///< Runtime object.
std::string yulIR; ///< Experimental Yul IR code. std::string yulIR; ///< Experimental Yul IR code.
std::string yulIROptimized; ///< Optimized experimental Yul IR code. std::string yulIROptimized; ///< Optimized experimental Yul IR code.
std::string eWasm; ///< Experimental eWasm code (text representation). std::string eWasm; ///< Experimental eWasm text representation
eth::LinkerObject eWasmObject; ///< Experimental eWasm code
mutable std::unique_ptr<std::string const> metadata; ///< The metadata json that will be hashed into the chain. mutable std::unique_ptr<std::string const> metadata; ///< The metadata json that will be hashed into the chain.
mutable std::unique_ptr<Json::Value const> abi; mutable std::unique_ptr<Json::Value const> abi;
mutable std::unique_ptr<Json::Value const> userDocumentation; mutable std::unique_ptr<Json::Value const> userDocumentation;
@ -357,7 +361,7 @@ private:
/// The IR is stored but otherwise unused. /// The IR is stored but otherwise unused.
void generateIR(ContractDefinition const& _contract); void generateIR(ContractDefinition const& _contract);
/// Generate eWasm text representation for a single contract. /// Generate eWasm representation for a single contract.
void generateEWasm(ContractDefinition const& _contract); void generateEWasm(ContractDefinition const& _contract);
/// Links all the known library addresses in the available objects. Any unknown /// Links all the known library addresses in the available objects. Any unknown

View File

@ -925,6 +925,8 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting
// eWasm // eWasm
if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "ewasm.wast", wildcardMatchesExperimental)) if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "ewasm.wast", wildcardMatchesExperimental))
contractData["ewasm"]["wast"] = compilerStack.eWasm(contractName); contractData["ewasm"]["wast"] = compilerStack.eWasm(contractName);
if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "ewasm.wasm", wildcardMatchesExperimental))
contractData["ewasm"]["wasm"] = compilerStack.eWasmObject(contractName).toHex();
// EVM // EVM
Json::Value evmData(Json::objectValue); Json::Value evmData(Json::objectValue);

View File

@ -202,7 +202,10 @@ MachineAssemblyObject AssemblyStack::assemble(Machine _machine) const
Dialect const& dialect = languageToDialect(m_language, EVMVersion{}); Dialect const& dialect = languageToDialect(m_language, EVMVersion{});
MachineAssemblyObject object; MachineAssemblyObject object;
object.assembly = EWasmObjectCompiler::compile(*m_parserResult, dialect); auto result = EWasmObjectCompiler::compile(*m_parserResult, dialect);
object.assembly = std::move(result.first);
object.bytecode = make_shared<dev::eth::LinkerObject>();
object.bytecode->bytecode = std::move(result.second);
return object; return object;
} }
} }

View File

@ -50,6 +50,8 @@ add_library(yul
backends/wasm/EWasmObjectCompiler.h backends/wasm/EWasmObjectCompiler.h
backends/wasm/EWasmToText.cpp backends/wasm/EWasmToText.cpp
backends/wasm/EWasmToText.h backends/wasm/EWasmToText.h
backends/wasm/BinaryTransform.cpp
backends/wasm/BinaryTransform.h
backends/wasm/WasmDialect.cpp backends/wasm/WasmDialect.cpp
backends/wasm/WasmDialect.h backends/wasm/WasmDialect.h
backends/wasm/WordSizeTransform.cpp backends/wasm/WordSizeTransform.cpp

View File

@ -0,0 +1,582 @@
/*
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/>.
*/
/**
* EWasm to binary encoder.
*/
#include <libyul/backends/wasm/BinaryTransform.h>
#include <libyul/Exceptions.h>
#include <libdevcore/CommonData.h>
#include <boost/range/adaptor/reversed.hpp>
using namespace std;
using namespace yul;
using namespace dev;
using namespace yul::wasm;
namespace
{
bytes toBytes(uint8_t _b)
{
return bytes(1, _b);
}
enum class Section: uint8_t
{
CUSTOM = 0x00,
TYPE = 0x01,
IMPORT = 0x02,
FUNCTION = 0x03,
MEMORY = 0x05,
GLOBAL = 0x06,
EXPORT = 0x07,
CODE = 0x0a
};
bytes toBytes(Section _s)
{
return toBytes(uint8_t(_s));
}
enum class ValueType: uint8_t
{
Void = 0x40,
Function = 0x60,
I64 = 0x7e,
I32 = 0x7f
};
bytes toBytes(ValueType _vt)
{
return toBytes(uint8_t(_vt));
}
enum class Export: uint8_t
{
Function = 0x0,
Memory = 0x2
};
bytes toBytes(Export _export)
{
return toBytes(uint8_t(_export));
}
enum class Opcode: uint8_t
{
Unreachable = 0x00,
Nop = 0x01,
Block = 0x02,
Loop = 0x03,
If = 0x04,
Else = 0x05,
Try = 0x06,
Catch = 0x07,
Throw = 0x08,
Rethrow = 0x09,
BrOnExn = 0x0a,
End = 0x0b,
Br = 0x0c,
BrIf = 0x0d,
BrTable = 0x0e,
Return = 0x0f,
Call = 0x10,
CallIndirect = 0x11,
ReturnCall = 0x12,
ReturnCallIndirect = 0x13,
Drop = 0x1a,
Select = 0x1b,
LocalGet = 0x20,
LocalSet = 0x21,
LocalTee = 0x22,
GlobalGet = 0x23,
GlobalSet = 0x24,
I32Const = 0x41,
I64Const = 0x42,
};
bytes toBytes(Opcode _o)
{
return toBytes(uint8_t(_o));
}
static std::map<string, uint8_t> const builtins = {
{"i32.load", 0x28},
{"i64.load", 0x29},
{"i32.load8_s", 0x2c},
{"i32.load8_u", 0x2d},
{"i32.load16_s", 0x2e},
{"i32.load16_u", 0x2f},
{"i64.load8_s", 0x30},
{"i64.load8_u", 0x31},
{"i64.load16_s", 0x32},
{"i64.load16_u", 0x33},
{"i64.load32_s", 0x34},
{"i64.load32_u", 0x35},
{"i32.store", 0x36},
{"i64.store", 0x37},
{"i32.store8", 0x3a},
{"i32.store16", 0x3b},
{"i64.store8", 0x3c},
{"i64.store16", 0x3d},
{"i64.store32", 0x3e},
{"memory.size", 0x3f},
{"memory.grow", 0x40},
{"i32.eqz", 0x45},
{"i32.eq", 0x46},
{"i32.ne", 0x47},
{"i32.lt_s", 0x48},
{"i32.lt_u", 0x49},
{"i32.gt_s", 0x4a},
{"i32.gt_u", 0x4b},
{"i32.le_s", 0x4c},
{"i32.le_u", 0x4d},
{"i32.ge_s", 0x4e},
{"i32.ge_u", 0x4f},
{"i64.eqz", 0x50},
{"i64.eq", 0x51},
{"i64.ne", 0x52},
{"i64.lt_s", 0x53},
{"i64.lt_u", 0x54},
{"i64.gt_s", 0x55},
{"i64.gt_u", 0x56},
{"i64.le_s", 0x57},
{"i64.le_u", 0x58},
{"i64.ge_s", 0x59},
{"i64.ge_u", 0x5a},
{"i32.clz", 0x67},
{"i32.ctz", 0x68},
{"i32.popcnt", 0x69},
{"i32.add", 0x6a},
{"i32.sub", 0x6b},
{"i32.mul", 0x6c},
{"i32.div_s", 0x6d},
{"i32.div_u", 0x6e},
{"i32.rem_s", 0x6f},
{"i32.rem_u", 0x70},
{"i32.and", 0x71},
{"i32.or", 0x72},
{"i32.xor", 0x73},
{"i32.shl", 0x74},
{"i32.shr_s", 0x75},
{"i32.shr_u", 0x76},
{"i32.rotl", 0x77},
{"i32.rotr", 0x78},
{"i64.clz", 0x79},
{"i64.ctz", 0x7a},
{"i64.popcnt", 0x7b},
{"i64.add", 0x7c},
{"i64.sub", 0x7d},
{"i64.mul", 0x7e},
{"i64.div_s", 0x7f},
{"i64.div_u", 0x80},
{"i64.rem_s", 0x81},
{"i64.rem_u", 0x82},
{"i64.and", 0x83},
{"i64.or", 0x84},
{"i64.xor", 0x85},
{"i64.shl", 0x86},
{"i64.shr_s", 0x87},
{"i64.shr_u", 0x88},
{"i64.rotl", 0x89},
{"i64.rotr", 0x8a},
{"i32.wrap_i64", 0xa7},
{"i64.extend_i32_s", 0xac},
{"i64.extend_i32_u", 0xad},
};
bytes lebEncode(uint64_t _n)
{
bytes encoded;
while (_n > 0x7f)
{
encoded.emplace_back(uint8_t(0x80 | (_n & 0x7f)));
_n >>= 7;
}
encoded.emplace_back(_n);
return encoded;
}
bytes lebEncodeSigned(int64_t _n)
{
if (_n >= 0 && _n < 0x40)
return toBytes(uint8_t(uint64_t(_n) & 0xff));
else if (-_n > 0 && -_n < 0x40)
return toBytes(uint8_t(uint64_t(_n + 0x80) & 0xff));
else
return toBytes(uint8_t(0x80 | uint8_t(_n & 0x7f))) + lebEncodeSigned(_n / 0x80);
}
bytes prefixSize(bytes _data)
{
size_t size = _data.size();
return lebEncode(size) + std::move(_data);
}
bytes makeSection(Section _section, bytes _data)
{
return toBytes(_section) + prefixSize(std::move(_data));
}
}
bytes BinaryTransform::run(Module const& _module)
{
BinaryTransform bt;
for (size_t i = 0; i < _module.globals.size(); ++i)
bt.m_globals[_module.globals[i].variableName] = i;
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++;
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();
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 += bt.codeSection(_module.functions);
return ret;
}
bytes BinaryTransform::operator()(Literal const& _literal)
{
return toBytes(Opcode::I64Const) + lebEncodeSigned(_literal.value);
}
bytes BinaryTransform::operator()(StringLiteral const&)
{
// TODO is this used?
yulAssert(false, "String literals not yet implemented");
}
bytes BinaryTransform::operator()(LocalVariable const& _variable)
{
return toBytes(Opcode::LocalGet) + lebEncode(m_locals.at(_variable.name));
}
bytes BinaryTransform::operator()(GlobalVariable const& _variable)
{
return toBytes(Opcode::GlobalGet) + lebEncode(m_globals.at(_variable.name));
}
bytes BinaryTransform::operator()(BuiltinCall const& _call)
{
// We need to avoid visiting the arguments of `dataoffset` and `datasize` because
// they are references to object names that should not end up in the code.
if (_call.functionName == "dataoffset")
{
string name = boost::get<StringLiteral>(_call.arguments.at(0)).value;
return toBytes(Opcode::I64Const) + lebEncodeSigned(m_subModulePosAndSize.at(name).first);
}
else if (_call.functionName == "datasize")
{
string name = boost::get<StringLiteral>(_call.arguments.at(0)).value;
return toBytes(Opcode::I64Const) + lebEncodeSigned(m_subModulePosAndSize.at(name).second);
}
bytes args = visit(_call.arguments);
if (_call.functionName == "unreachable")
return toBytes(Opcode::Unreachable);
else
{
yulAssert(builtins.count(_call.functionName), "Builtin " + _call.functionName + " not found");
bytes ret = std::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}};
return ret;
}
}
bytes BinaryTransform::operator()(FunctionCall const& _call)
{
return visit(_call.arguments) + toBytes(Opcode::Call) + lebEncode(m_functions.at(_call.functionName));
}
bytes BinaryTransform::operator()(LocalAssignment const& _assignment)
{
return
boost::apply_visitor(*this, *_assignment.value) +
toBytes(Opcode::LocalSet) +
lebEncode(m_locals.at(_assignment.variableName));
}
bytes BinaryTransform::operator()(GlobalAssignment const& _assignment)
{
return
boost::apply_visitor(*this, *_assignment.value) +
toBytes(Opcode::GlobalSet) +
lebEncode(m_globals.at(_assignment.variableName));
}
bytes BinaryTransform::operator()(If const& _if)
{
bytes result =
boost::apply_visitor(*this, *_if.condition) +
toBytes(Opcode::If) +
toBytes(ValueType::Void);
m_labels.push({});
result += visit(_if.statements);
if (_if.elseStatements)
result += toBytes(Opcode::Else) + visit(*_if.elseStatements);
m_labels.pop();
result += toBytes(Opcode::End);
return result;
}
bytes BinaryTransform::operator()(Loop const& _loop)
{
bytes result = toBytes(Opcode::Loop) + toBytes(ValueType::Void);
m_labels.push(_loop.labelName);
result += visit(_loop.statements);
m_labels.pop();
result += toBytes(Opcode::End);
return result;
}
bytes BinaryTransform::operator()(Break const&)
{
yulAssert(false, "br not yet implemented.");
// TODO the index is just the nesting depth.
return {};
}
bytes BinaryTransform::operator()(BreakIf const&)
{
yulAssert(false, "br_if not yet implemented.");
// TODO the index is just the nesting depth.
return {};
}
bytes BinaryTransform::operator()(Block const& _block)
{
return
toBytes(Opcode::Block) +
toBytes(ValueType::Void) +
visit(_block.statements) +
toBytes(Opcode::End);
}
bytes BinaryTransform::operator()(FunctionDefinition const& _function)
{
bytes ret;
// This is a kind of run-length-encoding of local types. Has to be adapted once
// we have locals of different types.
ret += lebEncode(1); // number of locals groups
ret += lebEncode(_function.locals.size());
ret += toBytes(ValueType::I64);
m_locals.clear();
size_t varIdx = 0;
for (size_t i = 0; i < _function.parameterNames.size(); ++i)
m_locals[_function.parameterNames[i]] = varIdx++;
for (size_t i = 0; i < _function.locals.size(); ++i)
m_locals[_function.locals[i].variableName] = varIdx++;
ret += visit(_function.body);
ret += toBytes(Opcode::End);
return prefixSize(std::move(ret));
}
BinaryTransform::Type BinaryTransform::typeOf(FunctionImport const& _import)
{
return {
encodeTypes(_import.paramTypes),
encodeTypes(_import.returnType ? vector<string>(1, *_import.returnType) : vector<string>())
};
}
BinaryTransform::Type BinaryTransform::typeOf(FunctionDefinition const& _funDef)
{
return {
encodeTypes(vector<string>(_funDef.parameterNames.size(), "i64")),
encodeTypes(vector<string>(_funDef.returns ? 1 : 0, "i64"))
};
}
uint8_t BinaryTransform::encodeType(string const& _typeName)
{
if (_typeName == "i32")
return uint8_t(ValueType::I32);
else if (_typeName == "i64")
return uint8_t(ValueType::I64);
else
yulAssert(false, "");
return 0;
}
vector<uint8_t> BinaryTransform::encodeTypes(vector<string> const& _typeNames)
{
vector<uint8_t> result;
for (auto const& t: _typeNames)
result.emplace_back(encodeType(t));
return result;
}
bytes BinaryTransform::typeSection(
vector<FunctionImport> const& _imports,
vector<FunctionDefinition> const& _functions
)
{
map<Type, vector<string>> types;
for (auto const& import: _imports)
types[typeOf(import)].emplace_back(import.internalName);
for (auto const& fun: _functions)
types[typeOf(fun)].emplace_back(fun.name);
bytes result;
size_t index = 0;
for (auto const& [type, funNames]: types)
{
for (string const& name: funNames)
m_functionTypes[name] = index;
result += toBytes(ValueType::Function);
result += lebEncode(type.first.size()) + type.first;
result += lebEncode(type.second.size()) + type.second;
index++;
}
return makeSection(Section::TYPE, lebEncode(index) + std::move(result));
}
bytes BinaryTransform::importSection(
vector<FunctionImport> const& _imports
)
{
bytes result = lebEncode(_imports.size());
for (FunctionImport const& import: _imports)
{
uint8_t importKind = 0; // function
result +=
encodeName(import.module) +
encodeName(import.externalName) +
toBytes(importKind) +
lebEncode(m_functionTypes[import.internalName]);
}
return makeSection(Section::IMPORT, std::move(result));
}
bytes BinaryTransform::functionSection(vector<FunctionDefinition> const& _functions)
{
bytes result = lebEncode(_functions.size());
for (auto const& fun: _functions)
result += lebEncode(m_functionTypes.at(fun.name));
return makeSection(Section::FUNCTION, std::move(result));
}
bytes BinaryTransform::memorySection()
{
bytes result = lebEncode(1);
result.push_back(0); // flags
result.push_back(1); // initial
return makeSection(Section::MEMORY, std::move(result));
}
bytes BinaryTransform::globalSection()
{
bytes result = lebEncode(m_globals.size());
for (size_t i = 0; i < m_globals.size(); ++i)
result +=
// mutable i64
bytes{uint8_t(ValueType::I64), 1} +
toBytes(Opcode::I64Const) +
lebEncodeSigned(0) +
toBytes(Opcode::End);
return makeSection(Section::GLOBAL, std::move(result));
}
bytes BinaryTransform::exportSection()
{
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));
}
bytes BinaryTransform::customSection(string const& _name, bytes _data)
{
bytes result = encodeName(_name) + std::move(_data);
return makeSection(Section::CUSTOM, std::move(result));
}
bytes BinaryTransform::codeSection(vector<wasm::FunctionDefinition> const& _functions)
{
bytes result = lebEncode(_functions.size());
for (FunctionDefinition const& fun: _functions)
result += (*this)(fun);
return makeSection(Section::CODE, std::move(result));
}
bytes BinaryTransform::visit(vector<Expression> const& _expressions)
{
bytes result;
for (auto const& expr: _expressions)
result += boost::apply_visitor(*this, expr);
return result;
}
bytes BinaryTransform::visitReversed(vector<Expression> const& _expressions)
{
bytes result;
for (auto const& expr: _expressions | boost::adaptors::reversed)
result += boost::apply_visitor(*this, expr);
return result;
}
bytes BinaryTransform::encodeName(std::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
// is a very bad sign.
for (char c: _name)
yulAssert(uint8_t(c) <= 0x7f, "Non-ascii character found.");
return lebEncode(_name.size()) + asBytes(_name);
}

View 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/>.
*/
/**
* EWasm to binary encoder.
*/
#pragma once
#include <libyul/backends/wasm/EWasmAST.h>
#include <libdevcore/Common.h>
#include <vector>
#include <stack>
namespace yul
{
namespace wasm
{
/**
* Web assembly to binary transform.
*/
class BinaryTransform: public boost::static_visitor<dev::bytes>
{
public:
static dev::bytes run(Module const& _module);
dev::bytes operator()(wasm::Literal const& _literal);
dev::bytes operator()(wasm::StringLiteral const& _literal);
dev::bytes operator()(wasm::LocalVariable const& _identifier);
dev::bytes operator()(wasm::GlobalVariable const& _identifier);
dev::bytes operator()(wasm::BuiltinCall const& _builinCall);
dev::bytes operator()(wasm::FunctionCall const& _functionCall);
dev::bytes operator()(wasm::LocalAssignment const& _assignment);
dev::bytes operator()(wasm::GlobalAssignment const& _assignment);
dev::bytes operator()(wasm::If const& _if);
dev::bytes operator()(wasm::Loop const& _loop);
dev::bytes operator()(wasm::Break const& _break);
dev::bytes operator()(wasm::BreakIf const& _break);
dev::bytes operator()(wasm::Block const& _block);
dev::bytes operator()(wasm::FunctionDefinition const& _function);
private:
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);
dev::bytes typeSection(
std::vector<wasm::FunctionImport> const& _imports,
std::vector<wasm::FunctionDefinition> const& _functions
);
dev::bytes importSection(std::vector<wasm::FunctionImport> const& _imports);
dev::bytes functionSection(std::vector<wasm::FunctionDefinition> const& _functions);
dev::bytes memorySection();
dev::bytes globalSection();
dev::bytes exportSection();
dev::bytes customSection(std::string const& _name, dev::bytes _data);
dev::bytes codeSection(std::vector<wasm::FunctionDefinition> const& _functions);
dev::bytes visit(std::vector<wasm::Expression> const& _expressions);
dev::bytes visitReversed(std::vector<wasm::Expression> const& _expressions);
static dev::bytes encodeName(std::string const& _name);
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::stack<std::string> m_labels;
std::map<std::string, std::pair<size_t, size_t>> m_subModulePosAndSize;
};
}
}

View File

@ -23,6 +23,8 @@
#include <boost/variant.hpp> #include <boost/variant.hpp>
#include <string> #include <string>
#include <vector> #include <vector>
#include <map>
#include <memory>
namespace yul namespace yul
{ {
@ -88,6 +90,16 @@ struct FunctionDefinition
std::vector<Expression> body; std::vector<Expression> body;
}; };
/**
* Abstract representation of a wasm module.
*/
struct Module
{
std::vector<GlobalVariableDeclaration> globals;
std::vector<FunctionImport> imports;
std::vector<FunctionDefinition> functions;
std::map<std::string, Module> subModules;
};
} }
} }

View File

@ -20,7 +20,6 @@
#include <libyul/backends/wasm/EWasmCodeTransform.h> #include <libyul/backends/wasm/EWasmCodeTransform.h>
#include <libyul/backends/wasm/EWasmToText.h>
#include <libyul/optimiser/NameCollector.h> #include <libyul/optimiser/NameCollector.h>
#include <libyul/AsmData.h> #include <libyul/AsmData.h>
@ -37,10 +36,11 @@ using namespace std;
using namespace dev; using namespace dev;
using namespace yul; using namespace yul;
string EWasmCodeTransform::run(Dialect const& _dialect, yul::Block const& _ast) wasm::Module EWasmCodeTransform::run(Dialect const& _dialect, yul::Block const& _ast)
{ {
wasm::Module module;
EWasmCodeTransform transform(_dialect, _ast); EWasmCodeTransform transform(_dialect, _ast);
vector<wasm::FunctionDefinition> functions;
for (auto const& statement: _ast.statements) for (auto const& statement: _ast.statements)
{ {
@ -49,17 +49,14 @@ string EWasmCodeTransform::run(Dialect const& _dialect, yul::Block const& _ast)
"Expected only function definitions at the highest level." "Expected only function definitions at the highest level."
); );
if (statement.type() == typeid(yul::FunctionDefinition)) if (statement.type() == typeid(yul::FunctionDefinition))
functions.emplace_back(transform.translateFunction(boost::get<yul::FunctionDefinition>(statement))); module.functions.emplace_back(transform.translateFunction(boost::get<yul::FunctionDefinition>(statement)));
} }
std::vector<wasm::FunctionImport> imports;
for (auto& imp: transform.m_functionsToImport) for (auto& imp: transform.m_functionsToImport)
imports.emplace_back(std::move(imp.second)); module.imports.emplace_back(std::move(imp.second));
return EWasmToText().run( module.globals = transform.m_globalVariables;
transform.m_globalVariables,
imports, return module;
functions
);
} }
wasm::Expression EWasmCodeTransform::generateMultiAssignment( wasm::Expression EWasmCodeTransform::generateMultiAssignment(

View File

@ -35,7 +35,7 @@ struct AsmAnalysisInfo;
class EWasmCodeTransform: public boost::static_visitor<wasm::Expression> class EWasmCodeTransform: public boost::static_visitor<wasm::Expression>
{ {
public: public:
static std::string run(Dialect const& _dialect, yul::Block const& _ast); static wasm::Module run(Dialect const& _dialect, yul::Block const& _ast);
public: public:
wasm::Expression operator()(yul::Literal const& _literal); wasm::Expression operator()(yul::Literal const& _literal);

View File

@ -21,32 +21,36 @@
#include <libyul/backends/wasm/EWasmObjectCompiler.h> #include <libyul/backends/wasm/EWasmObjectCompiler.h>
#include <libyul/backends/wasm/EWasmCodeTransform.h> #include <libyul/backends/wasm/EWasmCodeTransform.h>
#include <libyul/backends/wasm/BinaryTransform.h>
#include <libyul/backends/wasm/EWasmToText.h>
#include <libyul/Object.h> #include <libyul/Object.h>
#include <libyul/Exceptions.h> #include <libyul/Exceptions.h>
#include <libdevcore/CommonData.h>
using namespace yul; using namespace yul;
using namespace std; using namespace std;
string EWasmObjectCompiler::compile(Object& _object, Dialect const& _dialect) pair<string, dev::bytes> EWasmObjectCompiler::compile(Object& _object, Dialect const& _dialect)
{ {
EWasmObjectCompiler compiler(_dialect); EWasmObjectCompiler compiler(_dialect);
return compiler.run(_object); wasm::Module module = compiler.run(_object);
return {EWasmToText().run(module), wasm::BinaryTransform::run(module)};
} }
string EWasmObjectCompiler::run(Object& _object) wasm::Module EWasmObjectCompiler::run(Object& _object)
{ {
string ret; yulAssert(_object.analysisInfo, "No analysis info.");
yulAssert(_object.code, "No code.");
wasm::Module module = EWasmCodeTransform::run(m_dialect, *_object.code);
for (auto& subNode: _object.subObjects) for (auto& subNode: _object.subObjects)
if (Object* subObject = dynamic_cast<Object*>(subNode.get())) if (Object* subObject = dynamic_cast<Object*>(subNode.get()))
ret += compile(*subObject, m_dialect); module.subModules[subObject->name.str()] = run(*subObject);
else else
yulAssert(false, "Data is not yet supported for EWasm."); yulAssert(false, "Data is not yet supported for EWasm.");
yulAssert(_object.analysisInfo, "No analysis info."); return module;
yulAssert(_object.code, "No code.");
ret += EWasmCodeTransform::run(m_dialect, *_object.code);
return ret;
} }

View File

@ -21,22 +21,33 @@
#pragma once #pragma once
#include <string> #include <string>
#include <vector>
#include <tuple>
namespace dev
{
using bytes = std::vector<uint8_t>;
}
namespace yul namespace yul
{ {
struct Object; struct Object;
struct Dialect; struct Dialect;
namespace wasm
{
struct Module;
}
class EWasmObjectCompiler class EWasmObjectCompiler
{ {
public: public:
static std::string compile(Object& _object, Dialect const& _dialect); /// Compiles the given object and returns the WAST and the binary representation.
static std::pair<std::string, dev::bytes> compile(Object& _object, Dialect const& _dialect);
private: private:
EWasmObjectCompiler(Dialect const& _dialect): EWasmObjectCompiler(Dialect const& _dialect):
m_dialect(_dialect) m_dialect(_dialect)
{} {}
std::string run(Object& _object); wasm::Module run(Object& _object);
Dialect const& m_dialect; Dialect const& m_dialect;
}; };

View File

@ -30,14 +30,15 @@ using namespace std;
using namespace yul; using namespace yul;
using namespace dev; using namespace dev;
string EWasmToText::run( string EWasmToText::run(wasm::Module const& _module)
vector<wasm::GlobalVariableDeclaration> const& _globals,
vector<wasm::FunctionImport> const& _imports,
vector<wasm::FunctionDefinition> const& _functions
)
{ {
string ret = "(module\n"; string ret = "(module\n";
for (wasm::FunctionImport const& imp: _imports) for (auto const& sub: _module.subModules)
ret +=
" ;; sub-module \"" +
sub.first +
"\" will be encoded as custom section in binary here, but is skipped in text mode.\n";
for (wasm::FunctionImport const& imp: _module.imports)
{ {
ret += " (import \"" + imp.module + "\" \"" + imp.externalName + "\" (func $" + imp.internalName; ret += " (import \"" + imp.module + "\" \"" + imp.externalName + "\" (func $" + imp.internalName;
if (!imp.paramTypes.empty()) if (!imp.paramTypes.empty())
@ -52,10 +53,10 @@ string EWasmToText::run(
// export the main function // export the main function
ret += " (export \"main\" (func $main))\n"; ret += " (export \"main\" (func $main))\n";
for (auto const& g: _globals) for (auto const& g: _module.globals)
ret += " (global $" + g.variableName + " (mut i64) (i64.const 0))\n"; ret += " (global $" + g.variableName + " (mut i64) (i64.const 0))\n";
ret += "\n"; ret += "\n";
for (auto const& f: _functions) for (auto const& f: _module.functions)
ret += transform(f) + "\n"; ret += transform(f) + "\n";
return move(ret) + ")\n"; return move(ret) + ")\n";
} }

View File

@ -31,11 +31,7 @@ struct AsmAnalysisInfo;
class EWasmToText: public boost::static_visitor<std::string> class EWasmToText: public boost::static_visitor<std::string>
{ {
public: public:
std::string run( std::string run(wasm::Module const& _module);
std::vector<wasm::GlobalVariableDeclaration> const& _globals,
std::vector<wasm::FunctionImport> const& _imports,
std::vector<wasm::FunctionDefinition> const& _functions
);
public: public:
std::string operator()(wasm::Literal const& _literal); std::string operator()(wasm::Literal const& _literal);

View File

@ -36,11 +36,6 @@ source "${REPO_ROOT}/scripts/common.sh"
WORKDIR=`mktemp -d` WORKDIR=`mktemp -d`
CMDLINE_PID= CMDLINE_PID=
if [[ "$OSTYPE" == "darwin"* ]]
then
SMT_FLAGS="--no-smt"
fi
cleanup() { cleanup() {
# ensure failing commands don't cause termination during cleanup (especially within safe_kill) # ensure failing commands don't cause termination during cleanup (especially within safe_kill)
set +e set +e

View File

@ -329,11 +329,18 @@ void CommandLineInterface::handleEWasm(string const& _contractName)
if (m_args.count(g_argEWasm)) if (m_args.count(g_argEWasm))
{ {
if (m_args.count(g_argOutputDir)) if (m_args.count(g_argOutputDir))
{
createFile(m_compiler->filesystemFriendlyName(_contractName) + ".wast", m_compiler->eWasm(_contractName)); createFile(m_compiler->filesystemFriendlyName(_contractName) + ".wast", m_compiler->eWasm(_contractName));
createFile(
m_compiler->filesystemFriendlyName(_contractName) + ".wasm",
asString(m_compiler->eWasmObject(_contractName).bytecode)
);
}
else else
{ {
sout() << "eWasm: " << endl; sout() << "EWasm text: " << endl;
sout() << m_compiler->eWasm(_contractName) << endl; sout() << m_compiler->eWasm(_contractName) << endl;
sout() << "EWasm binary (hex): " << m_compiler->eWasmObject(_contractName).toHex() << endl;
} }
} }
} }

View File

@ -1,63 +1,5 @@
{"contracts":{"A":{"C":{"ewasm":{"wast":"(module {"contracts":{"A":{"C":{"ewasm":{"wast":"(module
(import \"ethereum\" \"revert\" (func $eth.revert (param i32 i32))) ;; sub-module \"C_2_deployed\" will be encoded as custom section in binary here, but is skipped in text mode.
(memory $memory (export \"memory\") 1)
(export \"main\" (func $main))
(func $main
(local $_1 i64)
(local $_2 i64)
(local $_3 i64)
(local $hi i64)
(local $y i64)
(local $hi_1 i64)
(local.set $_1 (i64.const 0))
(local.set $_2 (i64.add (call $u256_to_i32 (local.get $_1) (local.get $_1) (local.get $_1) (i64.const 64)) (i64.const 64)))
(local.set $_3 (i64.const 65280))
(local.set $hi (i64.shl (i64.or (i64.shl (i64.or (i64.and (i64.shl (local.get $_1) (i64.const 8)) (local.get $_3)) (i64.and (i64.shr_u (local.get $_1) (i64.const 8)) (i64.const 255))) (i64.const 16)) (call $endian_swap_16 (i64.shr_u (local.get $_1) (i64.const 16)))) (i64.const 32)))
(local.set $y (i64.or (local.get $hi) (call $endian_swap_32 (i64.shr_u (local.get $_1) (i64.const 32)))))
(i64.store (i32.wrap_i64 (local.get $_2)) (local.get $y))
(i64.store (i32.wrap_i64 (i64.add (local.get $_2) (i64.const 8))) (local.get $y))
(i64.store (i32.wrap_i64 (i64.add (local.get $_2) (i64.const 16))) (local.get $y))
(local.set $hi_1 (i64.shl (i64.or (i64.shl (i64.or (i64.and (i64.shl (i64.const 128) (i64.const 8)) (local.get $_3)) (i64.and (i64.shr_u (i64.const 128) (i64.const 8)) (i64.const 255))) (i64.const 16)) (call $endian_swap_16 (i64.shr_u (i64.const 128) (i64.const 16)))) (i64.const 32)))
(i64.store (i32.wrap_i64 (i64.add (local.get $_2) (i64.const 24))) (i64.or (local.get $hi_1) (call $endian_swap_32 (i64.shr_u (i64.const 128) (i64.const 32)))))
(call $eth.revert (i32.wrap_i64 (i64.add (call $u256_to_i32 (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1)) (i64.const 64))) (i32.wrap_i64 (call $u256_to_i32 (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1))))
)
(func $u256_to_i32
(param $x1 i64)
(param $x2 i64)
(param $x3 i64)
(param $x4 i64)
(result i64)
(local $v i64)
(if (i64.ne (i64.extend_i32_u (i64.ne (local.get $v) (i64.or (i64.or (local.get $x1) (local.get $x2)) (local.get $x3)))) (i64.const 0)) (then
(unreachable)))
(if (i64.ne (i64.extend_i32_u (i64.ne (local.get $v) (i64.shr_u (local.get $x4) (i64.const 32)))) (i64.const 0)) (then
(unreachable)))
(local.set $v (local.get $x4))
(local.get $v)
)
(func $endian_swap_16
(param $x i64)
(result i64)
(local $y i64)
(local.set $y (i64.or (i64.and (i64.shl (local.get $x) (i64.const 8)) (i64.const 65280)) (i64.and (i64.shr_u (local.get $x) (i64.const 8)) (i64.const 255))))
(local.get $y)
)
(func $endian_swap_32
(param $x i64)
(result i64)
(local $y i64)
(local $hi i64)
(local.set $hi (i64.shl (call $endian_swap_16 (local.get $x)) (i64.const 16)))
(local.set $y (i64.or (local.get $hi) (call $endian_swap_16 (i64.shr_u (local.get $x) (i64.const 16)))))
(local.get $y)
)
)
(module
(import \"ethereum\" \"codeCopy\" (func $eth.codeCopy (param i32 i32 i32))) (import \"ethereum\" \"codeCopy\" (func $eth.codeCopy (param i32 i32 i32)))
(import \"ethereum\" \"finish\" (func $eth.finish (param i32 i32))) (import \"ethereum\" \"finish\" (func $eth.finish (param i32 i32)))
(memory $memory (export \"memory\") 1) (memory $memory (export \"memory\") 1)

View File

@ -0,0 +1,25 @@
pragma experimental SMTChecker;
// 2 warnings, A.f and A.g
contract A {
uint x;
function f() public view {
assert(x == 1);
}
function g() public view {
assert(x == 1);
}
}
// 2 warnings, B.f and A.g
contract B is A {
function f() public view {
assert(x == 0);
}
}
// ----
// Warning: (113-127): Assertion violation happens here
// Warning: (162-176): Assertion violation happens here
// Warning: (259-273): Assertion violation happens here
// Warning: (162-176): Assertion violation happens here

View File

@ -0,0 +1,27 @@
pragma experimental SMTChecker;
// 2 warnings, A.f and A.g
contract A {
uint x;
function f() public view {
assert(x == 1);
}
function g() public view {
assert(x == 1);
}
}
// 2 warnings, B.f and A.g
contract B is A {
uint y;
function f() public view {
assert(x == 0);
}
}
// ----
// Warning: (113-127): Assertion violation happens here
// Warning: (162-176): Assertion violation happens here
// Warning: (269-283): Assertion violation happens here
// Warning: (162-176): Assertion violation happens here

View File

@ -0,0 +1,47 @@
pragma experimental SMTChecker;
// 2 warnings, A.f and A.g
contract A {
uint x;
function f() public view {
assert(x == 1);
}
function g() public view {
assert(x == 1);
}
}
// 3 warnings, B.f, B.h, A.g
contract B is A {
uint y;
function f() public view {
assert(x == 0);
}
function h() public view {
assert(x == 2);
}
}
// 4 warnings, C.f, C.i, B.h, A.g
contract C is B {
uint z;
function f() public view {
assert(x == 0);
}
function i() public view {
assert(x == 0);
}
}
// ----
// Warning: (113-127): Assertion violation happens here
// Warning: (162-176): Assertion violation happens here
// Warning: (271-285): Assertion violation happens here
// Warning: (320-334): Assertion violation happens here
// Warning: (162-176): Assertion violation happens here
// Warning: (434-448): Assertion violation happens here
// Warning: (483-497): Assertion violation happens here
// Warning: (320-334): Assertion violation happens here
// Warning: (162-176): Assertion violation happens here