Merge pull request #7773 from ethereum/develop

Merge develop into develop_060
This commit is contained in:
chriseth 2019-11-21 21:49:22 +01:00 committed by GitHub
commit a00f824479
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 313 additions and 18 deletions

View File

@ -37,6 +37,7 @@ Compiler Features:
### 0.5.14 (unreleased)
Language Features:
* Allow to obtain the selector of public or external library functions via a member ``.selector``.
Compiler Features:

View File

@ -208,6 +208,48 @@ Restrictions for libraries in comparison to contracts:
(These might be lifted at a later point.)
.. _library-selectors:
Function Signatures and Selectors in Libraries
==============================================
While external calls to public or external library functions are possible, the calling convention for such calls
is considered to be internal to Solidity and not the same as specified for the regular :ref:`contract ABI<ABI>`.
External library functions support more argument types than external contract functions, for example recursive structs
and storage pointers. For that reason, the function signatures used to compute the 4-byte selector are computed
following an internal naming schema and arguments of types not supported in the contract ABI use an internal encoding.
The following identifiers are used for the types in the signatures:
- Value types, non-storage ``string`` and non-storage ``bytes`` use the same identifiers as in the contract ABI.
- Non-storage array types follow the same convention as in the contract ABI, i.e. ``<type>[]`` for dynamic arrays and
``<type>[M]`` for fixed-size arrays of ``M`` elements.
- Non-storage structs are referred to by their fully qualified name, i.e. ``C.S`` for ``contract C { struct S { ... } }``.
- Storage pointer types use the type identifier of their corresponding non-storage type, but append a single space
followed by ``storage`` to it.
The argument encoding is the same as for the regular contract ABI, except for storage pointers, which are encoded as a
``uint256`` value referring to the storage slot to which they point.
Similarly to the contract ABI, the selector consists of the first four bytes of the Keccak256-hash of the signature.
Its value can be obtained from Solidity using the ``.selector`` member as follows:
::
pragma solidity >0.5.13 <0.7.0;
library L {
function f(uint256) external {}
}
contract C {
function g() public pure returns (bytes4) {
return L.f.selector;
}
}
.. _call-protection:
Call Protection For Libraries

View File

@ -57,7 +57,7 @@ Either add ``--libraries "file.sol:Math:0x12345678901234567890123456789012345678
If ``solc`` is called with the option ``--link``, all input files are interpreted to be unlinked binaries (hex-encoded) in the ``__$53aea86b7d70b31448b230b20ae141a537$__``-format given above and are linked in-place (if the input is read from stdin, it is written to stdout). All options except ``--libraries`` are ignored (including ``-o``) in this case.
If ``solc`` is called with the option ``--standard-json``, it will expect a JSON input (as explained below) on the standard input, and return a JSON output on the standard output. This is the recommended interface for more complex and especially automated uses.
If ``solc`` is called with the option ``--standard-json``, it will expect a JSON input (as explained below) on the standard input, and return a JSON output on the standard output. This is the recommended interface for more complex and especially automated uses. The process will always terminate in a "success" state and report any errors via the JSON output.
.. note::
The library placeholder used to be the fully qualified name of the library itself
@ -140,6 +140,8 @@ The fields are generally subject to change,
some are optional (as noted), but we try to only make backwards compatible changes.
The compiler API expects a JSON formatted input and outputs the compilation result in a JSON formatted output.
The standard error output is not used and the process will always terminate in a "success" state, even
if there were errors. Errors are always reported as part of the JSON output.
The following subsections describe the format through an example.
Comments are of course not permitted and used here only for explanatory purposes.

View File

@ -171,7 +171,7 @@ and have to be in the same function as the loop (or both have to be at the
top level).
The ``leave`` statement can only be used inside a function.
The condition part of the for-loop has to evaluate to exactly one value.
Functions cannot be defined inside for loop init blocks.
Functions cannot be defined anywhere inside for loop init blocks.
Literals cannot be larger than the their type. The largest type defined is 256-bit wide.

View File

@ -54,17 +54,17 @@ ostream& KnownState::stream(ostream& _out) const
_out << "=== State ===" << endl;
_out << "Stack height: " << dec << m_stackHeight << endl;
_out << "Equivalence classes: " << endl;
_out << "Equivalence classes:" << endl;
for (Id eqClass = 0; eqClass < m_expressionClasses->size(); ++eqClass)
streamExpressionClass(_out, eqClass);
_out << "Stack: " << endl;
_out << "Stack:" << endl;
for (auto const& it: m_stackElements)
{
_out << " " << dec << it.first << ": ";
streamExpressionClass(_out, it.second);
}
_out << "Storage: " << endl;
_out << "Storage:" << endl;
for (auto const& it: m_storageContent)
{
_out << " ";
@ -72,7 +72,7 @@ ostream& KnownState::stream(ostream& _out) const
_out << ": ";
streamExpressionClass(_out, it.second);
}
_out << "Memory: " << endl;
_out << "Memory:" << endl;
for (auto const& it: m_memoryContent)
{
_out << " ";

View File

@ -3007,6 +3007,20 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con
);
return members;
}
case Kind::DelegateCall:
{
auto const* functionDefinition = dynamic_cast<FunctionDefinition const*>(m_declaration);
solAssert(functionDefinition, "");
solAssert(functionDefinition->visibility() != Declaration::Visibility::Private, "");
if (functionDefinition->visibility() != Declaration::Visibility::Internal)
{
auto const* contract = dynamic_cast<ContractDefinition const*>(m_declaration->scope());
solAssert(contract, "");
solAssert(contract->isLibrary(), "");
return {{"selector", TypeProvider::fixedBytes(4)}};
}
return {};
}
default:
return MemberList::MemberMap();
}

View File

@ -437,7 +437,7 @@ void CompilerContext::appendInlineAssembly(
parserResult = std::move(obj.code);
#ifdef SOL_OUTPUT_ASM
cout << "After optimizer: " << endl;
cout << "After optimizer:" << endl;
cout << yul::AsmPrinter()(*parserResult) << endl;
#endif
}

View File

@ -29,6 +29,7 @@
#include <libyul/optimiser/Disambiguator.h>
#include <libyul/optimiser/NameDisplacer.h>
#include <libyul/optimiser/OptimiserStep.h>
#include <libyul/optimiser/ForLoopConditionIntoBody.h>
#include <libyul/AsmParser.h>
#include <libyul/AsmAnalysis.h>
@ -702,6 +703,7 @@ Object EVMToEWasmTranslator::run(Object const& _object)
FunctionHoister::run(context, ast);
FunctionGrouper::run(context, ast);
MainFunction{}(ast);
ForLoopConditionIntoBody::run(context, ast);
ExpressionSplitter::run(context, ast);
WordSizeTransform::run(m_dialect, ast, nameDispenser);

View File

@ -55,7 +55,7 @@ namespace yul
* takes four u64 parameters and is supposed to return the logical disjunction
* of them as a u64 value. If this name is already used somewhere, it is renamed.
*
* Prerequisite: Disambiguator, ExpressionSplitter
* Prerequisite: Disambiguator, ForLoopConditionIntoBody, ExpressionSplitter
*/
class WordSizeTransform: public ASTModifier
{

View File

@ -294,7 +294,7 @@ void CommandLineInterface::handleBinary(string const& _contract)
createFile(m_compiler->filesystemFriendlyName(_contract) + ".bin", objectWithLinkRefsHex(m_compiler->object(_contract)));
else
{
sout() << "Binary: " << endl;
sout() << "Binary:" << endl;
sout() << objectWithLinkRefsHex(m_compiler->object(_contract)) << endl;
}
}
@ -304,7 +304,7 @@ void CommandLineInterface::handleBinary(string const& _contract)
createFile(m_compiler->filesystemFriendlyName(_contract) + ".bin-runtime", objectWithLinkRefsHex(m_compiler->runtimeObject(_contract)));
else
{
sout() << "Binary of the runtime part: " << endl;
sout() << "Binary of the runtime part:" << endl;
sout() << objectWithLinkRefsHex(m_compiler->runtimeObject(_contract)) << endl;
}
}
@ -316,7 +316,7 @@ void CommandLineInterface::handleOpcode(string const& _contract)
createFile(m_compiler->filesystemFriendlyName(_contract) + ".opcode", dev::eth::disassemble(m_compiler->object(_contract).bytecode));
else
{
sout() << "Opcodes: " << endl;
sout() << "Opcodes:" << endl;
sout() << std::uppercase << dev::eth::disassemble(m_compiler->object(_contract).bytecode);
sout() << endl;
}
@ -330,7 +330,7 @@ void CommandLineInterface::handleIR(string const& _contractName)
createFile(m_compiler->filesystemFriendlyName(_contractName) + ".yul", m_compiler->yulIR(_contractName));
else
{
sout() << "IR: " << endl;
sout() << "IR:" << endl;
sout() << m_compiler->yulIR(_contractName) << endl;
}
}
@ -350,7 +350,7 @@ void CommandLineInterface::handleEWasm(string const& _contractName)
}
else
{
sout() << "EWasm text: " << endl;
sout() << "EWasm text:" << endl;
sout() << m_compiler->eWasm(_contractName) << endl;
sout() << "EWasm binary (hex): " << m_compiler->eWasmObject(_contractName).toHex() << endl;
}
@ -378,7 +378,7 @@ void CommandLineInterface::handleSignatureHashes(string const& _contract)
if (m_args.count(g_argOutputDir))
createFile(m_compiler->filesystemFriendlyName(_contract) + ".signatures", out);
else
sout() << "Function signatures: " << endl << out;
sout() << "Function signatures:" << endl << out;
}
void CommandLineInterface::handleMetadata(string const& _contract)
@ -390,7 +390,7 @@ void CommandLineInterface::handleMetadata(string const& _contract)
if (m_args.count(g_argOutputDir))
createFile(m_compiler->filesystemFriendlyName(_contract) + "_meta.json", data);
else
sout() << "Metadata: " << endl << data << endl;
sout() << "Metadata:" << endl << data << endl;
}
void CommandLineInterface::handleABI(string const& _contract)
@ -402,7 +402,7 @@ void CommandLineInterface::handleABI(string const& _contract)
if (m_args.count(g_argOutputDir))
createFile(m_compiler->filesystemFriendlyName(_contract) + ".abi", data);
else
sout() << "Contract JSON ABI " << endl << data << endl;
sout() << "Contract JSON ABI" << endl << data << endl;
}
void CommandLineInterface::handleNatspec(bool _natspecDev, string const& _contract)
@ -586,7 +586,7 @@ bool CommandLineInterface::parseLibraryOption(string const& _input)
addrString = addrString.substr(2);
if (addrString.empty())
{
serr() << "Empty address provided for library \"" << libName << "\": " << endl;
serr() << "Empty address provided for library \"" << libName << "\":" << endl;
serr() << "Note that there should not be any whitespace after the colon." << endl;
return false;
}

View File

@ -0,0 +1,32 @@
library L {
function f(uint256[] storage x) public pure returns (uint256) {
return 23;
}
}
contract C {
uint256[] y;
string x;
constructor() public { y.push(42); }
function f() public view returns (uint256) {
return L.f(y);
}
function g() public returns (bool, uint256) {
uint256 ys;
assembly { ys := y_slot }
(bool success, bytes memory data) = address(L).delegatecall(abi.encodeWithSelector(L.f.selector, ys));
return (success, success ? abi.decode(data,(uint256)) : 0);
}
function h() public returns (bool, uint256) {
uint256 ys;
assembly { ys := y_slot }
(bool success, bytes memory data) = address(L).call(abi.encodeWithSelector(L.f.selector, ys));
return (success, success ? abi.decode(data,(uint256)) : 0);
}
}
// ====
// EVMVersion: >homestead
// ----
// library: L
// f() -> 23
// g() -> true, 23
// h() -> true, 23

View File

@ -0,0 +1,32 @@
library L {
function f(uint256[] storage x) public view returns (uint256) {
return x.length;
}
}
contract C {
uint256[] y;
string x;
constructor() public { y.push(42); }
function f() public view returns (uint256) {
return L.f(y);
}
function g() public returns (bool, uint256) {
uint256 ys;
assembly { ys := y_slot }
(bool success, bytes memory data) = address(L).delegatecall(abi.encodeWithSelector(L.f.selector, ys));
return (success, success ? abi.decode(data,(uint256)) : 0);
}
function h() public returns (bool, uint256) {
uint256 ys;
assembly { ys := y_slot }
(bool success, bytes memory data) = address(L).call(abi.encodeWithSelector(L.f.selector, ys));
return (success, success ? abi.decode(data,(uint256)) : 0);
}
}
// ====
// EVMVersion: >homestead
// ----
// library: L
// f() -> 1
// g() -> true, 1
// h() -> true, 0 # this is bad - this should fail! #

View File

@ -0,0 +1,31 @@
library L {
function f(uint256[] storage x) public view returns (uint256) {
return 84;
}
}
contract C {
uint256[] y;
constructor() public { y.push(42); }
function f() public view returns (uint256) {
return L.f(y);
}
function g() public returns (bool, uint256) {
uint256 ys;
assembly { ys := y_slot }
(bool success, bytes memory data) = address(L).delegatecall(abi.encodeWithSelector(L.f.selector, ys));
return (success, success ? abi.decode(data,(uint256)) : 0);
}
function h() public returns (bool, uint256) {
uint256 ys;
assembly { ys := y_slot }
(bool success, bytes memory data) = address(L).call(abi.encodeWithSelector(L.f.selector, ys));
return (success, success ? abi.decode(data,(uint256)) : 0);
}
}
// ====
// EVMVersion: >homestead
// ----
// library: L
// f() -> 84
// g() -> true, 84
// h() -> true, 84

View File

@ -0,0 +1,31 @@
contract D {
uint public x;
constructor() public { x = 42; }
}
library L {
function f(D d) public view returns (uint256) {
return d.x();
}
}
contract C {
D d;
constructor() public { d = new D(); }
function f() public view returns (uint256) {
return L.f(d);
}
function g() public returns (bool, uint256) {
(bool success, bytes memory data) = address(L).delegatecall(abi.encodeWithSelector(L.f.selector, d));
return (success, success ? abi.decode(data,(uint256)) : 0);
}
function h() public returns (bool, uint256) {
(bool success, bytes memory data) = address(L).call(abi.encodeWithSelector(L.f.selector, d));
return (success, success ? abi.decode(data,(uint256)) : 0);
}
}
// ====
// EVMVersion: >homestead
// ----
// library: L
// f() -> 42
// g() -> true, 42
// h() -> true, 42

View File

@ -0,0 +1,30 @@
library L {
function f(uint256 x) external returns (uint) { return x; }
function g(uint256[] storage s) external returns (uint) { return s.length; }
function h(uint256[] memory m) public returns (uint) { return m.length; }
}
contract C {
uint256[] s;
constructor() public { while (s.length < 42) s.push(0); }
function f() public returns (bool, bool, uint256) {
(bool success, bytes memory data) = address(L).delegatecall(abi.encodeWithSelector(L.f.selector, 7));
return (L.f.selector == bytes4(keccak256("f(uint256)")), success, abi.decode(data, (uint256)));
}
function g() public returns (bool, bool, uint256) {
uint256 s_ptr;
assembly { s_ptr := s_slot }
(bool success, bytes memory data) = address(L).delegatecall(abi.encodeWithSelector(L.g.selector, s_ptr));
return (L.g.selector == bytes4(keccak256("g(uint256[] storage)")), success, abi.decode(data, (uint256)));
}
function h() public returns (bool, bool, uint256) {
(bool success, bytes memory data) = address(L).delegatecall(abi.encodeWithSelector(L.h.selector, new uint256[](23)));
return (L.h.selector == bytes4(keccak256("h(uint256[])")), success, abi.decode(data, (uint256)));
}
}
// ====
// EVMVersion: >homestead
// ----
// library: L
// f() -> true, true, 7
// g() -> true, true, 42
// h() -> true, true, 23

View File

@ -0,0 +1,27 @@
pragma experimental ABIEncoderV2;
library L {
struct S { uint256 a; }
function f(S storage s) external returns (uint) { return s.a; }
function g(S memory m) public returns (uint) { return m.a; }
}
contract C {
L.S s;
constructor() public { s.a = 42; }
function f() public returns (bool, bool, uint256) {
uint256 s_ptr;
assembly { s_ptr := s_slot }
(bool success, bytes memory data) = address(L).delegatecall(abi.encodeWithSelector(L.f.selector, s_ptr));
return (L.f.selector == bytes4(keccak256("f(L.S storage)")), success, abi.decode(data, (uint256)));
}
function g() public returns (bool, bool, uint256) {
(bool success, bytes memory data) = address(L).delegatecall(abi.encodeWithSelector(L.g.selector, L.S(23)));
return (L.g.selector == bytes4(keccak256("g(L.S)")), success, abi.decode(data, (uint256)));
}
}
// ====
// EVMVersion: >homestead
// ----
// library: L
// f() -> true, true, 42
// g() -> true, true, 23

View File

@ -0,0 +1,13 @@
library L {
function f(uint256) external {}
function g(uint256[] storage) external {}
function h(uint256[] memory) public {}
}
contract C {
function f() public pure returns (bytes4 a, bytes4 b, bytes4 c, bytes4 d) {
a = L.f.selector;
b = L.g.selector;
c = L.h.selector;
d = L.h.selector;
}
}

View File

@ -0,0 +1,10 @@
library L {
function f(uint256) internal {}
}
contract C {
function f() public pure returns (bytes4) {
return L.f.selector;
}
}
// ----
// TypeError: (126-138): Member "selector" not found or not visible after argument-dependent lookup in function (uint256).

View File

@ -0,0 +1,8 @@
library L {
function f(uint256) private {}
function g(uint256) public returns (uint256) {
return f.selector;
}
}
// ----
// TypeError: (113-123): Member "selector" not found or not visible after argument-dependent lookup in function (uint256).

View File

@ -0,0 +1,10 @@
library L {
function f(uint256) private {}
}
contract C {
function f() public pure returns (bytes4) {
return L.f.selector;
}
}
// ----
// TypeError: (125-128): Member "f" not found or not visible after argument-dependent lookup in type(library L).

View File

@ -0,0 +1,10 @@
library L {
function f(uint256) external pure {}
function g(uint256) external view {}
}
contract C {
function f() public pure returns (bytes4, bytes4) {
return (L.f.selector, L.g.selector);
}
}
// ----

View File

@ -140,7 +140,7 @@ public:
cout << " (e)xpr inline/(i)nline/(s)implify/varname c(l)eaner/(u)nusedprune/ss(a) transform/" << endl;
cout << " (r)edundant assign elim./re(m)aterializer/f(o)r-loop-init-rewriter/for-loop-condition-(I)nto-body/" << endl;
cout << " for-loop-condition-(O)ut-of-body/s(t)ructural simplifier/equi(v)alent function combiner/ssa re(V)erser/" << endl;
cout << " co(n)trol flow simplifier/stack com(p)ressor/(D)ead code eliminator/(L)oad resolver/ " << endl;
cout << " co(n)trol flow simplifier/stack com(p)ressor/(D)ead code eliminator/(L)oad resolver/" << endl;
cout << " (C)onditional simplifier?" << endl;
cout.flush();
int option = readStandardInputChar();