Merge pull request #9432 from ethereum/develop

Merge develop into breaking.
This commit is contained in:
chriseth
2020-07-16 17:14:45 +02:00
committed by GitHub
77 changed files with 909 additions and 288 deletions
+1
View File
@@ -0,0 +1 @@
--asm
+5
View File
@@ -0,0 +1,5 @@
Warning: SPDX license identifier not provided in source file. Before publishing, consider adding a comment containing "SPDX-License-Identifier: <SPDX-License>" to each source file. Use "SPDX-License-Identifier: UNLICENSED" for non-open-source code. Please see https://spdx.org for more information.
--> dup_opt_peephole/input.sol
Warning: Source file does not specify required compiler version!
--> dup_opt_peephole/input.sol
@@ -0,0 +1,9 @@
contract C {
fallback() external {
assembly {
let x := calldataload(0)
x := x
sstore(0, x)
}
}
}
+54
View File
@@ -0,0 +1,54 @@
======= dup_opt_peephole/input.sol:C =======
EVM assembly:
/* "dup_opt_peephole/input.sol":0:111 contract C {... */
mstore(0x40, 0x80)
callvalue
dup1
iszero
tag_1
jumpi
0x00
dup1
revert
tag_1:
pop
dataSize(sub_0)
dup1
dataOffset(sub_0)
0x00
codecopy
0x00
return
stop
sub_0: assembly {
/* "dup_opt_peephole/input.sol":0:111 contract C {... */
mstore(0x40, 0x80)
callvalue
dup1
iszero
tag_3
jumpi
0x00
dup1
revert
tag_3:
pop
/* "dup_opt_peephole/input.sol":74:75 0 */
0x00
/* "dup_opt_peephole/input.sol":61:76 calldataload(0) */
calldataload
/* "dup_opt_peephole/input.sol":100:101 x */
dup1
/* "dup_opt_peephole/input.sol":97:98 0 */
0x00
/* "dup_opt_peephole/input.sol":90:102 sstore(0, x) */
sstore
/* "dup_opt_peephole/input.sol":47:106 {... */
pop
/* "dup_opt_peephole/input.sol":0:111 contract C {... */
stop
auxdata: AUXDATA REMOVED
}
+13
View File
@@ -708,6 +708,19 @@ BOOST_AUTO_TEST_CASE(shift_optimizer_bug)
compareVersions("g(uint256)", u256(-1));
}
BOOST_AUTO_TEST_CASE(avoid_double_cleanup)
{
char const* sourceCode = R"(
contract C {
receive() external payable {
abi.encodePacked(uint200(0));
}
}
)";
compileBothVersions(sourceCode, 0, "C", 50);
// Check that there is no double AND instruction in the resulting code
BOOST_CHECK_EQUAL(numInstructions(m_nonOptimizedBytecode, Instruction::AND), 1);
}
BOOST_AUTO_TEST_SUITE_END()
@@ -0,0 +1,22 @@
contract C {
function oneByteUTF8() public pure returns (string memory) {
return "aaa\u0024aaa"; // usdollar
}
function twoBytesUTF8() public pure returns (string memory) {
return "aaa\u00A2aaa"; // cent
}
function threeBytesUTF8() public pure returns (string memory) {
return "aaa\u20ACaaa"; // euro
}
function combined() public pure returns (string memory) {
return "\u0024\u00A2\u20AC";
}
}
// ----
// oneByteUTF8() -> 0x20, 7, "aaa$aaa"
// twoBytesUTF8() -> 0x20, 8, "aaa\xc2\xa2aaa"
// threeBytesUTF8() -> 0x20, 9, "aaa\xe2\x82\xacaaa"
// combined() -> 0x20, 6, "$\xc2\xa2\xe2\x82\xac"
@@ -0,0 +1,7 @@
contract C {
function f() public pure returns (string memory) {
return "😃, 😭, and 😈";
}
}
// ----
// f() -> 0x20, 0x14, "\xf0\x9f\x98\x83, \xf0\x9f\x98\xad, and \xf0\x9f\x98\x88"
@@ -0,0 +1,8 @@
pragma experimental SMTChecker;
contract C {
function f() public pure returns (byte) {
return (byte("") & (""));
}
}
// ----
// Warning 5084: (101-109): Type conversion is not yet fully supported and might yield false positives.
@@ -0,0 +1,9 @@
pragma experimental SMTChecker;
contract Simp {
function f3() public pure returns (byte) {
bytes memory y = "def";
return y[0] ^ "e";
}
}
// ----
// Warning 1093: (142-152): Assertion checker does not yet implement this bitwise operator.
@@ -0,0 +1,15 @@
contract b {
struct c {
uint [2 ** 253] a;
}
c d;
function e() public {
var d = d;
}
}
// ----
// Warning 2519: (105-110): This declaration shadows an existing declaration.
// Warning 3408: (66-69): Variable "d" covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction.
// Warning 2332: (105-110): Type "b.c" covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction.
// SyntaxError 1719: (105-114): Use of the "var" keyword is disallowed. Use explicit declaration `struct b.c storage pointer d = ...´ instead.
@@ -4,5 +4,5 @@ contract C {
uint[2**255][2] a;
}
// ----
// TypeError 1534: (77-94): Type too large for storage.
// TypeError 7676: (60-97): Contract too large for storage.
// TypeError 1534: (77-94): Type too large for storage.
@@ -5,6 +5,6 @@ contract C {
uint[2**255] b;
}
// ----
// TypeError 7676: (60-114): Contract too large for storage.
// Warning 3408: (77-91): Variable "a" covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction.
// Warning 3408: (97-111): Variable "b" covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction.
// TypeError 7676: (60-114): Contract too large for storage.
@@ -0,0 +1,12 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity >= 0.0;
contract C {
uint[2**255] a;
}
contract D is C {
uint[2**255] b;
}
// ----
// TypeError 7676: (95-134): Contract too large for storage.
// Warning 3408: (77-91): Variable "a" covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction.
// Warning 3408: (117-131): Variable "b" covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction.
@@ -8,5 +8,5 @@ contract C {
S s;
}
// ----
// TypeError 1534: (146-149): Type too large for storage.
// TypeError 7676: (60-152): Contract too large for storage.
// TypeError 1534: (146-149): Type too large for storage.
@@ -2,4 +2,4 @@ contract test {
function f() payable internal {}
}
// ----
// TypeError 5587: (20-52): Internal functions cannot be payable.
// TypeError 5587: (20-52): "internal" and "private" functions cannot be payable.
@@ -2,4 +2,4 @@ contract test {
function f() payable private {}
}
// ----
// TypeError 5587: (20-51): Internal functions cannot be payable.
// TypeError 5587: (20-51): "internal" and "private" functions cannot be payable.
@@ -0,0 +1,5 @@
contract C {
string s = hex"a000";
}
// ----
// TypeError 7407: (28-37): Type literal_string (contains invalid UTF-8 sequence at position 0) is not implicitly convertible to expected type string storage ref.
@@ -0,0 +1,6 @@
contract C {
bytes b1 = "\xa0\x00";
bytes32 b2 = "\xa0\x00";
bytes b3 = hex"a000";
}
// ----
@@ -0,0 +1,6 @@
contract test {
function f() public pure returns (string memory) {
return "😃, 😭, and 😈";
}
}
// ----
@@ -1,5 +1,4 @@
contract test {
function oneByteUTF8() public pure returns (bytes32) {
bytes32 usdollar = "aaa\u0024aaa";
return usdollar;
@@ -15,17 +14,8 @@ contract test {
return eur;
}
function together() public pure returns (bytes32) {
function combined() public pure returns (bytes32) {
bytes32 res = "\u0024\u00A2\u20AC";
return res;
}
// this function returns an invalid unicode character
function invalidLiteral() public pure returns(bytes32) {
bytes32 invalid = "\u00xx";
return invalid;
}
}
// ----
// ParserError 8936: (678-681): Invalid escape sequence.
@@ -0,0 +1,7 @@
contract test {
function f() public pure returns (string memory) {
return "\xc1";
}
}
// ----
// TypeError 6359: (86-92): Return argument type literal_string (contains invalid UTF-8 sequence at position 0) is not implicitly convertible to expected type (type of first return variable) string memory.
@@ -0,0 +1,10 @@
contract test {
// this function returns an invalid unicode character
function invalidLiteral() public pure returns (bytes32) {
bytes32 invalid = "\u00xx";
return invalid;
}
}
// ----
// ParserError 8936: (162-165): Invalid escape sequence.
+2 -2
View File
@@ -22,6 +22,7 @@
#include <liblangutil/Common.h>
#include <libsolutil/CommonData.h>
#include <libsolutil/StringUtils.h>
#include <boost/algorithm/string.hpp>
@@ -198,8 +199,7 @@ string BytesUtils::formatString(bytes const& _bytes, size_t _cutOff)
if (isprint(v))
os << v;
else
os << "\\x" << setw(2) << setfill('0') << hex << v;
os << "\\x" << toHex(v);
}
}
os << "\"";
+1 -3
View File
@@ -99,11 +99,9 @@ string EwasmTranslationTest::interpret()
InterpreterState state;
state.maxTraceSize = 10000;
state.maxSteps = 100000;
WasmDialect dialect;
Interpreter interpreter(state, dialect);
try
{
interpreter(*m_object->code);
Interpreter::run(state, WasmDialect{}, *m_object->code);
}
catch (InterpreterTerminatedGeneric const&)
{
+63
View File
@@ -345,6 +345,69 @@ BOOST_AUTO_TEST_CASE(reuse_slots_function_with_gaps)
);
}
BOOST_AUTO_TEST_CASE(reuse_on_decl_assign_to_last_used)
{
string in = R"({
let x := 5
let y := x // y should reuse the stack slot of x
sstore(y, y)
})";
BOOST_CHECK_EQUAL(assemble(in),
"PUSH1 0x5 "
"DUP1 SWAP1 POP "
"DUP1 DUP2 SSTORE "
"POP "
);
}
BOOST_AUTO_TEST_CASE(reuse_on_decl_assign_to_last_used_expr)
{
string in = R"({
let x := 5
let y := add(x, 2) // y should reuse the stack slot of x
sstore(y, y)
})";
BOOST_CHECK_EQUAL(assemble(in),
"PUSH1 0x5 "
"PUSH1 0x2 DUP2 ADD "
"SWAP1 POP "
"DUP1 DUP2 SSTORE "
"POP "
);
}
BOOST_AUTO_TEST_CASE(reuse_on_decl_assign_to_not_last_used)
{
string in = R"({
let x := 5
let y := x // y should not reuse the stack slot of x, since x is still used below
sstore(y, x)
})";
BOOST_CHECK_EQUAL(assemble(in),
"PUSH1 0x5 "
"DUP1 "
"DUP2 DUP2 SSTORE "
"POP POP "
);
}
BOOST_AUTO_TEST_CASE(reuse_on_decl_assign_not_same_scope)
{
string in = R"({
let x := 5
{
let y := x // y should not reuse the stack slot of x, since x is not in the same scope
sstore(y, y)
}
})";
BOOST_CHECK_EQUAL(assemble(in),
"PUSH1 0x5 "
"DUP1 "
"DUP1 DUP2 SSTORE "
"POP POP "
);
}
BOOST_AUTO_TEST_SUITE_END()
+1 -2
View File
@@ -88,10 +88,9 @@ string YulInterpreterTest::interpret()
InterpreterState state;
state.maxTraceSize = 10000;
state.maxSteps = 10000;
Interpreter interpreter(state, EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion{}));
try
{
interpreter(*m_ast);
Interpreter::run(state, EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion{}), *m_ast);
}
catch (InterpreterTerminatedGeneric const&)
{
+1 -2
View File
@@ -44,12 +44,11 @@ yulFuzzerUtil::TerminationReason yulFuzzerUtil::interpret(
0xc7, 0x60, 0x5f, 0x7c, 0xcd, 0xfb, 0x92, 0xcd,
0x8e, 0xf3, 0x9b, 0xe4, 0x4f, 0x6c, 0x14, 0xde
};
Interpreter interpreter(state, _dialect);
TerminationReason reason = TerminationReason::None;
try
{
interpreter(*_ast);
Interpreter::run(state, _dialect, *_ast);
}
catch (StepLimitReached const&)
{
@@ -409,17 +409,17 @@ u256 EVMInstructionInterpreter::eval(
case Instruction::SWAP14:
case Instruction::SWAP15:
case Instruction::SWAP16:
// --------------- EVM 2.0 ---------------
case Instruction::JUMPTO:
case Instruction::JUMPIF:
case Instruction::JUMPV:
case Instruction::JUMPSUB:
case Instruction::JUMPSUBV:
case Instruction::BEGINSUB:
case Instruction::BEGINDATA:
case Instruction::RETURNSUB:
case Instruction::PUTLOCAL:
case Instruction::GETLOCAL:
// --------------- EIP-615 ---------------
case Instruction::EIP615_JUMPTO:
case Instruction::EIP615_JUMPIF:
case Instruction::EIP615_JUMPV:
case Instruction::EIP615_JUMPSUB:
case Instruction::EIP615_JUMPSUBV:
case Instruction::EIP615_BEGINSUB:
case Instruction::EIP615_BEGINDATA:
case Instruction::EIP615_RETURNSUB:
case Instruction::EIP615_PUTLOCAL:
case Instruction::EIP615_GETLOCAL:
{
yulAssert(false, "");
return 0;
@@ -67,6 +67,50 @@ uint64_t clz64(uint64_t _v)
return r;
}
/// Count trailing zeros for uint32. Following WebAssembly rules, it returns 32 for @a _v being zero.
/// NOTE: the ctz builtin of the compiler may or may not do this
uint32_t ctz32(uint32_t _v)
{
if (_v == 0)
return 32;
uint32_t r = 0;
while (!(_v & 1))
{
r++;
_v >>= 1;
}
return r;
}
/// Count trailing zeros for uint64. Following WebAssembly rules, it returns 64 for @a _v being zero.
/// NOTE: the ctz builtin of the compiler may or may not do this
uint64_t ctz64(uint64_t _v)
{
if (_v == 0)
return 64;
uint64_t r = 0;
while (!(_v & 1))
{
r++;
_v >>= 1;
}
return r;
}
/// Count number of bits set for uint64
uint64_t popcnt(uint64_t _v)
{
uint64_t r = 0;
while (_v)
{
r += (_v & 1);
_v >>= 1;
}
return r;
}
}
using u512 = boost::multiprecision::number<boost::multiprecision::cpp_int_backend<512, 256, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked, void>>;
@@ -77,11 +121,12 @@ u256 EwasmBuiltinInterpreter::evalBuiltin(YulString _fun, vector<u256> const& _a
for (u256 const& a: _arguments)
arg.emplace_back(uint64_t(a & uint64_t(-1)));
if (_fun == "datasize"_yulstring)
string fun = _fun.str();
if (fun == "datasize")
return u256(keccak256(h256(_arguments.at(0)))) & 0xfff;
else if (_fun == "dataoffset"_yulstring)
else if (fun == "dataoffset")
return u256(keccak256(h256(_arguments.at(0) + 2))) & 0xfff;
else if (_fun == "datacopy"_yulstring)
else if (fun == "datacopy")
{
// This is identical to codecopy.
if (accessMemory(_arguments.at(0), _arguments.at(2)))
@@ -94,42 +139,42 @@ u256 EwasmBuiltinInterpreter::evalBuiltin(YulString _fun, vector<u256> const& _a
);
return 0;
}
else if (_fun == "i32.drop"_yulstring || _fun == "i64.drop"_yulstring || _fun == "nop"_yulstring)
else if (fun == "i32.drop" || fun == "i64.drop" || fun == "nop")
return {};
else if (_fun == "i32.wrap_i64"_yulstring)
else if (fun == "i32.wrap_i64")
return arg.at(0) & uint32_t(-1);
else if (_fun == "i64.extend_i32_u"_yulstring)
else if (fun == "i64.extend_i32_u")
// Return the same as above because everything is u256 anyway.
return arg.at(0) & uint32_t(-1);
else if (_fun == "unreachable"_yulstring)
else if (fun == "unreachable")
{
logTrace(evmasm::Instruction::INVALID, {});
throw ExplicitlyTerminated();
}
else if (_fun == "i64.store"_yulstring)
else if (fun == "i64.store")
{
accessMemory(arg[0], 8);
writeMemoryWord(arg[0], arg[1]);
return 0;
}
else if (_fun == "i64.store8"_yulstring || _fun == "i32.store8"_yulstring)
else if (fun == "i64.store8" || fun == "i32.store8")
{
accessMemory(arg[0], 1);
writeMemoryByte(arg[0], static_cast<uint8_t>(arg[1] & 0xff));
return 0;
}
else if (_fun == "i64.load"_yulstring)
else if (fun == "i64.load")
{
accessMemory(arg[0], 8);
return readMemoryWord(arg[0]);
}
else if (_fun == "i32.store"_yulstring)
else if (fun == "i32.store")
{
accessMemory(arg[0], 4);
writeMemoryHalfWord(arg[0], arg[1]);
return 0;
}
else if (_fun == "i32.load"_yulstring)
else if (fun == "i32.load")
{
accessMemory(arg[0], 4);
return readMemoryHalfWord(arg[0]);
@@ -139,8 +184,12 @@ u256 EwasmBuiltinInterpreter::evalBuiltin(YulString _fun, vector<u256> const& _a
return clz64(arg[0] & uint32_t(-1)) - 32;
else if (_fun == "i64.clz"_yulstring)
return clz64(arg[0]);
else if (_fun == "i32.ctz"_yulstring)
return ctz32(uint32_t(arg[0] & uint32_t(-1)));
else if (_fun == "i64.ctz"_yulstring)
return ctz64(arg[0]);
string prefix = _fun.str();
string prefix = fun;
string suffix;
auto dot = prefix.find(".");
if (dot != string::npos)
@@ -207,6 +256,8 @@ u256 EwasmBuiltinInterpreter::evalWasmBuiltin(string const& _fun, vector<Word> c
return arg[0] != arg[1] ? 1 : 0;
else if (_fun == "eqz")
return arg[0] == 0 ? 1 : 0;
else if (_fun == "popcnt")
return popcnt(arg[0]);
else if (_fun == "lt_u")
return arg[0] < arg[1] ? 1 : 0;
else if (_fun == "gt_u")
+39 -42
View File
@@ -64,6 +64,12 @@ void InterpreterState::dumpTraceAndState(ostream& _out) const
_out << " " << slot.first.hex() << ": " << slot.second.hex() << endl;
}
void Interpreter::run(InterpreterState& _state, Dialect const& _dialect, Block const& _ast)
{
Scope scope;
Interpreter{_state, _dialect, scope}(_ast);
}
void Interpreter::operator()(ExpressionStatement const& _expressionStatement)
{
evaluateMulti(_expressionStatement.expression);
@@ -94,8 +100,7 @@ void Interpreter::operator()(VariableDeclaration const& _declaration)
YulString varName = _declaration.variables.at(i).name;
solAssert(!m_variables.count(varName), "");
m_variables[varName] = values.at(i);
solAssert(!m_scopes.back().count(varName), "");
m_scopes.back().emplace(varName, nullptr);
m_scope->names.emplace(varName, nullptr);
}
}
@@ -128,8 +133,8 @@ void Interpreter::operator()(ForLoop const& _forLoop)
{
solAssert(_forLoop.condition, "");
openScope();
ScopeGuard g([this]{ closeScope(); });
enterScope(_forLoop.pre);
ScopeGuard g([this]{ leaveScope(); });
for (auto const& statement: _forLoop.pre.statements)
{
@@ -176,14 +181,13 @@ void Interpreter::operator()(Block const& _block)
m_state.trace.emplace_back("Interpreter execution step limit reached.");
throw StepLimitReached();
}
openScope();
enterScope(_block);
// Register functions.
for (auto const& statement: _block.statements)
if (holds_alternative<FunctionDefinition>(statement))
{
FunctionDefinition const& funDef = std::get<FunctionDefinition>(statement);
solAssert(!m_scopes.back().count(funDef.name), "");
m_scopes.back().emplace(funDef.name, &funDef);
m_scope->names.emplace(funDef.name, &funDef);
}
for (auto const& statement: _block.statements)
@@ -193,29 +197,41 @@ void Interpreter::operator()(Block const& _block)
break;
}
closeScope();
leaveScope();
}
u256 Interpreter::evaluate(Expression const& _expression)
{
ExpressionEvaluator ev(m_state, m_dialect, m_variables, m_scopes);
ExpressionEvaluator ev(m_state, m_dialect, *m_scope, m_variables);
ev.visit(_expression);
return ev.value();
}
vector<u256> Interpreter::evaluateMulti(Expression const& _expression)
{
ExpressionEvaluator ev(m_state, m_dialect, m_variables, m_scopes);
ExpressionEvaluator ev(m_state, m_dialect, *m_scope, m_variables);
ev.visit(_expression);
return ev.values();
}
void Interpreter::closeScope()
void Interpreter::enterScope(Block const& _block)
{
for (auto const& [var, funDeclaration]: m_scopes.back())
if (!m_scope->subScopes.count(&_block))
m_scope->subScopes[&_block] = make_unique<Scope>(Scope{
{},
{},
m_scope
});
m_scope = m_scope->subScopes[&_block].get();
}
void Interpreter::leaveScope()
{
for (auto const& [var, funDeclaration]: m_scope->names)
if (!funDeclaration)
solAssert(m_variables.erase(var) == 1, "");
m_scopes.pop_back();
m_variables.erase(var);
m_scope = m_scope->parent;
yulAssert(m_scope, "");
}
void ExpressionEvaluator::operator()(Literal const& _literal)
@@ -253,10 +269,15 @@ void ExpressionEvaluator::operator()(FunctionCall const& _funCall)
return;
}
auto [functionScopes, fun] = findFunctionAndScope(_funCall.functionName.name);
Scope* scope = &m_scope;
for (; scope; scope = scope->parent)
if (scope->names.count(_funCall.functionName.name))
break;
yulAssert(scope, "");
solAssert(fun, "Function not found.");
solAssert(m_values.size() == fun->parameters.size(), "");
FunctionDefinition const* fun = scope->names.at(_funCall.functionName.name);
yulAssert(fun, "Function not found.");
yulAssert(m_values.size() == fun->parameters.size(), "");
map<YulString, u256> variables;
for (size_t i = 0; i < fun->parameters.size(); ++i)
variables[fun->parameters.at(i).name] = m_values.at(i);
@@ -264,7 +285,7 @@ void ExpressionEvaluator::operator()(FunctionCall const& _funCall)
variables[fun->returnVariables.at(i).name] = 0;
m_state.controlFlowState = ControlFlowState::Default;
Interpreter interpreter(m_state, m_dialect, variables, functionScopes);
Interpreter interpreter(m_state, m_dialect, *scope, std::move(variables));
interpreter(fun->body);
m_state.controlFlowState = ControlFlowState::Default;
@@ -297,27 +318,3 @@ void ExpressionEvaluator::evaluateArgs(vector<Expression> const& _expr)
m_values = std::move(values);
std::reverse(m_values.begin(), m_values.end());
}
pair<
vector<map<YulString, FunctionDefinition const*>>,
FunctionDefinition const*
> ExpressionEvaluator::findFunctionAndScope(YulString _functionName) const
{
FunctionDefinition const* fun = nullptr;
std::vector<std::map<YulString, FunctionDefinition const*>> newScopes;
for (auto const& scope: m_scopes)
{
// Copy over all functions.
newScopes.emplace_back();
for (auto const& [name, funDef]: scope)
if (funDef)
newScopes.back().emplace(name, funDef);
// Stop at the called function.
if (scope.count(_functionName))
{
fun = scope.at(_functionName);
break;
}
}
return {move(newScopes), fun};
}
+25 -23
View File
@@ -96,23 +96,37 @@ struct InterpreterState
void dumpTraceAndState(std::ostream& _out) const;
};
/**
* Scope structure built and maintained during execution.
*/
struct Scope
{
/// Used for variables and functions. Value is nullptr for variables.
std::map<YulString, FunctionDefinition const*> names;
std::map<Block const*, std::unique_ptr<Scope>> subScopes;
Scope* parent = nullptr;
};
/**
* Yul interpreter.
*/
class Interpreter: public ASTWalker
{
public:
static void run(InterpreterState& _state, Dialect const& _dialect, Block const& _ast);
Interpreter(
InterpreterState& _state,
Dialect const& _dialect,
std::map<YulString, u256> _variables = {},
std::vector<std::map<YulString, FunctionDefinition const*>> _scopes = {}
Scope& _scope,
std::map<YulString, u256> _variables = {}
):
m_dialect(_dialect),
m_state(_state),
m_variables(std::move(_variables)),
m_scopes(std::move(_scopes))
{}
m_scope(&_scope)
{
}
void operator()(ExpressionStatement const& _statement) override;
void operator()(Assignment const& _assignment) override;
@@ -136,18 +150,14 @@ private:
/// Evaluates the expression and returns its value.
std::vector<u256> evaluateMulti(Expression const& _expression);
void openScope() { m_scopes.emplace_back(); }
/// Unregisters variables and functions.
void closeScope();
void enterScope(Block const& _block);
void leaveScope();
Dialect const& m_dialect;
InterpreterState& m_state;
/// Values of variables.
std::map<YulString, u256> m_variables;
/// Scopes of variables and functions. Used for lookup, clearing at end of blocks
/// and passing over the visible functions across function calls.
/// The pointer is nullptr if and only if the key is a variable.
std::vector<std::map<YulString, FunctionDefinition const*>> m_scopes;
Scope* m_scope;
};
/**
@@ -159,13 +169,13 @@ public:
ExpressionEvaluator(
InterpreterState& _state,
Dialect const& _dialect,
std::map<YulString, u256> const& _variables,
std::vector<std::map<YulString, FunctionDefinition const*>> const& _scopes
Scope& _scope,
std::map<YulString, u256> const& _variables
):
m_state(_state),
m_dialect(_dialect),
m_variables(_variables),
m_scopes(_scopes)
m_scope(_scope)
{}
void operator()(Literal const&) override;
@@ -184,19 +194,11 @@ private:
/// stores it in m_value.
void evaluateArgs(std::vector<Expression> const& _expr);
/// Finds the function called @a _functionName in the current scope stack and returns
/// the function's scope stack (with variables removed) and definition.
std::pair<
std::vector<std::map<YulString, FunctionDefinition const*>>,
FunctionDefinition const*
> findFunctionAndScope(YulString _functionName) const;
InterpreterState& m_state;
Dialect const& m_dialect;
/// Values of variables.
std::map<YulString, u256> const& m_variables;
/// Stack of scopes in the current context.
std::vector<std::map<YulString, FunctionDefinition const*>> const& m_scopes;
Scope& m_scope;
/// Current value of the expression
std::vector<u256> m_values;
};
+2 -3
View File
@@ -88,11 +88,10 @@ void interpret(string const& _source)
InterpreterState state;
state.maxTraceSize = 10000;
Dialect const& dialect(EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion{}));
Interpreter interpreter(state, dialect);
try
{
interpreter(*ast);
Dialect const& dialect(EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion{}));
Interpreter::run(state, dialect, *ast);
}
catch (InterpreterTerminatedGeneric const&)
{