diff options
43 files changed, 876 insertions, 441 deletions
diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..86a837c1 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,18 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[*.{cpp,h}] +indent_style = tab + +[*.{py,rst,sh,yml}] +indent_style = space +indent_size = 4 + +[std/**.sol] +indent_style = space +indent_size = 4 diff --git a/docs/contracts.rst b/docs/contracts.rst index 8d7af2c1..a1192d4e 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -20,7 +20,7 @@ Contracts can be created "from outside" or from Solidity contracts. When a contract is created, its constructor (a function with the same name as the contract) is executed once. -A constructor is optional. Only one constructor is allowed and this means +A constructor is optional. Only one constructor is allowed, and this means overloading is not supported. From ``web3.js``, i.e. the JavaScript @@ -273,7 +273,7 @@ be done at declaration. The getter functions have external visibility. If the symbol is accessed internally (i.e. without ``this.``), -it is evaluated as a state variable and if it is accessed externally +it is evaluated as a state variable. If it is accessed externally (i.e. with ``this.``), it is evaluated as a function. :: @@ -321,8 +321,8 @@ is no good way to provide the key for the mapping. Function Modifiers ****************** -Modifiers can be used to easily change the behaviour of functions, for example -to automatically check a condition prior to executing the function. They are +Modifiers can be used to easily change the behaviour of functions. For example, +they can automatically check a condition prior to executing the function. Modifiers are inheritable properties of contracts and may be overridden by derived contracts. :: @@ -405,8 +405,8 @@ inheritable properties of contracts and may be overridden by derived contracts. } } -Multiple modifiers can be applied to a function by specifying them in a -whitespace-separated list and will be evaluated in order. +Multiple modifiers are applied to a function by specifying them in a +whitespace-separated list and are evaluated in the order presented. .. warning:: In an earlier version of Solidity, ``return`` statements in functions @@ -441,7 +441,7 @@ The reason behind allowing side-effects on the memory allocator is that it should be possible to construct complex objects like e.g. lookup-tables. This feature is not yet fully usable. -The compiler does not reserve a storage slot for these variables and every occurrence is +The compiler does not reserve a storage slot for these variables, and every occurrence is replaced by the respective constant expression (which might be computed to a single value by the optimizer). Not all types for constants are implemented at this time. The only supported types are @@ -462,7 +462,7 @@ value types and strings. Constant Functions ****************** -Functions can be declared constant. These functions promise not to modify the state. +Functions can be declared constant in which case they promise not to modify the state. :: @@ -491,7 +491,7 @@ Fallback Function A contract can have exactly one unnamed function. This function cannot have arguments and cannot return anything. It is executed on a call to the contract if none of the other -functions matches the given function identifier (or if no data was supplied at +functions match the given function identifier (or if no data was supplied at all). Furthermore, this function is executed whenever the contract receives plain @@ -567,13 +567,12 @@ the contract and will be incorporated into the blockchain and stay there as long as a block is accessible (forever as of Frontier and Homestead, but this might change with Serenity). Log and event data is not accessible from within contracts (not even from -the contract that created a log). +the contract that created them). SPV proofs for logs are possible, so if an external entity supplies a contract with such a proof, it can check that the log actually -exists inside the blockchain (but be aware of the fact that -ultimately, also the block headers have to be supplied because -the contract can only see the last 256 block hashes). +exists inside the blockchain. But be aware that block headers have to be supplied because +the contract can only see the last 256 block hashes. Up to three parameters can receive the attribute ``indexed`` which will cause the respective arguments @@ -590,7 +589,7 @@ not possible to filter for specific anonymous events by name. All non-indexed arguments will be stored in the data part of the log. .. note:: - Indexed arguments will not be stored themselves, you can only + Indexed arguments will not be stored themselves. You can only search for the values, but it is impossible to retrieve the values themselves. @@ -605,7 +604,7 @@ All non-indexed arguments will be stored in the data part of the log. uint _value ); - function deposit(bytes32 _id) { + function deposit(bytes32 _id) payable { // Any call to this function (even deeply nested) can // be detected from the JavaScript API by filtering // for `Deposit` to be called. @@ -679,9 +678,9 @@ Solidity supports multiple inheritance by copying code including polymorphism. All function calls are virtual, which means that the most derived function is called, except when the contract name is explicitly given. -Even if a contract inherits from multiple other contracts, only a single -contract is created on the blockchain, the code from the base contracts -is always copied into the final contract. +When a contract inherits from multiple contracts, only a single +contract is created on the blockchain, and the code from all the base contracts +is copied into the created contract. The general inheritance system is very similar to `Python's <https://docs.python.org/3/tutorial/classes.html#inheritance>`_, @@ -818,7 +817,7 @@ derived override, but this function will bypass } If ``Base1`` calls a function of ``super``, it does not simply -call this function on one of its base contracts, it rather +call this function on one of its base contracts. Rather, it calls this function on the next base contract in the final inheritance graph, so it will call ``Base2.kill()`` (note that the final inheritance sequence is -- starting with the most @@ -834,7 +833,7 @@ Arguments for Base Constructors =============================== Derived contracts need to provide all arguments needed for -the base constructors. This can be done at two places:: +the base constructors. This can be done in two ways:: pragma solidity ^0.4.0; @@ -849,7 +848,7 @@ the base constructors. This can be done at two places:: } } -Either directly in the inheritance list (``is Base(7)``) or in +One way is directly in the inheritance list (``is Base(7)``). The other is in the way a modifier would be invoked as part of the header of the derived constructor (``Base(_y * _y)``). The first way to do it is more convenient if the constructor argument is a @@ -865,7 +864,7 @@ Multiple Inheritance and Linearization ====================================== Languages that allow multiple inheritance have to deal with -several problems, one of them being the `Diamond Problem <https://en.wikipedia.org/wiki/Multiple_inheritance#The_diamond_problem>`_. +several problems. One is the `Diamond Problem <https://en.wikipedia.org/wiki/Multiple_inheritance#The_diamond_problem>`_. Solidity follows the path of Python and uses "`C3 Linearization <https://en.wikipedia.org/wiki/C3_linearization>`_" to force a specific order in the DAG of base classes. This results in the desirable property of monotonicity but @@ -937,7 +936,7 @@ Interfaces are similar to abstract contracts, but they cannot have any functions Some of these restrictions might be lifted in the future. -Interfaces are basically limited to what the Contract ABI can represent and the conversion between the ABI and +Interfaces are basically limited to what the Contract ABI can represent, and the conversion between the ABI and an Interface should be possible without any information loss. Interfaces are denoted by their own keyword: @@ -976,9 +975,9 @@ contracts (``L.f()`` if ``L`` is the name of the library). Furthermore, if the library were a base contract. Of course, calls to internal functions use the internal calling convention, which means that all internal types can be passed and memory types will be passed by reference and not copied. -In order to realise this in the EVM, code of internal library functions -(and all functions called from therein) will be pulled into the calling -contract and a regular ``JUMP`` call will be used instead of a ``DELEGATECALL``. +To realize this in the EVM, code of internal library functions +and all functions called from therein will be pulled into the calling +contract, and a regular ``JUMP`` call will be used instead of a ``DELEGATECALL``. .. index:: using for, set @@ -1041,8 +1040,8 @@ more advanced example to implement a set). Of course, you do not have to follow this way to use libraries - they can also be used without defining struct -data types, functions also work without any storage -reference parameters, can have multiple storage reference +data types. Functions also work without any storage +reference parameters, and they can have multiple storage reference parameters and in any position. The calls to ``Set.contains``, ``Set.insert`` and ``Set.remove`` diff --git a/docs/introduction-to-smart-contracts.rst b/docs/introduction-to-smart-contracts.rst index 9001a08c..d6c30db8 100644 --- a/docs/introduction-to-smart-contracts.rst +++ b/docs/introduction-to-smart-contracts.rst @@ -33,9 +33,11 @@ Storage The first line simply tells that the source code is written for Solidity version 0.4.0 or anything newer that does not break functionality (up to, but not including, version 0.5.0). This is to ensure that the -contract does not suddenly behave differently with a new compiler version. +contract does not suddenly behave differently with a new compiler version. The keyword ``pragma`` is called that way because, in general, +pragmas are instructions for the compiler about how to treat the +source code (e.g. [pragma once](https://en.wikipedia.org/wiki/Pragma_once)). . -A contract in the sense of Solidity is a collection of code (its functions) and +A contract in the sense of Solidity is a collection of code (its *functions*) and data (its *state*) that resides at a specific address on the Ethereum blockchain. The line ``uint storedData;`` declares a state variable called ``storedData`` of type ``uint`` (unsigned integer of 256 bits). You can think of it as a single slot @@ -47,9 +49,9 @@ or retrieve the value of the variable. To access a state variable, you do not need the prefix ``this.`` as is common in other languages. -This contract does not yet do much apart from (due to the infrastructure -built by Ethereum) allowing anyone to store a single number that is accessible by -anyone in the world without (feasible) a way to prevent you from publishing +This contract does not do much yet (due to the infrastructure +built by Ethereum) apart from allowing anyone to store a single number that is accessible by +anyone in the world without a (feasible) way to prevent you from publishing this number. Of course, anyone could just call ``set`` again with a different value and overwrite your number, but the number will still be stored in the history of the blockchain. Later, we will see how you can impose access restrictions @@ -124,7 +126,7 @@ get the idea - the compiler figures that out for you. The next line, ``mapping (address => uint) public balances;`` also creates a public state variable, but it is a more complex datatype. The type maps addresses to unsigned integers. -Mappings can be seen as hashtables which are +Mappings can be seen as [hash tables](https://en.wikipedia.org/wiki/Hash_table) which are virtually initialized such that every possible key exists and is mapped to a value whose byte-representation is all zeros. This analogy does not go too far, though, as it is neither possible to obtain a list of all keys of @@ -193,7 +195,7 @@ Blockchain Basics ***************** Blockchains as a concept are not too hard to understand for programmers. The reason is that -most of the complications (mining, hashing, elliptic-curve cryptography, peer-to-peer networks, ...) +most of the complications (mining, [hashing](https://en.wikipedia.org/wiki/Cryptographic_hash_function), [elliptic-curve cryptography](https://en.wikipedia.org/wiki/Elliptic_curve_cryptography), [peer-to-peer networks](https://en.wikipedia.org/wiki/Peer-to-peer), etc.) are just there to provide a certain set of features and promises. Once you accept these features as given, you do not have to worry about the underlying technology - or do you have to know how Amazon's AWS works internally in order to use it? @@ -402,7 +404,7 @@ such situations, so that exceptions "bubble up" the call stack. As already said, the called contract (which can be the same as the caller) will receive a freshly cleared instance of memory and has access to the call payload - which will be provided in a separate area called the **calldata**. -After it finished execution, it can return data which will be stored at +After it has finished execution, it can return data which will be stored at a location in the caller's memory preallocated by the caller. Calls are **limited** to a depth of 1024, which means that for more complex @@ -423,8 +425,8 @@ address at runtime. Storage, current address and balance still refer to the calling contract, only the code is taken from the called address. This makes it possible to implement the "library" feature in Solidity: -Reusable library code that can be applied to a contract's storage in -order to e.g. implement a complex data structure. +Reusable library code that can be applied to a contract's storage, e.g. in +order to implement a complex data structure. .. index:: log @@ -436,7 +438,7 @@ that maps all the way up to the block level. This feature called **logs** is used by Solidity in order to implement **events**. Contracts cannot access log data after it has been created, but they can be efficiently accessed from outside the blockchain. -Since some part of the log data is stored in bloom filters, it is +Since some part of the log data is stored in [bloom filters](https://en.wikipedia.org/wiki/Bloom_filter), it is possible to search for this data in an efficient and cryptographically secure way, so network peers that do not download the whole blockchain ("light clients") can still find these logs. diff --git a/docs/solidity-by-example.rst b/docs/solidity-by-example.rst index 4ed474df..ab3f375e 100644 --- a/docs/solidity-by-example.rst +++ b/docs/solidity-by-example.rst @@ -612,7 +612,7 @@ Safe Remote Purchase // block the refund - the withdraw pattern should be used. buyer.transfer(value); - seller.transfer(this.balance)); + seller.transfer(this.balance); } } diff --git a/docs/units-and-global-variables.rst b/docs/units-and-global-variables.rst index 246cc564..779e3819 100644 --- a/docs/units-and-global-variables.rst +++ b/docs/units-and-global-variables.rst @@ -55,7 +55,7 @@ Block and Transaction Properties - ``block.difficulty`` (``uint``): current block difficulty - ``block.gaslimit`` (``uint``): current block gaslimit - ``block.number`` (``uint``): current block number -- ``block.timestamp`` (``uint``): current block timestamp +- ``block.timestamp`` (``uint``): current block timestamp as seconds since unix epoch - ``msg.data`` (``bytes``): complete calldata - ``msg.gas`` (``uint``): remaining gas - ``msg.sender`` (``address``): sender of the message (current call) diff --git a/libdevcore/SHA3.h b/libdevcore/SHA3.h index ce075521..1a561066 100644 --- a/libdevcore/SHA3.h +++ b/libdevcore/SHA3.h @@ -29,28 +29,28 @@ namespace dev { -// SHA-3 convenience routines. +// Keccak-256 convenience routines. -/// Calculate SHA3-256 hash of the given input and load it into the given output. +/// Calculate Keccak-256 hash of the given input and load it into the given output. /// @returns false if o_output.size() != 32. bool keccak256(bytesConstRef _input, bytesRef o_output); -/// Calculate SHA3-256 hash of the given input, returning as a 256-bit hash. +/// Calculate Keccak-256 hash of the given input, returning as a 256-bit hash. inline h256 keccak256(bytesConstRef _input) { h256 ret; keccak256(_input, ret.ref()); return ret; } -/// Calculate SHA3-256 hash of the given input, returning as a 256-bit hash. +/// Calculate Keccak-256 hash of the given input, returning as a 256-bit hash. inline h256 keccak256(bytes const& _input) { return keccak256(bytesConstRef(&_input)); } -/// Calculate SHA3-256 hash of the given input (presented as a binary-filled string), returning as a 256-bit hash. +/// Calculate Keccak-256 hash of the given input (presented as a binary-filled string), returning as a 256-bit hash. inline h256 keccak256(std::string const& _input) { return keccak256(bytesConstRef(_input)); } -/// Calculate SHA3-256 hash of the given input (presented as a FixedHash), returns a 256-bit hash. +/// Calculate Keccak-256 hash of the given input (presented as a FixedHash), returns a 256-bit hash. template<unsigned N> inline h256 keccak256(FixedHash<N> const& _input) { return keccak256(_input.ref()); } -/// Calculate SHA3-256 hash of the given input, possibly interpreting it as nibbles, and return the hash as a string filled with binary data. +/// Calculate Keccak-256 hash of the given input, possibly interpreting it as nibbles, and return the hash as a string filled with binary data. inline std::string keccak256(std::string const& _input, bool _isNibbles) { return asString((_isNibbles ? keccak256(fromHex(_input)) : keccak256(bytesConstRef(&_input))).asBytes()); } -/// Calculate SHA3-256 MAC +/// Calculate Keccak-256 MAC inline void keccak256mac(bytesConstRef _secret, bytesConstRef _plain, bytesRef _output) { keccak256(_secret.toBytes() + _plain.toBytes()).ref().populate(_output); } } diff --git a/libevmasm/SimplificationRules.cpp b/libevmasm/SimplificationRules.cpp index 2976d95f..e6c51f95 100644 --- a/libevmasm/SimplificationRules.cpp +++ b/libevmasm/SimplificationRules.cpp @@ -103,7 +103,7 @@ Rules::Rules() {{Instruction::SMOD, {A, B}}, [=]{ return B.d() == 0 ? 0 : s2u(modWorkaround(u2s(A.d()), u2s(B.d()))); }}, {{Instruction::EXP, {A, B}}, [=]{ return u256(boost::multiprecision::powm(bigint(A.d()), bigint(B.d()), bigint(1) << 256)); }}, {{Instruction::NOT, {A}}, [=]{ return ~A.d(); }}, - {{Instruction::LT, {A, B}}, [=]() { return A.d() < B.d() ? u256(1) : 0; }}, + {{Instruction::LT, {A, B}}, [=]() -> u256 { return A.d() < B.d() ? 1 : 0; }}, {{Instruction::GT, {A, B}}, [=]() -> u256 { return A.d() > B.d() ? 1 : 0; }}, {{Instruction::SLT, {A, B}}, [=]() -> u256 { return u2s(A.d()) < u2s(B.d()) ? 1 : 0; }}, {{Instruction::SGT, {A, B}}, [=]() -> u256 { return u2s(A.d()) > u2s(B.d()) ? 1 : 0; }}, @@ -124,23 +124,26 @@ Rules::Rules() return u256(boost::multiprecision::bit_test(B.d(), testBit) ? B.d() | ~mask : B.d() & mask); }}, - // invariants involving known constants + // invariants involving known constants (commutative instructions will be checked with swapped operants too) {{Instruction::ADD, {X, 0}}, [=]{ return X; }}, {{Instruction::SUB, {X, 0}}, [=]{ return X; }}, + {{Instruction::MUL, {X, 0}}, [=]{ return u256(0); }}, {{Instruction::MUL, {X, 1}}, [=]{ return X; }}, + {{Instruction::DIV, {X, 0}}, [=]{ return u256(0); }}, + {{Instruction::DIV, {0, X}}, [=]{ return u256(0); }}, {{Instruction::DIV, {X, 1}}, [=]{ return X; }}, + {{Instruction::SDIV, {X, 0}}, [=]{ return u256(0); }}, + {{Instruction::SDIV, {0, X}}, [=]{ return u256(0); }}, {{Instruction::SDIV, {X, 1}}, [=]{ return X; }}, - {{Instruction::OR, {X, 0}}, [=]{ return X; }}, - {{Instruction::XOR, {X, 0}}, [=]{ return X; }}, {{Instruction::AND, {X, ~u256(0)}}, [=]{ return X; }}, {{Instruction::AND, {X, 0}}, [=]{ return u256(0); }}, - {{Instruction::MUL, {X, 0}}, [=]{ return u256(0); }}, - {{Instruction::DIV, {X, 0}}, [=]{ return u256(0); }}, - {{Instruction::DIV, {0, X}}, [=]{ return u256(0); }}, + {{Instruction::OR, {X, 0}}, [=]{ return X; }}, + {{Instruction::OR, {X, ~u256(0)}}, [=]{ return ~u256(0); }}, + {{Instruction::XOR, {X, 0}}, [=]{ return X; }}, {{Instruction::MOD, {X, 0}}, [=]{ return u256(0); }}, {{Instruction::MOD, {0, X}}, [=]{ return u256(0); }}, - {{Instruction::OR, {X, ~u256(0)}}, [=]{ return ~u256(0); }}, {{Instruction::EQ, {X, 0}}, [=]() -> Pattern { return {Instruction::ISZERO, {X}}; } }, + // operations involving an expression and itself {{Instruction::AND, {X, X}}, [=]{ return X; }}, {{Instruction::OR, {X, X}}, [=]{ return X; }}, @@ -153,6 +156,7 @@ Rules::Rules() {{Instruction::SGT, {X, X}}, [=]{ return u256(0); }}, {{Instruction::MOD, {X, X}}, [=]{ return u256(0); }}, + // logical instruction combinations {{Instruction::NOT, {{Instruction::NOT, {X}}}}, [=]{ return X; }}, {{Instruction::XOR, {{{X}, {Instruction::XOR, {X, Y}}}}}, [=]{ return Y; }}, {{Instruction::OR, {{{X}, {Instruction::AND, {X, Y}}}}}, [=]{ return X; }}, @@ -160,6 +164,7 @@ Rules::Rules() {{Instruction::AND, {{{X}, {Instruction::NOT, {X}}}}}, [=]{ return u256(0); }}, {{Instruction::OR, {{{X}, {Instruction::NOT, {X}}}}}, [=]{ return ~u256(0); }}, }); + // Double negation of opcodes with binary result for (auto const& op: vector<Instruction>{ Instruction::EQ, @@ -172,14 +177,17 @@ Rules::Rules() {Instruction::ISZERO, {{Instruction::ISZERO, {{op, {X, Y}}}}}}, [=]() -> Pattern { return {op, {X, Y}}; } }); + addRule({ {Instruction::ISZERO, {{Instruction::ISZERO, {{Instruction::ISZERO, {X}}}}}}, [=]() -> Pattern { return {Instruction::ISZERO, {X}}; } }); + addRule({ {Instruction::ISZERO, {{Instruction::XOR, {X, Y}}}}, [=]() -> Pattern { return { Instruction::EQ, {X, Y} }; } }); + // Associative operations for (auto const& opFun: vector<pair<Instruction,function<u256(u256 const&,u256 const&)>>>{ {Instruction::ADD, plus<u256>()}, @@ -210,6 +218,7 @@ Rules::Rules() [=]() -> Pattern { return {op, {{op, {X, Y}}, A}}; } }}); } + // move constants across subtractions addRules(vector<pair<Pattern, function<Pattern()>>>{ { diff --git a/libjulia/backends/AbstractAssembly.h b/libjulia/backends/AbstractAssembly.h new file mode 100644 index 00000000..e3afa2b6 --- /dev/null +++ b/libjulia/backends/AbstractAssembly.h @@ -0,0 +1,89 @@ +/* + 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/>. +*/ +/** + * @date 2017 + * Abstract assembly interface, subclasses of which are to be used with the generic + * bytecode generator. + */ + +#pragma once + +#include <libdevcore/CommonData.h> + +#include <functional> + +namespace dev +{ +struct SourceLocation; +namespace solidity +{ +enum class Instruction: uint8_t; +namespace assembly +{ +struct Instruction; +struct Identifier; +} +} +namespace julia +{ + +class AbstractAssembly +{ +public: + virtual ~AbstractAssembly() {} + + /// Set a new source location valid starting from the next instruction. + virtual void setSourceLocation(SourceLocation const& _location) = 0; + /// Retrieve the current height of the stack. This does not have to be zero + /// at the beginning. + virtual int stackHeight() const = 0; + /// Append an EVM instruction. + virtual void appendInstruction(solidity::Instruction _instruction) = 0; + /// Append a constant. + virtual void appendConstant(u256 const& _constant) = 0; + /// Append a label. + virtual void appendLabel(size_t _labelId) = 0; + /// Append a label reference. + virtual void appendLabelReference(size_t _labelId) = 0; + /// Generate a new unique label. + virtual size_t newLabelId() = 0; + /// Append a reference to a to-be-linked symobl. + /// Currently, we assume that the value is always a 20 byte number. + virtual void appendLinkerSymbol(std::string const& _name) = 0; +}; + +enum class IdentifierContext { LValue, RValue }; + +/// Object that is used to resolve references and generate code for access to identifiers external +/// to inline assembly (not used in standalone assembly mode). +struct ExternalIdentifierAccess +{ + using Resolver = std::function<size_t(solidity::assembly::Identifier const&, IdentifierContext)>; + /// Resolve a an external reference given by the identifier in the given context. + /// @returns the size of the value (number of stack slots) or size_t(-1) if not found. + Resolver resolve; + using CodeGenerator = std::function<void(solidity::assembly::Identifier const&, IdentifierContext, julia::AbstractAssembly&)>; + /// Generate code for retrieving the value (rvalue context) or storing the value (lvalue context) + /// of an identifier. The code should be appended to the assembly. In rvalue context, the value is supposed + /// to be put onto the stack, in lvalue context, the value is assumed to be at the top of the stack. + CodeGenerator generateCode; +}; + + + +} +} diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index 51dd9fd2..5c4f88c4 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -264,7 +264,7 @@ void CompilerContext::appendInlineAssembly( assembly = &replacedAssembly; } - unsigned startStackHeight = stackHeight(); + int startStackHeight = stackHeight(); assembly::ExternalIdentifierAccess identifierAccess; identifierAccess.resolve = [&]( @@ -278,26 +278,26 @@ void CompilerContext::appendInlineAssembly( identifierAccess.generateCode = [&]( assembly::Identifier const& _identifier, assembly::IdentifierContext _context, - eth::Assembly& _assembly + julia::AbstractAssembly& _assembly ) { auto it = std::find(_localVariables.begin(), _localVariables.end(), _identifier.name); solAssert(it != _localVariables.end(), ""); - unsigned stackDepth = _localVariables.end() - it; - int stackDiff = _assembly.deposit() - startStackHeight + stackDepth; + int stackDepth = _localVariables.end() - it; + int stackDiff = _assembly.stackHeight() - startStackHeight + stackDepth; if (_context == assembly::IdentifierContext::LValue) stackDiff -= 1; if (stackDiff < 1 || stackDiff > 16) BOOST_THROW_EXCEPTION( CompilerError() << - errinfo_comment("Stack too deep, try removing local variables.") + errinfo_comment("Stack too deep (" + to_string(stackDiff) + "), try removing local variables.") ); if (_context == assembly::IdentifierContext::RValue) - _assembly.append(dupInstruction(stackDiff)); + _assembly.appendInstruction(dupInstruction(stackDiff)); else { - _assembly.append(swapInstruction(stackDiff)); - _assembly.append(Instruction::POP); + _assembly.appendInstruction(swapInstruction(stackDiff)); + _assembly.appendInstruction(Instruction::POP); } }; diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index 34ef13c0..41940e1a 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -530,7 +530,7 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly) return size_t(-1); return ref->second.valueSize; }; - identifierAccess.generateCode = [&](assembly::Identifier const& _identifier, assembly::IdentifierContext _context, eth::Assembly& _assembly) + identifierAccess.generateCode = [&](assembly::Identifier const& _identifier, assembly::IdentifierContext _context, julia::AbstractAssembly& _assembly) { auto ref = _inlineAssembly.annotation().externalReferences.find(&_identifier); solAssert(ref != _inlineAssembly.annotation().externalReferences.end(), ""); @@ -538,21 +538,25 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly) solAssert(!!decl, ""); if (_context == assembly::IdentifierContext::RValue) { - int const depositBefore = _assembly.deposit(); + int const depositBefore = _assembly.stackHeight(); solAssert(!!decl->type(), "Type of declaration required but not yet determined."); if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(decl)) { solAssert(!ref->second.isOffset && !ref->second.isSlot, ""); functionDef = &m_context.resolveVirtualFunction(*functionDef); - _assembly.append(m_context.functionEntryLabel(*functionDef).pushTag()); + auto functionEntryLabel = m_context.functionEntryLabel(*functionDef).pushTag(); + solAssert(functionEntryLabel.data() <= std::numeric_limits<size_t>::max(), ""); + _assembly.appendLabelReference(size_t(functionEntryLabel.data())); // If there is a runtime context, we have to merge both labels into the same // stack slot in case we store it in storage. if (CompilerContext* rtc = m_context.runtimeContext()) { - _assembly.append(u256(1) << 32); - _assembly.append(Instruction::MUL); - _assembly.append(rtc->functionEntryLabel(*functionDef).toSubAssemblyTag(m_context.runtimeSub())); - _assembly.append(Instruction::OR); + _assembly.appendConstant(u256(1) << 32); + _assembly.appendInstruction(Instruction::MUL); + auto runtimeEntryLabel = rtc->functionEntryLabel(*functionDef).toSubAssemblyTag(m_context.runtimeSub()); + solAssert(runtimeEntryLabel.data() <= std::numeric_limits<size_t>::max(), ""); + _assembly.appendLabelReference(size_t(runtimeEntryLabel.data())); + _assembly.appendInstruction(Instruction::OR); } } else if (auto variable = dynamic_cast<VariableDeclaration const*>(decl)) @@ -570,7 +574,7 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly) } else if (m_context.isLocalVariable(decl)) { - int stackDiff = _assembly.deposit() - m_context.baseStackOffsetOfVariable(*variable); + int stackDiff = _assembly.stackHeight() - m_context.baseStackOffsetOfVariable(*variable); if (ref->second.isSlot || ref->second.isOffset) { solAssert(variable->type()->dataStoredIn(DataLocation::Storage), ""); @@ -587,7 +591,7 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly) // only slot, offset is zero if (ref->second.isOffset) { - _assembly.append(u256(0)); + _assembly.appendConstant(u256(0)); return; } } @@ -601,7 +605,7 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly) errinfo_comment("Stack too deep, try removing local variables.") ); solAssert(variable->type()->sizeOnStack() == 1, ""); - _assembly.append(dupInstruction(stackDiff)); + _assembly.appendInstruction(dupInstruction(stackDiff)); } else solAssert(false, ""); @@ -610,11 +614,11 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly) { solAssert(!ref->second.isOffset && !ref->second.isSlot, ""); solAssert(contract->isLibrary(), ""); - _assembly.appendLibraryAddress(contract->fullyQualifiedName()); + _assembly.appendLinkerSymbol(contract->fullyQualifiedName()); } else solAssert(false, "Invalid declaration type."); - solAssert(_assembly.deposit() - depositBefore == int(ref->second.valueSize), ""); + solAssert(_assembly.stackHeight() - depositBefore == int(ref->second.valueSize), ""); } else { @@ -626,15 +630,15 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly) "Can only assign to stack variables in inline assembly." ); solAssert(variable->type()->sizeOnStack() == 1, ""); - int stackDiff = _assembly.deposit() - m_context.baseStackOffsetOfVariable(*variable) - 1; + int stackDiff = _assembly.stackHeight() - m_context.baseStackOffsetOfVariable(*variable) - 1; if (stackDiff > 16 || stackDiff < 1) BOOST_THROW_EXCEPTION( CompilerError() << errinfo_sourceLocation(_inlineAssembly.location()) << - errinfo_comment("Stack too deep, try removing local variables.") + errinfo_comment("Stack too deep(" + to_string(stackDiff) + "), try removing local variables.") ); - _assembly.append(swapInstruction(stackDiff)); - _assembly.append(Instruction::POP); + _assembly.appendInstruction(swapInstruction(stackDiff)); + _assembly.appendInstruction(Instruction::POP); } }; solAssert(_inlineAssembly.annotation().analysisInfo, ""); diff --git a/libsolidity/inlineasm/AsmAnalysis.cpp b/libsolidity/inlineasm/AsmAnalysis.cpp index dad05a78..e03eea2e 100644 --- a/libsolidity/inlineasm/AsmAnalysis.cpp +++ b/libsolidity/inlineasm/AsmAnalysis.cpp @@ -72,7 +72,7 @@ bool AsmAnalyzer::operator()(assembly::Instruction const& _instruction) bool AsmAnalyzer::operator()(assembly::Literal const& _literal) { ++m_stackHeight; - if (!_literal.isNumber && _literal.value.size() > 32) + if (_literal.kind == assembly::LiteralKind::String && _literal.value.size() > 32) { m_errors.push_back(make_shared<Error>( Error::Type::TypeError, @@ -184,7 +184,7 @@ bool AsmAnalyzer::operator()(assembly::VariableDeclaration const& _varDecl) int const stackHeight = m_stackHeight; bool success = boost::apply_visitor(*this, *_varDecl.value); solAssert(m_stackHeight - stackHeight == 1, "Invalid value size."); - boost::get<Scope::Variable>(m_currentScope->identifiers.at(_varDecl.name)).active = true; + boost::get<Scope::Variable>(m_currentScope->identifiers.at(_varDecl.variable.name)).active = true; m_info.stackHeightInfo[&_varDecl] = m_stackHeight; return success; } @@ -193,7 +193,7 @@ bool AsmAnalyzer::operator()(assembly::FunctionDefinition const& _funDef) { Scope& bodyScope = scope(&_funDef.body); for (auto const& var: _funDef.arguments + _funDef.returns) - boost::get<Scope::Variable>(bodyScope.identifiers.at(var)).active = true; + boost::get<Scope::Variable>(bodyScope.identifiers.at(var.name)).active = true; int const stackHeight = m_stackHeight; m_stackHeight = _funDef.arguments.size() + _funDef.returns.size(); @@ -232,8 +232,9 @@ bool AsmAnalyzer::operator()(assembly::FunctionCall const& _funCall) }, [&](Scope::Function const& _fun) { - arguments = _fun.arguments; - returns = _fun.returns; + /// TODO: compare types too + arguments = _fun.arguments.size(); + returns = _fun.returns.size(); } ))) { diff --git a/libsolidity/inlineasm/AsmCodeGen.cpp b/libsolidity/inlineasm/AsmCodeGen.cpp index c19667b4..b8af9dc6 100644 --- a/libsolidity/inlineasm/AsmCodeGen.cpp +++ b/libsolidity/inlineasm/AsmCodeGen.cpp @@ -32,6 +32,8 @@ #include <libevmasm/SourceLocation.h> #include <libevmasm/Instruction.h> +#include <libjulia/backends/AbstractAssembly.h> + #include <libdevcore/CommonIO.h> #include <boost/range/adaptor/reversed.hpp> @@ -48,14 +50,53 @@ using namespace dev::solidity::assembly; struct GeneratorState { - GeneratorState(ErrorList& _errors, AsmAnalysisInfo& _analysisInfo, eth::Assembly& _assembly): - errors(_errors), info(_analysisInfo), assembly(_assembly) {} + GeneratorState(ErrorList& _errors, AsmAnalysisInfo& _analysisInfo): + errors(_errors), info(_analysisInfo) {} - size_t newLabelId() + ErrorList& errors; + AsmAnalysisInfo info; +}; + +class EthAssemblyAdapter: public julia::AbstractAssembly +{ +public: + EthAssemblyAdapter(eth::Assembly& _assembly): + m_assembly(_assembly) + { + } + virtual void setSourceLocation(SourceLocation const& _location) override + { + m_assembly.setSourceLocation(_location); + } + virtual int stackHeight() const override { return m_assembly.deposit(); } + virtual void appendInstruction(solidity::Instruction _instruction) override + { + m_assembly.append(_instruction); + } + virtual void appendConstant(u256 const& _constant) override { - return assemblyTagToIdentifier(assembly.newTag()); + m_assembly.append(_constant); + } + /// Append a label. + virtual void appendLabel(size_t _labelId) override + { + m_assembly.append(eth::AssemblyItem(eth::Tag, _labelId)); + } + /// Append a label reference. + virtual void appendLabelReference(size_t _labelId) override + { + m_assembly.append(eth::AssemblyItem(eth::PushTag, _labelId)); + } + virtual size_t newLabelId() override + { + return assemblyTagToIdentifier(m_assembly.newTag()); + } + virtual void appendLinkerSymbol(std::string const& _linkerSymbol) override + { + m_assembly.appendLibraryAddress(_linkerSymbol); } +private: size_t assemblyTagToIdentifier(eth::AssemblyItem const& _tag) const { u256 id = _tag.data(); @@ -63,9 +104,7 @@ struct GeneratorState return size_t(id); } - ErrorList& errors; - AsmAnalysisInfo info; - eth::Assembly& assembly; + eth::Assembly& m_assembly; }; class CodeTransform: public boost::static_visitor<> @@ -76,74 +115,84 @@ public: /// @param _identifierAccess used to resolve identifiers external to the inline assembly explicit CodeTransform( GeneratorState& _state, + julia::AbstractAssembly& _assembly, assembly::Block const& _block, assembly::ExternalIdentifierAccess const& _identifierAccess = assembly::ExternalIdentifierAccess() - ): CodeTransform(_state, _block, _identifierAccess, _state.assembly.deposit()) + ): CodeTransform(_state, _assembly, _block, _identifierAccess, _assembly.stackHeight()) { } private: CodeTransform( GeneratorState& _state, + julia::AbstractAssembly& _assembly, assembly::Block const& _block, assembly::ExternalIdentifierAccess const& _identifierAccess, - int _initialDeposit + int _initialStackHeight ): m_state(_state), + m_assembly(_assembly), m_scope(*m_state.info.scopes.at(&_block)), m_identifierAccess(_identifierAccess), - m_initialDeposit(_initialDeposit) + m_initialStackHeight(_initialStackHeight) { - int blockStartDeposit = m_state.assembly.deposit(); + int blockStartStackHeight = m_assembly.stackHeight(); std::for_each(_block.statements.begin(), _block.statements.end(), boost::apply_visitor(*this)); - m_state.assembly.setSourceLocation(_block.location); + m_assembly.setSourceLocation(_block.location); // pop variables for (auto const& identifier: m_scope.identifiers) if (identifier.second.type() == typeid(Scope::Variable)) - m_state.assembly.append(solidity::Instruction::POP); + m_assembly.appendInstruction(solidity::Instruction::POP); - int deposit = m_state.assembly.deposit() - blockStartDeposit; + int deposit = m_assembly.stackHeight() - blockStartStackHeight; solAssert(deposit == 0, "Invalid stack height at end of block."); } public: void operator()(assembly::Instruction const& _instruction) { - m_state.assembly.setSourceLocation(_instruction.location); - m_state.assembly.append(_instruction.instruction); + m_assembly.setSourceLocation(_instruction.location); + m_assembly.appendInstruction(_instruction.instruction); checkStackHeight(&_instruction); } void operator()(assembly::Literal const& _literal) { - m_state.assembly.setSourceLocation(_literal.location); - if (_literal.isNumber) - m_state.assembly.append(u256(_literal.value)); + m_assembly.setSourceLocation(_literal.location); + if (_literal.kind == assembly::LiteralKind::Number) + m_assembly.appendConstant(u256(_literal.value)); + else if (_literal.kind == assembly::LiteralKind::Boolean) + { + if (_literal.value == "true") + m_assembly.appendConstant(u256(1)); + else + m_assembly.appendConstant(u256(0)); + } else { solAssert(_literal.value.size() <= 32, ""); - m_state.assembly.append(u256(h256(_literal.value, h256::FromBinary, h256::AlignLeft))); + m_assembly.appendConstant(u256(h256(_literal.value, h256::FromBinary, h256::AlignLeft))); } checkStackHeight(&_literal); } void operator()(assembly::Identifier const& _identifier) { - m_state.assembly.setSourceLocation(_identifier.location); + m_assembly.setSourceLocation(_identifier.location); // First search internals, then externals. if (m_scope.lookup(_identifier.name, Scope::NonconstVisitor( [=](Scope::Variable& _var) { if (int heightDiff = variableHeightDiff(_var, _identifier.location, false)) - m_state.assembly.append(solidity::dupInstruction(heightDiff)); + m_assembly.appendInstruction(solidity::dupInstruction(heightDiff)); else // Store something to balance the stack - m_state.assembly.append(u256(0)); + m_assembly.appendConstant(u256(0)); }, [=](Scope::Label& _label) { assignLabelIdIfUnset(_label); - m_state.assembly.append(eth::AssemblyItem(eth::PushTag, _label.id)); + m_assembly.appendLabelReference(*_label.id); }, [=](Scope::Function&) { @@ -157,14 +206,14 @@ public: m_identifierAccess.generateCode, "Identifier not found and no external access available." ); - m_identifierAccess.generateCode(_identifier, IdentifierContext::RValue, m_state.assembly); + m_identifierAccess.generateCode(_identifier, IdentifierContext::RValue, m_assembly); checkStackHeight(&_identifier); } void operator()(FunctionalInstruction const& _instr) { for (auto it = _instr.arguments.rbegin(); it != _instr.arguments.rend(); ++it) { - int height = m_state.assembly.deposit(); + int height = m_assembly.stackHeight(); boost::apply_visitor(*this, *it); expectDeposit(1, height); } @@ -177,40 +226,40 @@ public: } void operator()(Label const& _label) { - m_state.assembly.setSourceLocation(_label.location); + m_assembly.setSourceLocation(_label.location); solAssert(m_scope.identifiers.count(_label.name), ""); Scope::Label& label = boost::get<Scope::Label>(m_scope.identifiers.at(_label.name)); assignLabelIdIfUnset(label); - m_state.assembly.append(eth::AssemblyItem(eth::Tag, label.id)); + m_assembly.appendLabel(*label.id); checkStackHeight(&_label); } void operator()(assembly::Assignment const& _assignment) { - m_state.assembly.setSourceLocation(_assignment.location); + m_assembly.setSourceLocation(_assignment.location); generateAssignment(_assignment.variableName, _assignment.location); checkStackHeight(&_assignment); } void operator()(FunctionalAssignment const& _assignment) { - int height = m_state.assembly.deposit(); + int height = m_assembly.stackHeight(); boost::apply_visitor(*this, *_assignment.value); expectDeposit(1, height); - m_state.assembly.setSourceLocation(_assignment.location); + m_assembly.setSourceLocation(_assignment.location); generateAssignment(_assignment.variableName, _assignment.location); checkStackHeight(&_assignment); } void operator()(assembly::VariableDeclaration const& _varDecl) { - int height = m_state.assembly.deposit(); + int height = m_assembly.stackHeight(); boost::apply_visitor(*this, *_varDecl.value); expectDeposit(1, height); - auto& var = boost::get<Scope::Variable>(m_scope.identifiers.at(_varDecl.name)); + auto& var = boost::get<Scope::Variable>(m_scope.identifiers.at(_varDecl.variable.name)); var.stackHeight = height; var.active = true; } void operator()(assembly::Block const& _block) { - CodeTransform(m_state, _block, m_identifierAccess, m_initialDeposit); + CodeTransform(m_state, m_assembly, _block, m_identifierAccess, m_initialStackHeight); checkStackHeight(&_block); } void operator()(assembly::FunctionDefinition const&) @@ -226,8 +275,8 @@ private: { Scope::Variable const& _var = boost::get<Scope::Variable>(*var); if (int heightDiff = variableHeightDiff(_var, _location, true)) - m_state.assembly.append(solidity::swapInstruction(heightDiff - 1)); - m_state.assembly.append(solidity::Instruction::POP); + m_assembly.appendInstruction(solidity::swapInstruction(heightDiff - 1)); + m_assembly.appendInstruction(solidity::Instruction::POP); } else { @@ -235,7 +284,7 @@ private: m_identifierAccess.generateCode, "Identifier not found and no external access available." ); - m_identifierAccess.generateCode(_variableName, IdentifierContext::LValue, m_state.assembly); + m_identifierAccess.generateCode(_variableName, IdentifierContext::LValue, m_assembly); } } @@ -244,7 +293,7 @@ private: /// errors and the (positive) stack height difference otherwise. int variableHeightDiff(Scope::Variable const& _var, SourceLocation const& _location, bool _forSwap) { - int heightDiff = m_state.assembly.deposit() - _var.stackHeight; + int heightDiff = m_assembly.stackHeight() - _var.stackHeight; if (heightDiff <= (_forSwap ? 1 : 0) || heightDiff > (_forSwap ? 17 : 16)) { //@TODO move this to analysis phase. @@ -261,14 +310,14 @@ private: void expectDeposit(int _deposit, int _oldHeight) { - solAssert(m_state.assembly.deposit() == _oldHeight + _deposit, "Invalid stack deposit."); + solAssert(m_assembly.stackHeight() == _oldHeight + _deposit, "Invalid stack deposit."); } void checkStackHeight(void const* _astElement) { solAssert(m_state.info.stackHeightInfo.count(_astElement), "Stack height for AST element not found."); solAssert( - m_state.info.stackHeightInfo.at(_astElement) == m_state.assembly.deposit() - m_initialDeposit, + m_state.info.stackHeightInfo.at(_astElement) == m_assembly.stackHeight() - m_initialStackHeight, "Stack height mismatch between analysis and code generation phase." ); } @@ -276,15 +325,16 @@ private: /// Assigns the label's id to a value taken from eth::Assembly if it has not yet been set. void assignLabelIdIfUnset(Scope::Label& _label) { - if (_label.id == Scope::Label::unassignedLabelId) - _label.id = m_state.newLabelId(); + if (!_label.id) + _label.id.reset(m_assembly.newLabelId()); } GeneratorState& m_state; + julia::AbstractAssembly& m_assembly; Scope& m_scope; ExternalIdentifierAccess m_identifierAccess; - int const m_initialDeposit; + int const m_initialStackHeight; }; eth::Assembly assembly::CodeGenerator::assemble( @@ -294,8 +344,9 @@ eth::Assembly assembly::CodeGenerator::assemble( ) { eth::Assembly assembly; - GeneratorState state(m_errors, _analysisInfo, assembly); - CodeTransform(state, _parsedData, _identifierAccess); + GeneratorState state(m_errors, _analysisInfo); + EthAssemblyAdapter assemblyAdapter(assembly); + CodeTransform(state, assemblyAdapter, _parsedData, _identifierAccess); return assembly; } @@ -306,6 +357,7 @@ void assembly::CodeGenerator::assemble( ExternalIdentifierAccess const& _identifierAccess ) { - GeneratorState state(m_errors, _analysisInfo, _assembly); - CodeTransform(state, _parsedData, _identifierAccess); + GeneratorState state(m_errors, _analysisInfo); + EthAssemblyAdapter assemblyAdapter(_assembly); + CodeTransform(state, assemblyAdapter, _parsedData, _identifierAccess); } diff --git a/libsolidity/inlineasm/AsmData.h b/libsolidity/inlineasm/AsmData.h index d61b5803..8efe1f07 100644 --- a/libsolidity/inlineasm/AsmData.h +++ b/libsolidity/inlineasm/AsmData.h @@ -33,12 +33,18 @@ namespace solidity namespace assembly { +using Type = std::string; + +struct TypedName { SourceLocation location; std::string name; Type type; }; +using TypedNameList = std::vector<TypedName>; + /// What follows are the AST nodes for assembly. /// Direct EVM instruction (except PUSHi and JUMPDEST) struct Instruction { SourceLocation location; solidity::Instruction instruction; }; /// Literal number or string (up to 32 bytes) -struct Literal { SourceLocation location; bool isNumber; std::string value; }; +enum class LiteralKind { Number, Boolean, String }; +struct Literal { SourceLocation location; LiteralKind kind; std::string value; Type type; }; /// External / internal identifier or label reference struct Identifier { SourceLocation location; std::string name; }; struct FunctionalInstruction; @@ -52,18 +58,18 @@ struct FunctionDefinition; struct FunctionCall; struct Block; using Statement = boost::variant<Instruction, Literal, Label, Assignment, Identifier, FunctionalAssignment, FunctionCall, FunctionalInstruction, VariableDeclaration, FunctionDefinition, Block>; -/// Functional assignment ("x := mload(20)", expects push-1-expression on the right hand +/// Functional assignment ("x := mload(20:u256)", expects push-1-expression on the right hand /// side and requires x to occupy exactly one stack slot. struct FunctionalAssignment { SourceLocation location; Identifier variableName; std::shared_ptr<Statement> value; }; -/// Functional instruction, e.g. "mul(mload(20), add(2, x))" +/// Functional instruction, e.g. "mul(mload(20:u256), add(2:u256, x))" struct FunctionalInstruction { SourceLocation location; Instruction instruction; std::vector<Statement> arguments; }; struct FunctionCall { SourceLocation location; Identifier functionName; std::vector<Statement> arguments; }; -/// Block-scope variable declaration ("let x := mload(20)"), non-hoisted -struct VariableDeclaration { SourceLocation location; std::string name; std::shared_ptr<Statement> value; }; +/// Block-scope variable declaration ("let x:u256 := mload(20:u256)"), non-hoisted +struct VariableDeclaration { SourceLocation location; TypedName variable; std::shared_ptr<Statement> value; }; /// Block that creates a scope (frees declared stack variables) struct Block { SourceLocation location; std::vector<Statement> statements; }; /// Function definition ("function f(a, b) -> (d, e) { ... }") -struct FunctionDefinition { SourceLocation location; std::string name; std::vector<std::string> arguments; std::vector<std::string> returns; Block body; }; +struct FunctionDefinition { SourceLocation location; std::string name; TypedNameList arguments; TypedNameList returns; Block body; }; struct LocationExtractor: boost::static_visitor<SourceLocation> { diff --git a/libsolidity/inlineasm/AsmParser.cpp b/libsolidity/inlineasm/AsmParser.cpp index d9b0b3e0..a96984f5 100644 --- a/libsolidity/inlineasm/AsmParser.cpp +++ b/libsolidity/inlineasm/AsmParser.cpp @@ -201,16 +201,47 @@ assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher) } else ret = Identifier{location(), literal}; + m_scanner->next(); break; } case Token::StringLiteral: case Token::Number: + case Token::TrueLiteral: + case Token::FalseLiteral: { - ret = Literal{ + LiteralKind kind = LiteralKind::Number; + switch (m_scanner->currentToken()) + { + case Token::StringLiteral: + kind = LiteralKind::String; + break; + case Token::Number: + kind = LiteralKind::Number; + break; + case Token::TrueLiteral: + case Token::FalseLiteral: + kind = LiteralKind::Boolean; + break; + default: + break; + } + + Literal literal{ location(), - m_scanner->currentToken() == Token::Number, - m_scanner->currentLiteral() + kind, + m_scanner->currentLiteral(), + "" }; + m_scanner->next(); + if (m_julia) + { + expectToken(Token::Colon); + literal.location.end = endPosition(); + literal.type = expectAsmIdentifier(); + } + else if (kind == LiteralKind::Boolean) + fatalParserError("True and false are not valid literals."); + ret = std::move(literal); break; } default: @@ -220,7 +251,6 @@ assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher) "Expected elementary inline assembly operation." ); } - m_scanner->next(); return ret; } @@ -228,7 +258,7 @@ assembly::VariableDeclaration Parser::parseVariableDeclaration() { VariableDeclaration varDecl = createWithLocation<VariableDeclaration>(); expectToken(Token::Let); - varDecl.name = expectAsmIdentifier(); + varDecl.variable = parseTypedName(); expectToken(Token::Colon); expectToken(Token::Assign); varDecl.value.reset(new Statement(parseExpression())); @@ -244,7 +274,7 @@ assembly::FunctionDefinition Parser::parseFunctionDefinition() expectToken(Token::LParen); while (m_scanner->currentToken() != Token::RParen) { - funDef.arguments.push_back(expectAsmIdentifier()); + funDef.arguments.emplace_back(parseTypedName()); if (m_scanner->currentToken() == Token::RParen) break; expectToken(Token::Comma); @@ -256,7 +286,7 @@ assembly::FunctionDefinition Parser::parseFunctionDefinition() expectToken(Token::GreaterThan); while (true) { - funDef.returns.push_back(expectAsmIdentifier()); + funDef.returns.emplace_back(parseTypedName()); if (m_scanner->currentToken() == Token::LBrace) break; expectToken(Token::Comma); @@ -335,10 +365,31 @@ assembly::Statement Parser::parseFunctionalInstruction(assembly::Statement&& _in return {}; } +TypedName Parser::parseTypedName() +{ + TypedName typedName = createWithLocation<TypedName>(); + typedName.name = expectAsmIdentifier(); + if (m_julia) + { + expectToken(Token::Colon); + typedName.location.end = endPosition(); + typedName.type = expectAsmIdentifier(); + } + return typedName; +} + string Parser::expectAsmIdentifier() { string name = m_scanner->currentLiteral(); - if (!m_julia && instructions().count(name)) + if (m_julia) + { + if (m_scanner->currentToken() == Token::Bool) + { + m_scanner->next(); + return name; + } + } + else if (instructions().count(name)) fatalParserError("Cannot use instruction names for identifier names."); expectToken(Token::Identifier); return name; diff --git a/libsolidity/inlineasm/AsmParser.h b/libsolidity/inlineasm/AsmParser.h index c55fd2ac..addc1725 100644 --- a/libsolidity/inlineasm/AsmParser.h +++ b/libsolidity/inlineasm/AsmParser.h @@ -69,6 +69,7 @@ protected: VariableDeclaration parseVariableDeclaration(); FunctionDefinition parseFunctionDefinition(); Statement parseFunctionalInstruction(Statement&& _instruction); + TypedName parseTypedName(); std::string expectAsmIdentifier(); private: diff --git a/libsolidity/inlineasm/AsmPrinter.cpp b/libsolidity/inlineasm/AsmPrinter.cpp index 4a6f975d..636e61b8 100644 --- a/libsolidity/inlineasm/AsmPrinter.cpp +++ b/libsolidity/inlineasm/AsmPrinter.cpp @@ -46,8 +46,16 @@ string AsmPrinter::operator()(assembly::Instruction const& _instruction) string AsmPrinter::operator()(assembly::Literal const& _literal) { - if (_literal.isNumber) - return _literal.value; + switch (_literal.kind) + { + case LiteralKind::Number: + return _literal.value + appendTypeName(_literal.type); + case LiteralKind::Boolean: + return ((_literal.value == "true") ? "true" : "false") + appendTypeName(_literal.type); + case LiteralKind::String: + break; + } + string out; for (char c: _literal.value) if (c == '\\') @@ -74,7 +82,7 @@ string AsmPrinter::operator()(assembly::Literal const& _literal) } else out += c; - return "\"" + out + "\""; + return "\"" + out + "\"" + appendTypeName(_literal.type); } string AsmPrinter::operator()(assembly::Identifier const& _identifier) @@ -113,14 +121,30 @@ string AsmPrinter::operator()(assembly::FunctionalAssignment const& _functionalA string AsmPrinter::operator()(assembly::VariableDeclaration const& _variableDeclaration) { - return "let " + _variableDeclaration.name + " := " + boost::apply_visitor(*this, *_variableDeclaration.value); + return "let " + _variableDeclaration.variable.name + appendTypeName(_variableDeclaration.variable.type) + " := " + boost::apply_visitor(*this, *_variableDeclaration.value); } string AsmPrinter::operator()(assembly::FunctionDefinition const& _functionDefinition) { - string out = "function " + _functionDefinition.name + "(" + boost::algorithm::join(_functionDefinition.arguments, ", ") + ")"; + string out = "function " + _functionDefinition.name + "("; + out += boost::algorithm::join( + _functionDefinition.arguments | boost::adaptors::transformed( + [this](TypedName argument) { return argument.name + appendTypeName(argument.type); } + ), + ", " + ); + out += ")"; if (!_functionDefinition.returns.empty()) - out += " -> " + boost::algorithm::join(_functionDefinition.returns, ", "); + { + out += " -> "; + out += boost::algorithm::join( + _functionDefinition.returns | boost::adaptors::transformed( + [this](TypedName argument) { return argument.name + appendTypeName(argument.type); } + ), + ", " + ); + } + return out + "\n" + (*this)(_functionDefinition.body); } @@ -145,3 +169,10 @@ string AsmPrinter::operator()(Block const& _block) boost::replace_all(body, "\n", "\n "); return "{\n " + body + "\n}"; } + +string AsmPrinter::appendTypeName(std::string const& _type) +{ + if (m_julia) + return ":" + _type; + return ""; +} diff --git a/libsolidity/inlineasm/AsmPrinter.h b/libsolidity/inlineasm/AsmPrinter.h index 038c6d85..282fd7e3 100644 --- a/libsolidity/inlineasm/AsmPrinter.h +++ b/libsolidity/inlineasm/AsmPrinter.h @@ -60,6 +60,8 @@ public: std::string operator()(assembly::Block const& _block); private: + std::string appendTypeName(std::string const& _type); + bool m_julia = false; }; diff --git a/libsolidity/inlineasm/AsmScope.cpp b/libsolidity/inlineasm/AsmScope.cpp index 609dca16..e3f4615a 100644 --- a/libsolidity/inlineasm/AsmScope.cpp +++ b/libsolidity/inlineasm/AsmScope.cpp @@ -32,15 +32,17 @@ bool Scope::registerLabel(string const& _name) return true; } -bool Scope::registerVariable(string const& _name) +bool Scope::registerVariable(string const& _name, JuliaType const& _type) { if (exists(_name)) return false; - identifiers[_name] = Variable(); + Variable variable; + variable.type = _type; + identifiers[_name] = variable; return true; } -bool Scope::registerFunction(string const& _name, size_t _arguments, size_t _returns) +bool Scope::registerFunction(string const& _name, std::vector<JuliaType> const& _arguments, std::vector<JuliaType> const& _returns) { if (exists(_name)) return false; diff --git a/libsolidity/inlineasm/AsmScope.h b/libsolidity/inlineasm/AsmScope.h index b70bee67..70786dce 100644 --- a/libsolidity/inlineasm/AsmScope.h +++ b/libsolidity/inlineasm/AsmScope.h @@ -23,6 +23,7 @@ #include <libsolidity/interface/Exceptions.h> #include <boost/variant.hpp> +#include <boost/optional.hpp> #include <functional> #include <memory> @@ -61,6 +62,8 @@ struct GenericVisitor<>: public boost::static_visitor<> { struct Scope { + using JuliaType = std::string; + struct Variable { /// Used during code generation to store the stack height. @todo move there. @@ -68,28 +71,32 @@ struct Scope /// Used during analysis to check whether we already passed the declaration inside the block. /// @todo move there. bool active = false; + JuliaType type; }; struct Label { - size_t id = unassignedLabelId; - static const size_t unassignedLabelId = 0; + boost::optional<size_t> id; }; struct Function { - Function(size_t _arguments, size_t _returns): arguments(_arguments), returns(_returns) {} - size_t arguments = 0; - size_t returns = 0; + Function(std::vector<JuliaType> const& _arguments, std::vector<JuliaType> const& _returns): arguments(_arguments), returns(_returns) {} + std::vector<JuliaType> arguments; + std::vector<JuliaType> returns; }; using Identifier = boost::variant<Variable, Label, Function>; using Visitor = GenericVisitor<Variable const, Label const, Function const>; using NonconstVisitor = GenericVisitor<Variable, Label, Function>; - bool registerVariable(std::string const& _name); + bool registerVariable(std::string const& _name, JuliaType const& _type); bool registerLabel(std::string const& _name); - bool registerFunction(std::string const& _name, size_t _arguments, size_t _returns); + bool registerFunction( + std::string const& _name, + std::vector<JuliaType> const& _arguments, + std::vector<JuliaType> const& _returns + ); /// Looks up the identifier in this or super scopes and returns a valid pointer if found /// or a nullptr if not found. Variable lookups up across function boundaries will fail, as diff --git a/libsolidity/inlineasm/AsmScopeFiller.cpp b/libsolidity/inlineasm/AsmScopeFiller.cpp index 4a651388..eb10dbb3 100644 --- a/libsolidity/inlineasm/AsmScopeFiller.cpp +++ b/libsolidity/inlineasm/AsmScopeFiller.cpp @@ -59,13 +59,19 @@ bool ScopeFiller::operator()(Label const& _item) bool ScopeFiller::operator()(assembly::VariableDeclaration const& _varDecl) { - return registerVariable(_varDecl.name, _varDecl.location, *m_currentScope); + return registerVariable(_varDecl.variable, _varDecl.location, *m_currentScope); } bool ScopeFiller::operator()(assembly::FunctionDefinition const& _funDef) { bool success = true; - if (!m_currentScope->registerFunction(_funDef.name, _funDef.arguments.size(), _funDef.returns.size())) + vector<Scope::JuliaType> arguments; + for (auto const& _argument: _funDef.arguments) + arguments.push_back(_argument.type); + vector<Scope::JuliaType> returns; + for (auto const& _return: _funDef.returns) + returns.push_back(_return.type); + if (!m_currentScope->registerFunction(_funDef.name, arguments, returns)) { //@TODO secondary location m_errors.push_back(make_shared<Error>( @@ -102,14 +108,14 @@ bool ScopeFiller::operator()(Block const& _block) return success; } -bool ScopeFiller::registerVariable(string const& _name, SourceLocation const& _location, Scope& _scope) +bool ScopeFiller::registerVariable(TypedName const& _name, SourceLocation const& _location, Scope& _scope) { - if (!_scope.registerVariable(_name)) + if (!_scope.registerVariable(_name.name, _name.type)) { //@TODO secondary location m_errors.push_back(make_shared<Error>( Error::Type::DeclarationError, - "Variable name " + _name + " already taken in this scope.", + "Variable name " + _name.name + " already taken in this scope.", _location )); return false; diff --git a/libsolidity/inlineasm/AsmScopeFiller.h b/libsolidity/inlineasm/AsmScopeFiller.h index bb62948b..61428eea 100644 --- a/libsolidity/inlineasm/AsmScopeFiller.h +++ b/libsolidity/inlineasm/AsmScopeFiller.h @@ -34,6 +34,7 @@ namespace solidity namespace assembly { +struct TypedName; struct Literal; struct Block; struct Label; @@ -72,7 +73,7 @@ public: private: bool registerVariable( - std::string const& _name, + TypedName const& _name, SourceLocation const& _location, Scope& _scope ); diff --git a/libsolidity/inlineasm/AsmStack.h b/libsolidity/inlineasm/AsmStack.h index 77a7e02a..e223ccc9 100644 --- a/libsolidity/inlineasm/AsmStack.h +++ b/libsolidity/inlineasm/AsmStack.h @@ -24,6 +24,8 @@ #include <libsolidity/interface/Exceptions.h> +#include <libjulia/backends/AbstractAssembly.h> + #include <string> #include <functional> @@ -51,7 +53,7 @@ struct ExternalIdentifierAccess /// Resolve a an external reference given by the identifier in the given context. /// @returns the size of the value (number of stack slots) or size_t(-1) if not found. Resolver resolve; - using CodeGenerator = std::function<void(assembly::Identifier const&, IdentifierContext, eth::Assembly&)>; + using CodeGenerator = std::function<void(assembly::Identifier const&, IdentifierContext, julia::AbstractAssembly&)>; /// Generate code for retrieving the value (rvalue context) or storing the value (lvalue context) /// of an identifier. The code should be appended to the assembly. In rvalue context, the value is supposed /// to be put onto the stack, in lvalue context, the value is assumed to be at the top of the stack. diff --git a/libsolidity/interface/ABI.cpp b/libsolidity/interface/ABI.cpp new file mode 100644 index 00000000..12f958fc --- /dev/null +++ b/libsolidity/interface/ABI.cpp @@ -0,0 +1,116 @@ +/* + 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/>. +*/ +/** + * Utilities to handle the Contract ABI (https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI) + */ + +#include <libsolidity/interface/ABI.h> +#include <boost/range/irange.hpp> +#include <libsolidity/ast/AST.h> + +using namespace std; +using namespace dev; +using namespace dev::solidity; + +Json::Value ABI::generate(ContractDefinition const& _contractDef) +{ + Json::Value abi(Json::arrayValue); + + for (auto it: _contractDef.interfaceFunctions()) + { + auto externalFunctionType = it.second->interfaceFunctionType(); + Json::Value method; + method["type"] = "function"; + method["name"] = it.second->declaration().name(); + method["constant"] = it.second->isConstant(); + method["payable"] = it.second->isPayable(); + method["inputs"] = formatTypeList( + externalFunctionType->parameterNames(), + externalFunctionType->parameterTypes(), + _contractDef.isLibrary() + ); + method["outputs"] = formatTypeList( + externalFunctionType->returnParameterNames(), + externalFunctionType->returnParameterTypes(), + _contractDef.isLibrary() + ); + abi.append(method); + } + if (_contractDef.constructor()) + { + Json::Value method; + method["type"] = "constructor"; + auto externalFunction = FunctionType(*_contractDef.constructor(), false).interfaceFunctionType(); + solAssert(!!externalFunction, ""); + method["payable"] = externalFunction->isPayable(); + method["inputs"] = formatTypeList( + externalFunction->parameterNames(), + externalFunction->parameterTypes(), + _contractDef.isLibrary() + ); + abi.append(method); + } + if (_contractDef.fallbackFunction()) + { + auto externalFunctionType = FunctionType(*_contractDef.fallbackFunction(), false).interfaceFunctionType(); + solAssert(!!externalFunctionType, ""); + Json::Value method; + method["type"] = "fallback"; + method["payable"] = externalFunctionType->isPayable(); + abi.append(method); + } + for (auto const& it: _contractDef.interfaceEvents()) + { + Json::Value event; + event["type"] = "event"; + event["name"] = it->name(); + event["anonymous"] = it->isAnonymous(); + Json::Value params(Json::arrayValue); + for (auto const& p: it->parameters()) + { + solAssert(!!p->annotation().type->interfaceType(false), ""); + Json::Value input; + input["name"] = p->name(); + input["type"] = p->annotation().type->interfaceType(false)->canonicalName(false); + input["indexed"] = p->isIndexed(); + params.append(input); + } + event["inputs"] = params; + abi.append(event); + } + + return abi; +} + +Json::Value ABI::formatTypeList( + vector<string> const& _names, + vector<TypePointer> const& _types, + bool _forLibrary +) +{ + Json::Value params(Json::arrayValue); + solAssert(_names.size() == _types.size(), "Names and types vector size does not match"); + for (unsigned i = 0; i < _names.size(); ++i) + { + solAssert(_types[i], ""); + Json::Value param; + param["name"] = _names[i]; + param["type"] = _types[i]->canonicalName(_forLibrary); + params.append(param); + } + return params; +} diff --git a/libsolidity/interface/ABI.h b/libsolidity/interface/ABI.h new file mode 100644 index 00000000..95b162a9 --- /dev/null +++ b/libsolidity/interface/ABI.h @@ -0,0 +1,56 @@ +/* + 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/>. +*/ +/** + * Utilities to handle the Contract ABI (https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI) + */ + +#pragma once + +#include <string> +#include <memory> +#include <json/json.h> + +namespace dev +{ +namespace solidity +{ + +// Forward declarations +class ContractDefinition; +class Type; +using TypePointer = std::shared_ptr<Type const>; + +class ABI +{ +public: + /// Get the ABI Interface of the contract + /// @param _contractDef The contract definition + /// @return A JSONrepresentation of the contract's ABI Interface + static Json::Value generate(ContractDefinition const& _contractDef); +private: + /// @returns a json value suitable for a list of types in function input or output + /// parameters or other places. If @a _forLibrary is true, complex types are referenced + /// by name, otherwise they are anonymously expanded. + static Json::Value formatTypeList( + std::vector<std::string> const& _names, + std::vector<TypePointer> const& _types, + bool _forLibrary + ); +}; + +} +} diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 5c836358..72712298 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -37,7 +37,8 @@ #include <libsolidity/analysis/PostTypeChecker.h> #include <libsolidity/analysis/SyntaxChecker.h> #include <libsolidity/codegen/Compiler.h> -#include <libsolidity/interface/InterfaceHandler.h> +#include <libsolidity/interface/ABI.h> +#include <libsolidity/interface/Natspec.h> #include <libsolidity/interface/GasEstimator.h> #include <libsolidity/formal/Why3Translator.h> @@ -219,8 +220,8 @@ bool CompilerStack::analyze() TypeChecker typeChecker(m_errors); if (typeChecker.checkTypeRequirements(*contract)) { - contract->setDevDocumentation(InterfaceHandler::devDocumentation(*contract)); - contract->setUserDocumentation(InterfaceHandler::userDocumentation(*contract)); + contract->setDevDocumentation(Natspec::devDocumentation(*contract)); + contract->setUserDocumentation(Natspec::userDocumentation(*contract)); } else noErrors = false; @@ -444,17 +445,31 @@ map<string, unsigned> CompilerStack::sourceIndices() const return indices; } -Json::Value const& CompilerStack::interface(string const& _contractName) const +Json::Value const& CompilerStack::contractABI(string const& _contractName) const { - return metadata(_contractName, DocumentationType::ABIInterface); + return contractABI(contract(_contractName)); } -Json::Value const& CompilerStack::metadata(string const& _contractName, DocumentationType _type) const +Json::Value const& CompilerStack::contractABI(Contract const& _contract) const { - return metadata(contract(_contractName), _type); + if (m_stackState < AnalysisSuccessful) + BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful.")); + + solAssert(_contract.contract, ""); + + // caches the result + if (!_contract.abi) + _contract.abi.reset(new Json::Value(ABI::generate(*_contract.contract))); + + return *_contract.abi; } -Json::Value const& CompilerStack::metadata(Contract const& _contract, DocumentationType _type) const +Json::Value const& CompilerStack::natspec(string const& _contractName, DocumentationType _type) const +{ + return natspec(contract(_contractName), _type); +} + +Json::Value const& CompilerStack::natspec(Contract const& _contract, DocumentationType _type) const { if (m_stackState < AnalysisSuccessful) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful.")); @@ -471,16 +486,13 @@ Json::Value const& CompilerStack::metadata(Contract const& _contract, Documentat case DocumentationType::NatspecDev: doc = &_contract.devDocumentation; break; - case DocumentationType::ABIInterface: - doc = &_contract.interface; - break; default: BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Illegal documentation type.")); } // caches the result if (!*doc) - doc->reset(new Json::Value(InterfaceHandler::documentation(*_contract.contract, _type))); + doc->reset(new Json::Value(Natspec::documentation(*_contract.contract, _type))); return *(*doc); } @@ -830,9 +842,9 @@ string CompilerStack::createOnChainMetadata(Contract const& _contract) const for (auto const& library: m_libraries) meta["settings"]["libraries"][library.first] = "0x" + toHex(library.second.asBytes()); - meta["output"]["abi"] = metadata(_contract, DocumentationType::ABIInterface); - meta["output"]["userdoc"] = metadata(_contract, DocumentationType::NatspecUser); - meta["output"]["devdoc"] = metadata(_contract, DocumentationType::NatspecDev); + meta["output"]["abi"] = contractABI(_contract); + meta["output"]["userdoc"] = natspec(_contract, DocumentationType::NatspecUser); + meta["output"]["devdoc"] = natspec(_contract, DocumentationType::NatspecDev); return jsonCompactPrint(meta); } diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index c1d344ca..84d15d70 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -59,15 +59,14 @@ class FunctionDefinition; class SourceUnit; class Compiler; class GlobalContext; -class InterfaceHandler; +class Natspec; class Error; class DeclarationContainer; enum class DocumentationType: uint8_t { NatspecUser = 1, - NatspecDev, - ABIInterface + NatspecDev }; /** @@ -173,14 +172,14 @@ public: /// @returns a mapping assigning each source name its index inside the vector returned /// by sourceNames(). std::map<std::string, unsigned> sourceIndices() const; - /// @returns a JSON representing the contract interface. + /// @returns a JSON representing the contract ABI. /// Prerequisite: Successful call to parse or compile. - Json::Value const& interface(std::string const& _contractName = "") const; + Json::Value const& contractABI(std::string const& _contractName = "") const; /// @returns a JSON representing the contract's documentation. /// Prerequisite: Successful call to parse or compile. /// @param type The type of the documentation to get. /// Can be one of 4 types defined at @c DocumentationType - Json::Value const& metadata(std::string const& _contractName, DocumentationType _type) const; + Json::Value const& natspec(std::string const& _contractName, DocumentationType _type) const; std::string const& onChainMetadata(std::string const& _contractName) const; void useMetadataLiteralSources(bool _metadataLiteralSources) { m_metadataLiteralSources = _metadataLiteralSources; } @@ -230,7 +229,7 @@ private: eth::LinkerObject runtimeObject; eth::LinkerObject cloneObject; std::string onChainMetadata; ///< The metadata json that will be hashed into the chain. - mutable std::unique_ptr<Json::Value const> interface; + mutable std::unique_ptr<Json::Value const> abi; mutable std::unique_ptr<Json::Value const> userDocumentation; mutable std::unique_ptr<Json::Value const> devDocumentation; mutable std::unique_ptr<std::string const> sourceMapping; @@ -267,7 +266,8 @@ private: std::string createOnChainMetadata(Contract const& _contract) const; std::string computeSourceMapping(eth::AssemblyItems const& _items) const; - Json::Value const& metadata(Contract const&, DocumentationType _type) const; + Json::Value const& contractABI(Contract const&) const; + Json::Value const& natspec(Contract const&, DocumentationType _type) const; struct Remapping { diff --git a/libsolidity/interface/InterfaceHandler.cpp b/libsolidity/interface/InterfaceHandler.cpp deleted file mode 100644 index 6c1bb0c4..00000000 --- a/libsolidity/interface/InterfaceHandler.cpp +++ /dev/null @@ -1,197 +0,0 @@ - -#include <libsolidity/interface/InterfaceHandler.h> -#include <boost/range/irange.hpp> -#include <libsolidity/ast/AST.h> -#include <libsolidity/interface/CompilerStack.h> - -using namespace std; -using namespace dev; -using namespace dev::solidity; - -Json::Value InterfaceHandler::documentation( - ContractDefinition const& _contractDef, - DocumentationType _type -) -{ - switch(_type) - { - case DocumentationType::NatspecUser: - return userDocumentation(_contractDef); - case DocumentationType::NatspecDev: - return devDocumentation(_contractDef); - case DocumentationType::ABIInterface: - return abiInterface(_contractDef); - } - - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown documentation type")); -} - -Json::Value InterfaceHandler::abiInterface(ContractDefinition const& _contractDef) -{ - Json::Value abi(Json::arrayValue); - - for (auto it: _contractDef.interfaceFunctions()) - { - auto externalFunctionType = it.second->interfaceFunctionType(); - Json::Value method; - method["type"] = "function"; - method["name"] = it.second->declaration().name(); - method["constant"] = it.second->isConstant(); - method["payable"] = it.second->isPayable(); - method["inputs"] = formatTypeList( - externalFunctionType->parameterNames(), - externalFunctionType->parameterTypes(), - _contractDef.isLibrary() - ); - method["outputs"] = formatTypeList( - externalFunctionType->returnParameterNames(), - externalFunctionType->returnParameterTypes(), - _contractDef.isLibrary() - ); - abi.append(method); - } - if (_contractDef.constructor()) - { - Json::Value method; - method["type"] = "constructor"; - auto externalFunction = FunctionType(*_contractDef.constructor(), false).interfaceFunctionType(); - solAssert(!!externalFunction, ""); - method["payable"] = externalFunction->isPayable(); - method["inputs"] = formatTypeList( - externalFunction->parameterNames(), - externalFunction->parameterTypes(), - _contractDef.isLibrary() - ); - abi.append(method); - } - if (_contractDef.fallbackFunction()) - { - auto externalFunctionType = FunctionType(*_contractDef.fallbackFunction(), false).interfaceFunctionType(); - solAssert(!!externalFunctionType, ""); - Json::Value method; - method["type"] = "fallback"; - method["payable"] = externalFunctionType->isPayable(); - abi.append(method); - } - for (auto const& it: _contractDef.interfaceEvents()) - { - Json::Value event; - event["type"] = "event"; - event["name"] = it->name(); - event["anonymous"] = it->isAnonymous(); - Json::Value params(Json::arrayValue); - for (auto const& p: it->parameters()) - { - solAssert(!!p->annotation().type->interfaceType(false), ""); - Json::Value input; - input["name"] = p->name(); - input["type"] = p->annotation().type->interfaceType(false)->canonicalName(false); - input["indexed"] = p->isIndexed(); - params.append(input); - } - event["inputs"] = params; - abi.append(event); - } - - return abi; -} - -Json::Value InterfaceHandler::userDocumentation(ContractDefinition const& _contractDef) -{ - Json::Value doc; - Json::Value methods(Json::objectValue); - - for (auto const& it: _contractDef.interfaceFunctions()) - if (it.second->hasDeclaration()) - if (auto const* f = dynamic_cast<FunctionDefinition const*>(&it.second->declaration())) - { - string value = extractDoc(f->annotation().docTags, "notice"); - if (!value.empty()) - { - Json::Value user; - // since @notice is the only user tag if missing function should not appear - user["notice"] = Json::Value(value); - methods[it.second->externalSignature()] = user; - } - } - doc["methods"] = methods; - - return doc; -} - -Json::Value InterfaceHandler::devDocumentation(ContractDefinition const& _contractDef) -{ - Json::Value doc; - Json::Value methods(Json::objectValue); - - auto author = extractDoc(_contractDef.annotation().docTags, "author"); - if (!author.empty()) - doc["author"] = author; - auto title = extractDoc(_contractDef.annotation().docTags, "title"); - if (!title.empty()) - doc["title"] = title; - - for (auto const& it: _contractDef.interfaceFunctions()) - { - if (!it.second->hasDeclaration()) - continue; - Json::Value method; - if (auto fun = dynamic_cast<FunctionDefinition const*>(&it.second->declaration())) - { - auto dev = extractDoc(fun->annotation().docTags, "dev"); - if (!dev.empty()) - method["details"] = Json::Value(dev); - - auto author = extractDoc(fun->annotation().docTags, "author"); - if (!author.empty()) - method["author"] = author; - - auto ret = extractDoc(fun->annotation().docTags, "return"); - if (!ret.empty()) - method["return"] = ret; - - Json::Value params(Json::objectValue); - auto paramRange = fun->annotation().docTags.equal_range("param"); - for (auto i = paramRange.first; i != paramRange.second; ++i) - params[i->second.paramName] = Json::Value(i->second.content); - - if (!params.empty()) - method["params"] = params; - - if (!method.empty()) - // add the function, only if we have any documentation to add - methods[it.second->externalSignature()] = method; - } - } - doc["methods"] = methods; - - return doc; -} - -Json::Value InterfaceHandler::formatTypeList( - vector<string> const& _names, - vector<TypePointer> const& _types, - bool _forLibrary -) -{ - Json::Value params(Json::arrayValue); - solAssert(_names.size() == _types.size(), "Names and types vector size does not match"); - for (unsigned i = 0; i < _names.size(); ++i) - { - solAssert(_types[i], ""); - Json::Value param; - param["name"] = _names[i]; - param["type"] = _types[i]->canonicalName(_forLibrary); - params.append(param); - } - return params; -} - -string InterfaceHandler::extractDoc(multimap<string, DocTag> const& _tags, string const& _name) -{ - string value; - auto range = _tags.equal_range(_name); - for (auto i = range.first; i != range.second; i++) - value += i->second.content; - return value; -} diff --git a/libsolidity/interface/Natspec.cpp b/libsolidity/interface/Natspec.cpp new file mode 100644 index 00000000..70486e23 --- /dev/null +++ b/libsolidity/interface/Natspec.cpp @@ -0,0 +1,130 @@ +/* + 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/>. +*/ +/** + * @author Lefteris <lefteris@ethdev.com> + * @date 2014 + * Takes the parsed AST and produces the Natspec documentation: + * https://github.com/ethereum/wiki/wiki/Ethereum-Natural-Specification-Format + * + * Can generally deal with JSON files + */ + +#include <libsolidity/interface/Natspec.h> +#include <boost/range/irange.hpp> +#include <libsolidity/ast/AST.h> +#include <libsolidity/interface/CompilerStack.h> + +using namespace std; +using namespace dev; +using namespace dev::solidity; + +Json::Value Natspec::documentation( + ContractDefinition const& _contractDef, + DocumentationType _type +) +{ + switch(_type) + { + case DocumentationType::NatspecUser: + return userDocumentation(_contractDef); + case DocumentationType::NatspecDev: + return devDocumentation(_contractDef); + } + + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown documentation type")); +} + +Json::Value Natspec::userDocumentation(ContractDefinition const& _contractDef) +{ + Json::Value doc; + Json::Value methods(Json::objectValue); + + for (auto const& it: _contractDef.interfaceFunctions()) + if (it.second->hasDeclaration()) + if (auto const* f = dynamic_cast<FunctionDefinition const*>(&it.second->declaration())) + { + string value = extractDoc(f->annotation().docTags, "notice"); + if (!value.empty()) + { + Json::Value user; + // since @notice is the only user tag if missing function should not appear + user["notice"] = Json::Value(value); + methods[it.second->externalSignature()] = user; + } + } + doc["methods"] = methods; + + return doc; +} + +Json::Value Natspec::devDocumentation(ContractDefinition const& _contractDef) +{ + Json::Value doc; + Json::Value methods(Json::objectValue); + + auto author = extractDoc(_contractDef.annotation().docTags, "author"); + if (!author.empty()) + doc["author"] = author; + auto title = extractDoc(_contractDef.annotation().docTags, "title"); + if (!title.empty()) + doc["title"] = title; + + for (auto const& it: _contractDef.interfaceFunctions()) + { + if (!it.second->hasDeclaration()) + continue; + Json::Value method; + if (auto fun = dynamic_cast<FunctionDefinition const*>(&it.second->declaration())) + { + auto dev = extractDoc(fun->annotation().docTags, "dev"); + if (!dev.empty()) + method["details"] = Json::Value(dev); + + auto author = extractDoc(fun->annotation().docTags, "author"); + if (!author.empty()) + method["author"] = author; + + auto ret = extractDoc(fun->annotation().docTags, "return"); + if (!ret.empty()) + method["return"] = ret; + + Json::Value params(Json::objectValue); + auto paramRange = fun->annotation().docTags.equal_range("param"); + for (auto i = paramRange.first; i != paramRange.second; ++i) + params[i->second.paramName] = Json::Value(i->second.content); + + if (!params.empty()) + method["params"] = params; + + if (!method.empty()) + // add the function, only if we have any documentation to add + methods[it.second->externalSignature()] = method; + } + } + doc["methods"] = methods; + + return doc; +} + +string Natspec::extractDoc(multimap<string, DocTag> const& _tags, string const& _name) +{ + string value; + auto range = _tags.equal_range(_name); + for (auto i = range.first; i != range.second; i++) + value += i->second.content; + return value; +} diff --git a/libsolidity/interface/InterfaceHandler.h b/libsolidity/interface/Natspec.h index 56927d44..bec9acd2 100644 --- a/libsolidity/interface/InterfaceHandler.h +++ b/libsolidity/interface/Natspec.h @@ -17,8 +17,7 @@ /** * @author Lefteris <lefteris@ethdev.com> * @date 2014 - * Takes the parsed AST and produces the Natspec - * documentation and the ABI interface + * Takes the parsed AST and produces the Natspec documentation: * https://github.com/ethereum/wiki/wiki/Ethereum-Natural-Specification-Format * * Can generally deal with JSON files @@ -59,7 +58,7 @@ enum class CommentOwner Function }; -class InterfaceHandler +class Natspec { public: /// Get the given type of documentation @@ -71,10 +70,6 @@ public: ContractDefinition const& _contractDef, DocumentationType _type ); - /// Get the ABI Interface of the contract - /// @param _contractDef The contract definition - /// @return A JSONrepresentation of the contract's ABI Interface - static Json::Value abiInterface(ContractDefinition const& _contractDef); /// Get the User documentation of the contract /// @param _contractDef The contract definition /// @return A JSON representation of the contract's user documentation diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index 0afa9ae1..d5dbaa46 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -392,10 +392,10 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) // ABI, documentation and metadata Json::Value contractData(Json::objectValue); - contractData["abi"] = m_compilerStack.metadata(contractName, DocumentationType::ABIInterface); + contractData["abi"] = m_compilerStack.contractABI(contractName); contractData["metadata"] = m_compilerStack.onChainMetadata(contractName); - contractData["userdoc"] = m_compilerStack.metadata(contractName, DocumentationType::NatspecUser); - contractData["devdoc"] = m_compilerStack.metadata(contractName, DocumentationType::NatspecDev); + contractData["userdoc"] = m_compilerStack.natspec(contractName, DocumentationType::NatspecUser); + contractData["devdoc"] = m_compilerStack.natspec(contractName, DocumentationType::NatspecDev); // EVM Json::Value evmData(Json::objectValue); diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index b5130c8a..72150a3e 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -27,7 +27,6 @@ #include <libsolidity/parsing/Scanner.h> #include <libsolidity/inlineasm/AsmParser.h> #include <libsolidity/interface/Exceptions.h> -#include <libsolidity/interface/InterfaceHandler.h> using namespace std; diff --git a/scripts/bytecodecompare/storebytecode.sh b/scripts/bytecodecompare/storebytecode.sh index 9a40bc6d..b3491cfa 100755 --- a/scripts/bytecodecompare/storebytecode.sh +++ b/scripts/bytecodecompare/storebytecode.sh @@ -87,18 +87,27 @@ EOF eval `ssh-agent -s` ssh-add deploy_key + COMMIT_DATE=$(cd "$REPO_ROOT" && git show -s --format="%cd" --date=short "$TRAVIS_COMMIT") + git clone --depth 2 git@github.com:ethereum/solidity-test-bytecode.git cd solidity-test-bytecode git config user.name "travis" git config user.email "chris@ethereum.org" git clean -f -d -x - mkdir -p "$TRAVIS_COMMIT" - REPORT="$TRAVIS_COMMIT/$ZIP_SUFFIX.txt" + DIRECTORY=$COMMIT_DATE"_"$TRAVIS_COMMIT + mkdir -p "$DIRECTORY" + REPORT="$DIRECTORY/$ZIP_SUFFIX.txt" cp ../report.txt "$REPORT" - git add "$REPORT" - git commit -a -m "Added report $REPORT" - git push origin + # Only push if adding actually worked, i.e. there were changes. + if git add "$REPORT" + then + git commit -a -m "Added report $REPORT" + git pull --rebase + git push origin + else + echo "Adding report failed, it might already exist in the repository." + fi fi ) rm -rf "$TMPDIR"
\ No newline at end of file diff --git a/scripts/release_ppa.sh b/scripts/release_ppa.sh index 86288c47..5e01347b 100755 --- a/scripts/release_ppa.sh +++ b/scripts/release_ppa.sh @@ -39,7 +39,7 @@ keyid=703F83D0 email=builds@ethereum.org packagename=solc -for distribution in trusty vivid wily xenial yakkety +for distribution in trusty vivid xenial yakkety zesty do cd /tmp/ mkdir $distribution diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index ebd16fd3..e55e3e7e 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -266,24 +266,31 @@ void CommandLineInterface::handleOnChainMetadata(string const& _contract) return; string data = m_compiler->onChainMetadata(_contract); - if (m_args.count("output-dir")) + if (m_args.count(g_argOutputDir)) createFile(m_compiler->filesystemFriendlyName(_contract) + "_meta.json", data); else cout << "Metadata: " << endl << data << endl; } -void CommandLineInterface::handleMeta(DocumentationType _type, string const& _contract) +void CommandLineInterface::handleABI(string const& _contract) +{ + if (!m_args.count(g_argAbi)) + return; + + string data = dev::jsonCompactPrint(m_compiler->contractABI(_contract)); + if (m_args.count(g_argOutputDir)) + createFile(m_compiler->filesystemFriendlyName(_contract) + ".abi", data); + else + cout << "Contract JSON ABI " << endl << data << endl; +} + +void CommandLineInterface::handleNatspec(DocumentationType _type, string const& _contract) { std::string argName; std::string suffix; std::string title; switch(_type) { - case DocumentationType::ABIInterface: - argName = g_argAbi; - suffix = ".abi"; - title = "Contract JSON ABI"; - break; case DocumentationType::NatspecUser: argName = g_argNatspecUser; suffix = ".docuser"; @@ -301,11 +308,7 @@ void CommandLineInterface::handleMeta(DocumentationType _type, string const& _co if (m_args.count(argName)) { - std::string output; - if (_type == DocumentationType::ABIInterface) - output = dev::jsonCompactPrint(m_compiler->metadata(_contract, _type)); - else - output = dev::jsonPrettyPrint(m_compiler->metadata(_contract, _type)); + std::string output = dev::jsonPrettyPrint(m_compiler->natspec(_contract, _type)); if (m_args.count(g_argOutputDir)) createFile(m_compiler->filesystemFriendlyName(_contract) + suffix, output); @@ -787,7 +790,7 @@ void CommandLineInterface::handleCombinedJSON() { Json::Value contractData(Json::objectValue); if (requests.count(g_strAbi)) - contractData[g_strAbi] = dev::jsonCompactPrint(m_compiler->interface(contractName)); + contractData[g_strAbi] = dev::jsonCompactPrint(m_compiler->contractABI(contractName)); if (requests.count("metadata")) contractData["metadata"] = m_compiler->onChainMetadata(contractName); if (requests.count(g_strBinary)) @@ -814,9 +817,9 @@ void CommandLineInterface::handleCombinedJSON() contractData[g_strSrcMapRuntime] = map ? *map : ""; } if (requests.count(g_strNatspecDev)) - contractData[g_strNatspecDev] = dev::jsonCompactPrint(m_compiler->metadata(contractName, DocumentationType::NatspecDev)); + contractData[g_strNatspecDev] = dev::jsonCompactPrint(m_compiler->natspec(contractName, DocumentationType::NatspecDev)); if (requests.count(g_strNatspecUser)) - contractData[g_strNatspecUser] = dev::jsonCompactPrint(m_compiler->metadata(contractName, DocumentationType::NatspecUser)); + contractData[g_strNatspecUser] = dev::jsonCompactPrint(m_compiler->natspec(contractName, DocumentationType::NatspecUser)); output[g_strContracts][contractName] = contractData; } @@ -1067,9 +1070,9 @@ void CommandLineInterface::outputCompilationResults() handleBytecode(contract); handleSignatureHashes(contract); handleOnChainMetadata(contract); - handleMeta(DocumentationType::ABIInterface, contract); - handleMeta(DocumentationType::NatspecDev, contract); - handleMeta(DocumentationType::NatspecUser, contract); + handleABI(contract); + handleNatspec(DocumentationType::NatspecDev, contract); + handleNatspec(DocumentationType::NatspecUser, contract); } // end of contracts iteration handleFormal(); diff --git a/solc/CommandLineInterface.h b/solc/CommandLineInterface.h index f52a03c7..ebf52625 100644 --- a/solc/CommandLineInterface.h +++ b/solc/CommandLineInterface.h @@ -67,7 +67,8 @@ private: void handleBytecode(std::string const& _contract); void handleSignatureHashes(std::string const& _contract); void handleOnChainMetadata(std::string const& _contract); - void handleMeta(DocumentationType _type, std::string const& _contract); + void handleABI(std::string const& _contract); + void handleNatspec(DocumentationType _type, std::string const& _contract); void handleGasEstimation(std::string const& _contract); void handleFormal(); diff --git a/solc/jsonCompiler.cpp b/solc/jsonCompiler.cpp index 2501c8e8..7f99324e 100644 --- a/solc/jsonCompiler.cpp +++ b/solc/jsonCompiler.cpp @@ -193,7 +193,7 @@ string compile(StringMap const& _sources, bool _optimize, CStyleReadFileCallback for (string const& contractName: compiler.contractNames()) { Json::Value contractData(Json::objectValue); - contractData["interface"] = dev::jsonCompactPrint(compiler.interface(contractName)); + contractData["interface"] = dev::jsonCompactPrint(compiler.contractABI(contractName)); contractData["bytecode"] = compiler.object(contractName).toHex(); contractData["runtimeBytecode"] = compiler.runtimeObject(contractName).toHex(); contractData["opcodes"] = solidity::disassemble(compiler.object(contractName).bytecode); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index ddec205e..8e7b8916 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -4,6 +4,7 @@ aux_source_directory(. SRC_LIST) aux_source_directory(libdevcore SRC_LIST) aux_source_directory(libevmasm SRC_LIST) aux_source_directory(libsolidity SRC_LIST) +aux_source_directory(libjulia SRC_LIST) aux_source_directory(contracts SRC_LIST) aux_source_directory(liblll SRC_LIST) aux_source_directory(libjulia SRC_LIST) diff --git a/test/RPCSession.cpp b/test/RPCSession.cpp index 3ea3b1fe..c4fbfefb 100644 --- a/test/RPCSession.cpp +++ b/test/RPCSession.cpp @@ -316,9 +316,9 @@ Json::Value RPCSession::rpcCall(string const& _methodName, vector<string> const& request += "],\"id\":" + to_string(m_rpcSequence) + "}"; ++m_rpcSequence; - // cout << "Request: " << request << endl; + BOOST_TEST_MESSAGE("Request: " + request); string reply = m_ipcSocket.sendRequest(request); - // cout << "Reply: " << reply << endl; + BOOST_TEST_MESSAGE("Reply: " + reply); Json::Value result; BOOST_REQUIRE(Json::Reader().parse(reply, result, false)); diff --git a/test/cmdlineTests.sh b/test/cmdlineTests.sh index 6dc3f77f..4074ce55 100755 --- a/test/cmdlineTests.sh +++ b/test/cmdlineTests.sh @@ -79,7 +79,14 @@ TMPDIR=$(mktemp -d) "$REPO_ROOT"/scripts/isolate_tests.py "$REPO_ROOT"/test/ for f in *.sol do + set +e "$REPO_ROOT"/build/test/solfuzzer --quiet < "$f" + if [ $? -ne 0 ]; then + echo "Fuzzer failed on:" + cat "$f" + exit 1 + fi + set -e done ) rm -rf "$TMPDIR" diff --git a/test/libjulia/Parser.cpp b/test/libjulia/Parser.cpp index 11820c01..d1081067 100644 --- a/test/libjulia/Parser.cpp +++ b/test/libjulia/Parser.cpp @@ -117,12 +117,18 @@ BOOST_AUTO_TEST_CASE(smoke_test) BOOST_AUTO_TEST_CASE(vardecl) { - BOOST_CHECK(successParse("{ let x := 7 }")); + BOOST_CHECK(successParse("{ let x:u256 := 7:u256 }")); +} + +BOOST_AUTO_TEST_CASE(vardecl_bool) +{ + BOOST_CHECK(successParse("{ let x:bool := true:bool }")); + BOOST_CHECK(successParse("{ let x:bool := false:bool }")); } BOOST_AUTO_TEST_CASE(assignment) { - BOOST_CHECK(successParse("{ let x := 2 let y := x }")); + BOOST_CHECK(successParse("{ let x:u256 := 2:u256 let y:u256 := x }")); } BOOST_AUTO_TEST_CASE(function_call) @@ -132,27 +138,27 @@ BOOST_AUTO_TEST_CASE(function_call) BOOST_AUTO_TEST_CASE(vardecl_complex) { - BOOST_CHECK(successParse("{ let y := 2 let x := add(7, mul(6, y)) }")); + BOOST_CHECK(successParse("{ let y:u256 := 2:u256 let x:u256 := add(7:u256, mul(6:u256, y)) }")); } BOOST_AUTO_TEST_CASE(blocks) { - BOOST_CHECK(successParse("{ let x := 7 { let y := 3 } { let z := 2 } }")); + BOOST_CHECK(successParse("{ let x:u256 := 7:u256 { let y:u256 := 3:u256 } { let z:u256 := 2:u256 } }")); } BOOST_AUTO_TEST_CASE(function_definitions) { - BOOST_CHECK(successParse("{ function f() { } function g(a) -> x { } }")); + BOOST_CHECK(successParse("{ function f() { } function g(a:u256) -> x:u256 { } }")); } BOOST_AUTO_TEST_CASE(function_definitions_multiple_args) { - BOOST_CHECK(successParse("{ function f(a, d) { } function g(a, d) -> x, y { } }")); + BOOST_CHECK(successParse("{ function f(a:u256, d:u256) { } function g(a:u256, d:u256) -> x:u256, y:u256 { } }")); } BOOST_AUTO_TEST_CASE(function_calls) { - BOOST_CHECK(successParse("{ function f(a) -> b {} function g(a, b, c) {} function x() { g(1, 2, f(mul(2, 3))) x() } }")); + BOOST_CHECK(successParse("{ function f(a:u256) -> b:u256 {} function g(a:u256, b:u256, c:u256) {} function x() { g(1:u256, 2:u256, f(mul(2:u256, 3:u256))) x() } }")); } BOOST_AUTO_TEST_CASE(label) @@ -167,12 +173,12 @@ BOOST_AUTO_TEST_CASE(instructions) BOOST_AUTO_TEST_CASE(push) { - CHECK_ERROR("{ 0x42 }", ParserError, "Call or assignment expected."); + CHECK_ERROR("{ 0x42:u256 }", ParserError, "Call or assignment expected."); } BOOST_AUTO_TEST_CASE(assign_from_stack) { - CHECK_ERROR("{ =: x }", ParserError, "Literal or identifier expected."); + CHECK_ERROR("{ =: x:u256 }", ParserError, "Literal or identifier expected."); } BOOST_AUTO_TEST_CASE(empty_call) @@ -180,6 +186,14 @@ BOOST_AUTO_TEST_CASE(empty_call) CHECK_ERROR("{ () }", ParserError, "Literal or identifier expected."); } +BOOST_AUTO_TEST_CASE(lacking_types) +{ + CHECK_ERROR("{ let x := 1:u256 }", ParserError, "Expected token Identifier got 'Assign'"); + CHECK_ERROR("{ let x:u256 := 1 }", ParserError, "Expected token Colon got 'RBrace'"); + CHECK_ERROR("{ function f(a) {} }", ParserError, "Expected token Colon got 'RParen'"); + CHECK_ERROR("{ function f(a:u256) -> b {} }", ParserError, "Expected token Colon got 'LBrace'"); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/InlineAssembly.cpp b/test/libsolidity/InlineAssembly.cpp index b7046f80..0729c8db 100644 --- a/test/libsolidity/InlineAssembly.cpp +++ b/test/libsolidity/InlineAssembly.cpp @@ -161,6 +161,12 @@ BOOST_AUTO_TEST_CASE(vardecl) BOOST_CHECK(successParse("{ let x := 7 }")); } +BOOST_AUTO_TEST_CASE(vardecl_bool) +{ + CHECK_PARSE_ERROR("{ let x := true }", ParserError, "True and false are not valid literals."); + CHECK_PARSE_ERROR("{ let x := false }", ParserError, "True and false are not valid literals."); +} + BOOST_AUTO_TEST_CASE(assignment) { BOOST_CHECK(successParse("{ let x := 2 7 8 add =: x }")); diff --git a/test/libsolidity/SolidityABIJSON.cpp b/test/libsolidity/SolidityABIJSON.cpp index bdcc5b10..1ebccc13 100644 --- a/test/libsolidity/SolidityABIJSON.cpp +++ b/test/libsolidity/SolidityABIJSON.cpp @@ -44,7 +44,7 @@ public: { ETH_TEST_REQUIRE_NO_THROW(m_compilerStack.parseAndAnalyze("pragma solidity >=0.0;\n" + _code), "Parsing contract failed"); - Json::Value generatedInterface = m_compilerStack.metadata("", DocumentationType::ABIInterface); + Json::Value generatedInterface = m_compilerStack.contractABI(""); Json::Value expectedInterface; m_reader.parse(_expectedInterfaceString, expectedInterface); BOOST_CHECK_MESSAGE( diff --git a/test/libsolidity/SolidityNatspecJSON.cpp b/test/libsolidity/SolidityNatspecJSON.cpp index 78c1a0ee..2a7376b9 100644 --- a/test/libsolidity/SolidityNatspecJSON.cpp +++ b/test/libsolidity/SolidityNatspecJSON.cpp @@ -49,9 +49,9 @@ public: Json::Value generatedDocumentation; if (_userDocumentation) - generatedDocumentation = m_compilerStack.metadata("", DocumentationType::NatspecUser); + generatedDocumentation = m_compilerStack.natspec("", DocumentationType::NatspecUser); else - generatedDocumentation = m_compilerStack.metadata("", DocumentationType::NatspecDev); + generatedDocumentation = m_compilerStack.natspec("", DocumentationType::NatspecDev); Json::Value expectedDocumentation; m_reader.parse(_expectedDocumentationString, expectedDocumentation); BOOST_CHECK_MESSAGE( |