diff --git a/test/libyul/yulInterpreterTests/long_obect_name.yul b/test/libyul/yulInterpreterTests/long_obect_name.yul new file mode 100644 index 000000000..4105c6e42 --- /dev/null +++ b/test/libyul/yulInterpreterTests/long_obect_name.yul @@ -0,0 +1,19 @@ +object "t" { + code { + datacopy(not(datasize("object2.object3.object4.datablock")), 0, 0) + } + object "object2" { + code{} + object "object3" { + code{} + object "object4" { + code{} + data "datablock" "" + } + } + } +} +// ---- +// Trace: +// Memory dump: +// Storage dump: diff --git a/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp b/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp index e5acf3948..75c59c180 100644 --- a/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp +++ b/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp @@ -430,28 +430,46 @@ u256 EVMInstructionInterpreter::eval( return 0; } -u256 EVMInstructionInterpreter::evalBuiltin(BuiltinFunctionForEVM const& _fun, const std::vector& _arguments) +u256 EVMInstructionInterpreter::evalBuiltin( + BuiltinFunctionForEVM const& _fun, + vector const& _arguments, + vector const& _evaluatedArguments +) { if (_fun.instruction) - return eval(*_fun.instruction, _arguments); - else if (_fun.name == "datasize"_yulstring) - return u256(keccak256(h256(_arguments.at(0)))) & 0xfff; - else if (_fun.name == "dataoffset"_yulstring) - return u256(keccak256(h256(_arguments.at(0) + 2))) & 0xfff; - else if (_fun.name == "datacopy"_yulstring) + return eval(*_fun.instruction, _evaluatedArguments); + + string fun = _fun.name.str(); + // Evaluate datasize/offset/copy instructions + if (fun == "datasize" || fun == "dataoffset") + { + string arg = std::get(_arguments.at(0)).value.str(); + if (arg.length() < 32) + arg.resize(32, 0); + if (fun == "datasize") + return u256(keccak256(arg)) & 0xfff; + else + { + // Force different value than for datasize + arg[31] += 2; + return u256(keccak256(arg)) & 0xfff; + } + } + else if (fun == "datacopy") { // This is identical to codecopy. - if (accessMemory(_arguments.at(0), _arguments.at(2))) + if (accessMemory(_evaluatedArguments.at(0), _evaluatedArguments.at(2))) copyZeroExtended( m_state.memory, m_state.code, - size_t(_arguments.at(0)), - size_t(_arguments.at(1) & numeric_limits::max()), - size_t(_arguments.at(2)) + size_t(_evaluatedArguments.at(0)), + size_t(_evaluatedArguments.at(1) & numeric_limits::max()), + size_t(_evaluatedArguments.at(2)) ); + return 0; } else - yulAssert(false, "Unknown builtin: " + _fun.name.str()); + yulAssert(false, "Unknown builtin: " + fun); return 0; } diff --git a/test/tools/yulInterpreter/EVMInstructionInterpreter.h b/test/tools/yulInterpreter/EVMInstructionInterpreter.h index 05d27df96..3747a3fb1 100644 --- a/test/tools/yulInterpreter/EVMInstructionInterpreter.h +++ b/test/tools/yulInterpreter/EVMInstructionInterpreter.h @@ -71,7 +71,11 @@ public: /// Evaluate instruction u256 eval(evmasm::Instruction _instruction, std::vector const& _arguments); /// Evaluate builtin function - u256 evalBuiltin(BuiltinFunctionForEVM const& _fun, std::vector const& _arguments); + u256 evalBuiltin( + BuiltinFunctionForEVM const& _fun, + std::vector const& _arguments, + std::vector const& _evaluatedArguments + ); private: /// Checks if the memory access is not too large for the interpreter and adjusts diff --git a/test/tools/yulInterpreter/EwasmBuiltinInterpreter.cpp b/test/tools/yulInterpreter/EwasmBuiltinInterpreter.cpp index 61c7b4817..3ec9a84a7 100644 --- a/test/tools/yulInterpreter/EwasmBuiltinInterpreter.cpp +++ b/test/tools/yulInterpreter/EwasmBuiltinInterpreter.cpp @@ -116,27 +116,40 @@ uint64_t popcnt(uint64_t _v) using u512 = boost::multiprecision::number>; -u256 EwasmBuiltinInterpreter::evalBuiltin(YulString _fun, vector const& _arguments) +u256 EwasmBuiltinInterpreter::evalBuiltin( + YulString _functionName, + vector const& _arguments, + vector const& _evaluatedArguments +) { vector arg; - for (u256 const& a: _arguments) + for (u256 const& a: _evaluatedArguments) arg.emplace_back(uint64_t(a & uint64_t(-1))); - string fun = _fun.str(); - if (fun == "datasize") - return u256(keccak256(h256(_arguments.at(0)))) & 0xfff; - else if (fun == "dataoffset") - return u256(keccak256(h256(_arguments.at(0) + 2))) & 0xfff; + string const fun = _functionName.str(); + if (fun == "datasize" || fun == "dataoffset") + { + string arg = std::get(_arguments.at(0)).value.str(); + if (arg.length() < 32) + arg.resize(32, 0); + if (fun == "datasize") + return u256(util::keccak256(arg)) & 0xfff; + else if (fun == "dataoffset") + { + arg[31] += 2; + return u256(util::keccak256(arg)) & 0xfff; + } + } else if (fun == "datacopy") { // This is identical to codecopy. - if (accessMemory(_arguments.at(0), _arguments.at(2))) + if (accessMemory(_evaluatedArguments.at(0), _evaluatedArguments.at(2))) copyZeroExtended( m_state.memory, m_state.code, - static_cast(_arguments.at(0)), - static_cast(_arguments.at(1) & numeric_limits::max()), - static_cast(_arguments.at(2)) + static_cast(_evaluatedArguments.at(0)), + static_cast(_evaluatedArguments.at(1) & numeric_limits::max()), + static_cast(_evaluatedArguments.at(2)) ); return 0; } @@ -180,14 +193,14 @@ u256 EwasmBuiltinInterpreter::evalBuiltin(YulString _fun, vector const& _a accessMemory(arg[0], 4); return readMemoryHalfWord(arg[0]); } - else if (_fun == "i32.clz"_yulstring) + else if (fun == "i32.clz") // NOTE: the clz implementation assumes 64-bit inputs, hence the adjustment return clz64(arg[0] & uint32_t(-1)) - 32; - else if (_fun == "i64.clz"_yulstring) + else if (fun == "i64.clz") return clz64(arg[0]); - else if (_fun == "i32.ctz"_yulstring) + else if (fun == "i32.ctz") return ctz32(uint32_t(arg[0] & uint32_t(-1))); - else if (_fun == "i64.ctz"_yulstring) + else if (fun == "i64.ctz") return ctz64(arg[0]); string prefix = fun; @@ -211,7 +224,7 @@ u256 EwasmBuiltinInterpreter::evalBuiltin(YulString _fun, vector const& _a else if (prefix == "eth") return evalEthBuiltin(suffix, arg); - yulAssert(false, "Unknown builtin: " + _fun.str() + " (or implementation did not return)"); + yulAssert(false, "Unknown builtin: " + fun + " (or implementation did not return)"); return 0; } diff --git a/test/tools/yulInterpreter/EwasmBuiltinInterpreter.h b/test/tools/yulInterpreter/EwasmBuiltinInterpreter.h index 78cf9216e..5a855932d 100644 --- a/test/tools/yulInterpreter/EwasmBuiltinInterpreter.h +++ b/test/tools/yulInterpreter/EwasmBuiltinInterpreter.h @@ -69,7 +69,11 @@ public: m_state(_state) {} /// Evaluate builtin function - u256 evalBuiltin(YulString _fun, std::vector const& _arguments); + u256 evalBuiltin( + YulString _functionName, + std::vector const& _arguments, + std::vector const& _evaluatedArguments + ); private: template diff --git a/test/tools/yulInterpreter/Interpreter.cpp b/test/tools/yulInterpreter/Interpreter.cpp index a8496793f..c2fd9bff9 100644 --- a/test/tools/yulInterpreter/Interpreter.cpp +++ b/test/tools/yulInterpreter/Interpreter.cpp @@ -261,14 +261,18 @@ void ExpressionEvaluator::operator()(Identifier const& _identifier) void ExpressionEvaluator::operator()(FunctionCall const& _funCall) { - evaluateArgs(_funCall.arguments); + vector> const* literalArguments = nullptr; + if (BuiltinFunction const* builtin = m_dialect.builtin(_funCall.functionName.name)) + if (!builtin->literalArguments.empty()) + literalArguments = &builtin->literalArguments; + evaluateArgs(_funCall.arguments, literalArguments); if (EVMDialect const* dialect = dynamic_cast(&m_dialect)) { if (BuiltinFunctionForEVM const* fun = dialect->builtin(_funCall.functionName.name)) { EVMInstructionInterpreter interpreter(m_state); - setValue(interpreter.evalBuiltin(*fun, values())); + setValue(interpreter.evalBuiltin(*fun, _funCall.arguments, values())); return; } } @@ -276,7 +280,7 @@ void ExpressionEvaluator::operator()(FunctionCall const& _funCall) if (dialect->builtin(_funCall.functionName.name)) { EwasmBuiltinInterpreter interpreter(m_state); - setValue(interpreter.evalBuiltin(_funCall.functionName.name, values())); + setValue(interpreter.evalBuiltin(_funCall.functionName.name, _funCall.arguments, values())); return; } @@ -317,14 +321,22 @@ void ExpressionEvaluator::setValue(u256 _value) m_values.emplace_back(std::move(_value)); } -void ExpressionEvaluator::evaluateArgs(vector const& _expr) +void ExpressionEvaluator::evaluateArgs( + vector const& _expr, + vector> const* _literalArguments +) { vector values; + size_t i = 0; /// Function arguments are evaluated in reverse. for (auto const& expr: _expr | boost::adaptors::reversed) { - visit(expr); + if (!_literalArguments || !_literalArguments->at(_expr.size() - i - 1)) + visit(expr); + else + m_values = {0}; values.push_back(value()); + ++i; } m_values = std::move(values); std::reverse(m_values.begin(), m_values.end()); diff --git a/test/tools/yulInterpreter/Interpreter.h b/test/tools/yulInterpreter/Interpreter.h index 00e02f6e2..0f2fa5c7e 100644 --- a/test/tools/yulInterpreter/Interpreter.h +++ b/test/tools/yulInterpreter/Interpreter.h @@ -21,7 +21,7 @@ #pragma once -#include +#include #include #include @@ -197,7 +197,10 @@ private: /// Evaluates the given expression from right to left and /// stores it in m_value. - void evaluateArgs(std::vector const& _expr); + void evaluateArgs( + std::vector const& _expr, + std::vector> const* _literalArguments + ); InterpreterState& m_state; Dialect const& m_dialect;