b1370be397
Bug: chromium:1127914 Change-Id: Ic55028b501ad7fdd5b940b1ceff2fa0f249747ce Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2449979 Commit-Queue: Philip Pfaffe <pfaffe@chromium.org> Reviewed-by: Clemens Backes <clemensb@chromium.org> Cr-Commit-Position: refs/heads/master@{#70347}
576 lines
20 KiB
C++
576 lines
20 KiB
C++
// Copyright 2020 the V8 project authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include <initializer_list>
|
|
|
|
#include "src/api/api-inl.h"
|
|
#include "src/base/macros.h"
|
|
#include "src/codegen/assembler-inl.h"
|
|
#include "src/compiler/heap-refs.h"
|
|
#include "src/debug/debug-evaluate.h"
|
|
#include "src/debug/debug-interface.h"
|
|
#include "src/diagnostics/disassembler.h"
|
|
#include "src/execution/frames-inl.h"
|
|
#include "src/execution/frames.h"
|
|
#include "src/objects/js-objects.h"
|
|
#include "src/objects/property-descriptor.h"
|
|
#include "src/utils/utils.h"
|
|
#include "src/utils/vector.h"
|
|
#include "src/wasm/compilation-environment.h"
|
|
#include "src/wasm/module-decoder.h"
|
|
#include "src/wasm/value-type.h"
|
|
#include "src/wasm/wasm-constants.h"
|
|
#include "src/wasm/wasm-debug-evaluate.h"
|
|
#include "src/wasm/wasm-debug.h"
|
|
#include "src/wasm/wasm-module-builder.h"
|
|
#include "src/wasm/wasm-module.h"
|
|
#include "src/wasm/wasm-objects-inl.h"
|
|
#include "src/wasm/wasm-opcodes.h"
|
|
#include "src/wasm/wasm-tier.h"
|
|
#include "test/cctest/cctest.h"
|
|
#include "test/cctest/compiler/value-helper.h"
|
|
#include "test/cctest/wasm/wasm-run-utils.h"
|
|
#include "test/common/wasm/test-signatures.h"
|
|
#include "test/common/wasm/wasm-macro-gen.h"
|
|
|
|
namespace v8 {
|
|
namespace internal {
|
|
namespace wasm {
|
|
|
|
static Handle<String> V8String(Isolate* isolate, const char* str) {
|
|
return isolate->factory()->NewStringFromAsciiChecked(str);
|
|
}
|
|
|
|
namespace {
|
|
template <typename... FunctionArgsT>
|
|
class TestCode {
|
|
public:
|
|
TestCode(WasmRunnerBase* runner, std::initializer_list<byte> code,
|
|
std::initializer_list<ValueType::Kind> locals = {})
|
|
: compiler_(&runner->NewFunction<FunctionArgsT...>()),
|
|
code_(code),
|
|
locals_(static_cast<int32_t>(locals.size())) {
|
|
for (ValueType::Kind T : locals) {
|
|
compiler_->AllocateLocal(ValueType::Primitive(T));
|
|
}
|
|
compiler_->Build(code.begin(), code.end());
|
|
}
|
|
|
|
Handle<BreakPoint> BreakOnReturn(WasmRunnerBase* runner) {
|
|
runner->TierDown();
|
|
uint32_t return_idx = FindReturn();
|
|
uint32_t return_offset_in_function =
|
|
static_cast<uint32_t>(LEBHelper::sizeof_i32v(locals_)) + 2 * locals_ +
|
|
return_idx;
|
|
|
|
int function_index = compiler_->function_index();
|
|
int function_offset =
|
|
runner->builder().GetFunctionAt(function_index)->code.offset();
|
|
int return_offset_in_module = function_offset + return_offset_in_function;
|
|
|
|
Handle<WasmInstanceObject> instance = runner->builder().instance_object();
|
|
Handle<Script> script(instance->module_object().script(),
|
|
runner->main_isolate());
|
|
static int break_index = 0;
|
|
Handle<BreakPoint> break_point =
|
|
runner->main_isolate()->factory()->NewBreakPoint(
|
|
break_index++, runner->main_isolate()->factory()->empty_string());
|
|
|
|
int expected_breakpoint_position = return_offset_in_module;
|
|
CHECK(WasmScript::SetBreakPoint(script, &return_offset_in_module,
|
|
break_point));
|
|
// Check that the breakpoint doesn't slide
|
|
DCHECK_EQ(expected_breakpoint_position, return_offset_in_module);
|
|
USE(expected_breakpoint_position);
|
|
return break_point;
|
|
}
|
|
|
|
MaybeHandle<Object> Run(WasmRunnerBase* runner) {
|
|
Isolate* isolate = runner->main_isolate();
|
|
Handle<JSFunction> fun_wrapper =
|
|
runner->builder().WrapCode(compiler_->function_index());
|
|
Handle<Object> global(isolate->context().global_object(), isolate);
|
|
return Execution::Call(isolate, fun_wrapper, global, 0, nullptr);
|
|
}
|
|
|
|
private:
|
|
uint32_t FindReturn() const {
|
|
for (auto i = code_.begin(); i != code_.end();
|
|
i += OpcodeLength(&*i, &*code_.end())) {
|
|
if (*i == kExprReturn) {
|
|
return static_cast<uint32_t>(std::distance(code_.begin(), i));
|
|
}
|
|
}
|
|
UNREACHABLE();
|
|
}
|
|
|
|
WasmFunctionCompiler* compiler_;
|
|
std::vector<byte> code_;
|
|
int32_t locals_;
|
|
};
|
|
|
|
class WasmEvaluatorBuilder {
|
|
public:
|
|
explicit WasmEvaluatorBuilder(TestExecutionTier execution_tier,
|
|
uint32_t min_memory = 1,
|
|
uint32_t max_memory = 1)
|
|
: zone_(&allocator_, ZONE_NAME), builder_(&zone_) {
|
|
get_memory_function_index = AddImport<void, uint32_t, uint32_t, uint32_t>(
|
|
CStrVector("__getMemory"));
|
|
get_local_function_index =
|
|
AddImport<void, uint32_t, uint32_t>(CStrVector("__getLocal"));
|
|
get_global_function_index =
|
|
AddImport<void, uint32_t, uint32_t>(CStrVector("__getGlobal"));
|
|
get_operand_function_index =
|
|
AddImport<void, uint32_t, uint32_t>(CStrVector("__getOperand"));
|
|
sbrk_function_index = AddImport<uint32_t, uint32_t>(CStrVector("__sbrk"));
|
|
wasm_format_function =
|
|
builder_.AddFunction(WasmRunnerBase::CreateSig<uint32_t>(&zone_));
|
|
wasm_format_function->SetName(CStrVector("wasm_format"));
|
|
builder_.AddExport(CStrVector("wasm_format"), wasm_format_function);
|
|
builder_.SetMinMemorySize(min_memory);
|
|
builder_.SetMaxMemorySize(max_memory);
|
|
}
|
|
|
|
template <typename ReturnT, typename... ArgTs>
|
|
uint32_t AddImport(Vector<const char> name) {
|
|
return builder_.AddImport(
|
|
name, WasmRunnerBase::CreateSig<ReturnT, ArgTs...>(&zone_),
|
|
CStrVector("env"));
|
|
}
|
|
|
|
void push_back(std::initializer_list<byte> code) {
|
|
wasm_format_function->EmitCode(code.begin(),
|
|
static_cast<uint32_t>(code.size()));
|
|
}
|
|
|
|
void CallSbrk(std::initializer_list<byte> args) {
|
|
push_back(args);
|
|
push_back({WASM_CALL_FUNCTION0(sbrk_function_index)});
|
|
}
|
|
|
|
void CallGetOperand(std::initializer_list<byte> args) {
|
|
push_back(args);
|
|
push_back({WASM_CALL_FUNCTION0(get_operand_function_index)});
|
|
}
|
|
|
|
void CallGetGlobal(std::initializer_list<byte> args) {
|
|
push_back(args);
|
|
push_back({WASM_CALL_FUNCTION0(get_global_function_index)});
|
|
}
|
|
|
|
void CallGetLocal(std::initializer_list<byte> args) {
|
|
push_back(args);
|
|
push_back({WASM_CALL_FUNCTION0(get_local_function_index)});
|
|
}
|
|
|
|
void CallGetMemory(std::initializer_list<byte> args) {
|
|
push_back(args);
|
|
push_back({WASM_CALL_FUNCTION0(get_memory_function_index)});
|
|
}
|
|
|
|
ZoneBuffer bytes() {
|
|
ZoneBuffer bytes(&zone_);
|
|
builder_.WriteTo(&bytes);
|
|
return bytes;
|
|
}
|
|
|
|
private:
|
|
v8::internal::AccountingAllocator allocator_;
|
|
Zone zone_;
|
|
WasmModuleBuilder builder_;
|
|
uint32_t get_memory_function_index = 0;
|
|
uint32_t get_local_function_index = 0;
|
|
uint32_t get_global_function_index = 0;
|
|
uint32_t get_operand_function_index = 0;
|
|
uint32_t sbrk_function_index = 0;
|
|
WasmFunctionBuilder* wasm_format_function = nullptr;
|
|
};
|
|
|
|
class WasmBreakHandler : public debug::DebugDelegate {
|
|
public:
|
|
struct EvaluationResult {
|
|
Maybe<std::string> result = Nothing<std::string>();
|
|
Maybe<std::string> error = Nothing<std::string>();
|
|
};
|
|
|
|
WasmBreakHandler(Isolate* isolate, ZoneBuffer evaluator_bytes)
|
|
: isolate_(isolate),
|
|
evaluator_bytes_(std::move(evaluator_bytes)),
|
|
result_(Nothing<EvaluationResult>()) {
|
|
v8::debug::SetDebugDelegate(reinterpret_cast<v8::Isolate*>(isolate_), this);
|
|
}
|
|
|
|
~WasmBreakHandler() override {
|
|
v8::debug::SetDebugDelegate(reinterpret_cast<v8::Isolate*>(isolate_),
|
|
nullptr);
|
|
}
|
|
|
|
const Maybe<EvaluationResult>& result() const { return result_; }
|
|
|
|
private:
|
|
Isolate* isolate_;
|
|
ZoneBuffer evaluator_bytes_;
|
|
Maybe<EvaluationResult> result_;
|
|
|
|
Maybe<std::string> GetPendingExceptionAsString() {
|
|
if (!isolate_->has_pending_exception()) return Nothing<std::string>();
|
|
Handle<Object> exception(isolate_->pending_exception(), isolate_);
|
|
isolate_->clear_pending_exception();
|
|
|
|
Handle<String> exception_string;
|
|
if (!Object::ToString(isolate_, exception).ToHandle(&exception_string)) {
|
|
return Just<std::string>("");
|
|
}
|
|
return Just<std::string>(exception_string->ToCString().get());
|
|
}
|
|
|
|
void BreakProgramRequested(v8::Local<v8::Context> paused_context,
|
|
const std::vector<int>&) override {
|
|
// Check the current position.
|
|
StackTraceFrameIterator frame_it(isolate_);
|
|
|
|
WasmFrame* frame = WasmFrame::cast(frame_it.frame());
|
|
Handle<WasmInstanceObject> instance{frame->wasm_instance(), isolate_};
|
|
|
|
MaybeHandle<String> result_handle = v8::internal::wasm::DebugEvaluate(
|
|
{evaluator_bytes_.begin(), evaluator_bytes_.size()}, instance,
|
|
frame_it.frame());
|
|
|
|
Maybe<std::string> error_message = GetPendingExceptionAsString();
|
|
Maybe<std::string> result_message =
|
|
result_handle.is_null()
|
|
? Nothing<std::string>()
|
|
: Just<std::string>(
|
|
result_handle.ToHandleChecked()->ToCString().get());
|
|
|
|
isolate_->clear_pending_exception();
|
|
result_ = Just<EvaluationResult>({result_message, error_message});
|
|
}
|
|
};
|
|
|
|
class WasmJSBreakHandler : public debug::DebugDelegate {
|
|
public:
|
|
struct EvaluationResult {
|
|
Maybe<std::string> result = Nothing<std::string>();
|
|
Maybe<std::string> error = Nothing<std::string>();
|
|
};
|
|
|
|
WasmJSBreakHandler(Isolate* isolate, Handle<String> snippet)
|
|
: isolate_(isolate),
|
|
snippet_(snippet),
|
|
result_(Nothing<EvaluationResult>()) {
|
|
v8::debug::SetDebugDelegate(reinterpret_cast<v8::Isolate*>(isolate_), this);
|
|
}
|
|
|
|
~WasmJSBreakHandler() override {
|
|
v8::debug::SetDebugDelegate(reinterpret_cast<v8::Isolate*>(isolate_),
|
|
nullptr);
|
|
}
|
|
|
|
const Maybe<EvaluationResult>& result() const { return result_; }
|
|
|
|
private:
|
|
Isolate* isolate_;
|
|
Handle<String> snippet_;
|
|
Maybe<EvaluationResult> result_;
|
|
|
|
Maybe<std::string> GetPendingExceptionAsString() const {
|
|
if (!isolate_->has_pending_exception()) return Nothing<std::string>();
|
|
Handle<Object> exception(isolate_->pending_exception(), isolate_);
|
|
isolate_->clear_pending_exception();
|
|
|
|
Handle<String> exception_string;
|
|
if (!Object::ToString(isolate_, exception).ToHandle(&exception_string)) {
|
|
return Just<std::string>("");
|
|
}
|
|
return Just<std::string>(exception_string->ToCString().get());
|
|
}
|
|
|
|
Maybe<std::string> GetResultAsString(MaybeHandle<Object> result) const {
|
|
Handle<Object> just_result;
|
|
if (!result.ToHandle(&just_result)) return Nothing<std::string>();
|
|
MaybeHandle<String> maybe_string = Object::ToString(isolate_, just_result);
|
|
Handle<String> just_string;
|
|
if (!maybe_string.ToHandle(&just_string)) return Nothing<std::string>();
|
|
return Just<std::string>(just_string->ToCString().get());
|
|
}
|
|
|
|
void BreakProgramRequested(v8::Local<v8::Context> paused_context,
|
|
const std::vector<int>&) override {
|
|
StackTraceFrameIterator frame_it(isolate_);
|
|
|
|
WasmFrame* frame = WasmFrame::cast(frame_it.frame());
|
|
Handle<WasmInstanceObject> instance{frame->wasm_instance(), isolate_};
|
|
|
|
MaybeHandle<Object> result_handle = DebugEvaluate::WebAssembly(
|
|
instance, frame_it.frame()->id(), snippet_, false);
|
|
|
|
Maybe<std::string> error_message = GetPendingExceptionAsString();
|
|
Maybe<std::string> result_message = GetResultAsString(result_handle);
|
|
|
|
isolate_->clear_pending_exception();
|
|
result_ = Just<EvaluationResult>({result_message, error_message});
|
|
}
|
|
};
|
|
|
|
WASM_COMPILED_EXEC_TEST(WasmDebugEvaluate_CompileFailed) {
|
|
WasmRunner<int> runner(execution_tier);
|
|
|
|
TestCode<int> code(&runner, {WASM_RETURN1(WASM_I32V_1(32))});
|
|
code.BreakOnReturn(&runner);
|
|
|
|
WasmEvaluatorBuilder evaluator(execution_tier);
|
|
// Create a module that doesn't compile by missing the END bytecode.
|
|
evaluator.push_back({WASM_RETURN1(WASM_I32V_1(33))});
|
|
|
|
Isolate* isolate = runner.main_isolate();
|
|
WasmBreakHandler break_handler(isolate, evaluator.bytes());
|
|
CHECK(!code.Run(&runner).is_null());
|
|
|
|
WasmBreakHandler::EvaluationResult result =
|
|
break_handler.result().ToChecked();
|
|
CHECK(result.result.IsNothing());
|
|
CHECK_NE(result.error.ToChecked().find(
|
|
"function body must end with \"end\" opcode"),
|
|
std::string::npos);
|
|
}
|
|
|
|
WASM_COMPILED_EXEC_TEST(WasmDebugEvaluate_MissingEntrypoint) {
|
|
WasmRunner<int> runner(execution_tier);
|
|
|
|
TestCode<int> code(&runner, {WASM_RETURN1(WASM_I32V_1(32))});
|
|
code.BreakOnReturn(&runner);
|
|
|
|
v8::internal::AccountingAllocator allocator;
|
|
Zone zone(&allocator, ZONE_NAME);
|
|
WasmModuleBuilder evaluator(&zone);
|
|
ZoneBuffer evaluator_bytes(&zone);
|
|
evaluator.WriteTo(&evaluator_bytes);
|
|
|
|
Isolate* isolate = runner.main_isolate();
|
|
WasmBreakHandler break_handler(isolate, std::move(evaluator_bytes));
|
|
CHECK(!code.Run(&runner).is_null());
|
|
|
|
WasmBreakHandler::EvaluationResult result =
|
|
break_handler.result().ToChecked();
|
|
CHECK(result.result.IsNothing());
|
|
CHECK_NE(result.error.ToChecked().find("Missing export: \"wasm_format\""),
|
|
std::string::npos);
|
|
}
|
|
|
|
WASM_COMPILED_EXEC_TEST(WasmDebugEvaluate_ExecuteFailed_SEGV) {
|
|
WasmRunner<int> runner(execution_tier);
|
|
runner.builder().AddMemoryElems<int32_t>(64);
|
|
|
|
TestCode<int> code(&runner, {WASM_RETURN1(WASM_I32V_1(32))});
|
|
|
|
// Use a max memory size of 2 here to verify the precondition for the
|
|
// GrowMemory test below.
|
|
WasmEvaluatorBuilder evaluator(execution_tier, 1, 2);
|
|
code.BreakOnReturn(&runner);
|
|
|
|
// Load 1 byte from an address that's too high.
|
|
evaluator.CallGetMemory(
|
|
{WASM_I32V_1(32), WASM_I32V_1(1), WASM_I32V_3((1 << 16) + 1)});
|
|
evaluator.push_back({WASM_RETURN1(WASM_I32V_1(33)), WASM_END});
|
|
|
|
Isolate* isolate = runner.main_isolate();
|
|
WasmBreakHandler break_handler(isolate, evaluator.bytes());
|
|
CHECK(!code.Run(&runner).is_null());
|
|
|
|
WasmBreakHandler::EvaluationResult result =
|
|
break_handler.result().ToChecked();
|
|
CHECK(result.result.IsNothing());
|
|
CHECK_NE(
|
|
result.error.ToChecked().find("Illegal access to out-of-bounds memory"),
|
|
std::string::npos);
|
|
}
|
|
|
|
WASM_COMPILED_EXEC_TEST(WasmDebugEvaluate_GrowMemory) {
|
|
WasmRunner<int> runner(execution_tier);
|
|
runner.builder().AddMemoryElems<int32_t>(64);
|
|
|
|
TestCode<int> code(
|
|
&runner,
|
|
{WASM_STORE_MEM(MachineType::Int32(), WASM_I32V_1(32), WASM_I32V_2('A')),
|
|
WASM_RETURN1(WASM_LOAD_MEM(MachineType::Int32(), WASM_I32V_1(32)))});
|
|
code.BreakOnReturn(&runner);
|
|
|
|
WasmEvaluatorBuilder evaluator(execution_tier, 1, 2);
|
|
// Grow the memory.
|
|
evaluator.CallSbrk({WASM_I32V_1(1)});
|
|
// Load 1 byte from an address that's too high for the default memory.
|
|
evaluator.CallGetMemory(
|
|
{WASM_I32V_1(32), WASM_I32V_1(1), WASM_I32V_3((1 << 16) + 1)});
|
|
evaluator.push_back({WASM_RETURN1(WASM_I32V_3((1 << 16) + 1)), WASM_END});
|
|
|
|
Isolate* isolate = runner.main_isolate();
|
|
WasmBreakHandler break_handler(isolate, evaluator.bytes());
|
|
CHECK(!code.Run(&runner).is_null());
|
|
|
|
WasmBreakHandler::EvaluationResult result =
|
|
break_handler.result().ToChecked();
|
|
CHECK(result.error.IsNothing());
|
|
CHECK_EQ(result.result.ToChecked(), "A");
|
|
}
|
|
|
|
WASM_COMPILED_EXEC_TEST(WasmDebugEvaluate_LinearMemory) {
|
|
WasmRunner<int> runner(execution_tier);
|
|
runner.builder().AddMemoryElems<int32_t>(64);
|
|
|
|
TestCode<int> code(
|
|
&runner,
|
|
{WASM_STORE_MEM(MachineType::Int32(), WASM_I32V_1(32), WASM_I32V_2('A')),
|
|
WASM_RETURN1(WASM_LOAD_MEM(MachineType::Int32(), WASM_I32V_1(32)))});
|
|
code.BreakOnReturn(&runner);
|
|
|
|
WasmEvaluatorBuilder evaluator(execution_tier);
|
|
// Load 4 bytes from debuggee memory at address 32, and store at the offset 33
|
|
// of the linear memory.
|
|
evaluator.CallGetMemory({WASM_I32V_1(32), WASM_I32V_1(4), WASM_I32V_1(33)});
|
|
evaluator.push_back({WASM_RETURN1(WASM_I32V_1(33)), WASM_END});
|
|
|
|
Isolate* isolate = runner.main_isolate();
|
|
WasmBreakHandler break_handler(isolate, evaluator.bytes());
|
|
CHECK(!code.Run(&runner).is_null());
|
|
|
|
WasmBreakHandler::EvaluationResult result =
|
|
break_handler.result().ToChecked();
|
|
CHECK(result.error.IsNothing());
|
|
CHECK_EQ(result.result.ToChecked(), "A");
|
|
}
|
|
|
|
WASM_COMPILED_EXEC_TEST(WasmDebugEvaluate_Locals) {
|
|
WasmRunner<int> runner(execution_tier);
|
|
runner.builder().AddMemoryElems<int32_t>(64);
|
|
|
|
TestCode<int> code(
|
|
&runner,
|
|
{WASM_SET_LOCAL(0, WASM_I32V_2('A')), WASM_RETURN1(WASM_GET_LOCAL(0))},
|
|
{ValueType::kI32});
|
|
code.BreakOnReturn(&runner);
|
|
|
|
WasmEvaluatorBuilder evaluator(execution_tier);
|
|
evaluator.CallGetLocal({WASM_I32V_1(0), WASM_I32V_1(33)});
|
|
evaluator.push_back({WASM_RETURN1(WASM_I32V_1(33)), WASM_END});
|
|
|
|
Isolate* isolate = runner.main_isolate();
|
|
WasmBreakHandler break_handler(isolate, evaluator.bytes());
|
|
CHECK(!code.Run(&runner).is_null());
|
|
|
|
WasmBreakHandler::EvaluationResult result =
|
|
break_handler.result().ToChecked();
|
|
CHECK(result.error.IsNothing());
|
|
CHECK_EQ(result.result.ToChecked(), "A");
|
|
}
|
|
|
|
WASM_COMPILED_EXEC_TEST(WasmDebugEvaluate_Globals) {
|
|
WasmRunner<int> runner(execution_tier);
|
|
runner.builder().AddMemoryElems<int32_t>(64);
|
|
runner.builder().AddGlobal<int32_t>();
|
|
runner.builder().AddGlobal<int32_t>();
|
|
|
|
TestCode<void> code(&runner,
|
|
{WASM_SET_GLOBAL(0, WASM_I32V_1('4')),
|
|
WASM_SET_GLOBAL(1, WASM_I32V_1('5')), WASM_RETURN0},
|
|
{});
|
|
code.BreakOnReturn(&runner);
|
|
|
|
WasmEvaluatorBuilder evaluator(execution_tier);
|
|
evaluator.CallGetGlobal({WASM_I32V_1(0), WASM_I32V_1(33)});
|
|
evaluator.CallGetGlobal({WASM_I32V_1(1), WASM_I32V_1(34)});
|
|
evaluator.push_back({WASM_RETURN1(WASM_I32V_1(33)), WASM_END});
|
|
|
|
Isolate* isolate = runner.main_isolate();
|
|
WasmBreakHandler break_handler(isolate, evaluator.bytes());
|
|
CHECK(!code.Run(&runner).is_null());
|
|
|
|
WasmBreakHandler::EvaluationResult result =
|
|
break_handler.result().ToChecked();
|
|
CHECK(result.error.IsNothing());
|
|
CHECK_EQ(result.result.ToChecked(), "45");
|
|
}
|
|
|
|
WASM_COMPILED_EXEC_TEST(WasmDebugEvaluate_Operands) {
|
|
WasmRunner<int> runner(execution_tier);
|
|
runner.builder().AddMemoryElems<int32_t>(64);
|
|
|
|
TestCode<int> code(&runner,
|
|
{WASM_SET_LOCAL(0, WASM_I32V_1('4')), WASM_GET_LOCAL(0),
|
|
WASM_RETURN1(WASM_I32V_1('5'))},
|
|
{ValueType::kI32});
|
|
code.BreakOnReturn(&runner);
|
|
|
|
WasmEvaluatorBuilder evaluator(execution_tier);
|
|
evaluator.CallGetOperand({WASM_I32V_1(0), WASM_I32V_1(33)});
|
|
evaluator.CallGetOperand({WASM_I32V_1(1), WASM_I32V_1(34)});
|
|
evaluator.push_back({WASM_RETURN1(WASM_I32V_1(33)), WASM_END});
|
|
|
|
Isolate* isolate = runner.main_isolate();
|
|
WasmBreakHandler break_handler(isolate, evaluator.bytes());
|
|
CHECK(!code.Run(&runner).is_null());
|
|
|
|
WasmBreakHandler::EvaluationResult result =
|
|
break_handler.result().ToChecked();
|
|
CHECK(result.error.IsNothing());
|
|
CHECK_EQ(result.result.ToChecked(), "45");
|
|
}
|
|
|
|
WASM_COMPILED_EXEC_TEST(WasmDebugEvaluate_JavaScript) {
|
|
WasmRunner<int> runner(execution_tier);
|
|
runner.builder().AddGlobal<int32_t>();
|
|
runner.builder().AddMemoryElems<int32_t>(64);
|
|
uint16_t index = 0;
|
|
runner.builder().AddIndirectFunctionTable(&index, 1);
|
|
|
|
TestCode<int64_t> code(
|
|
&runner,
|
|
{WASM_SET_GLOBAL(0, WASM_I32V_2('B')),
|
|
WASM_SET_LOCAL(0, WASM_I64V_2('A')), WASM_RETURN1(WASM_GET_LOCAL(0))},
|
|
{ValueType::kI64});
|
|
code.BreakOnReturn(&runner);
|
|
|
|
Isolate* isolate = runner.main_isolate();
|
|
Handle<String> snippet =
|
|
V8String(isolate,
|
|
"JSON.stringify(["
|
|
"$global0, "
|
|
"$table0, "
|
|
"$var0, "
|
|
"$main, "
|
|
"$memory0, "
|
|
"globals[0], "
|
|
"tables[0], "
|
|
"locals[0], "
|
|
"functions[0], "
|
|
"memories[0], "
|
|
"memories, "
|
|
"tables, "
|
|
"stack, "
|
|
"imports, "
|
|
"exports, "
|
|
"globals, "
|
|
"locals, "
|
|
"functions, "
|
|
"], (k, v) => k === 'at' || typeof v === 'undefined' || typeof "
|
|
"v === 'object' ? v : v.toString())");
|
|
|
|
WasmJSBreakHandler break_handler(isolate, snippet);
|
|
CHECK(!code.Run(&runner).is_null());
|
|
|
|
WasmJSBreakHandler::EvaluationResult result =
|
|
break_handler.result().ToChecked();
|
|
CHECK_WITH_MSG(result.error.IsNothing(), result.error.ToChecked().c_str());
|
|
CHECK_EQ(result.result.ToChecked(),
|
|
"[\"66\",{},\"65\",\"function 0() { [native code] }\",{},"
|
|
"\"66\",{},\"65\",\"function 0() { [native code] }\",{},"
|
|
"{},{},{\"0\":\"65\"},{},{},{},{},{}]");
|
|
}
|
|
|
|
} // namespace
|
|
} // namespace wasm
|
|
} // namespace internal
|
|
} // namespace v8
|