[inspector][wasm] Remove obsolete Debugger.executeWasmEvaluator().

With https://crrev.com/c/2087396 we introduced a new CDP method
`Debugger.executeWasmEvaluator()`, which we originally intended
to use as the foundation for Debug-Evaluate on Wasm frames.

However in the process of prototyping we learned that it is too
costly and too inefficient to use WebAssembly modules here, and
we switched to regular Debug-Evaluate with JavaScript instead
(with a special debug proxy exposed that allows JavaScript to
peak into the Wasm frame), since JavaScript is better suited
for short-lived / short-running snippets and we don't need
clang and wasm-ld then to generate these snippets.

The JavaScript exposed debug proxy (as described in [1]) not
only enables more powerful and flexible Debug-Evaluate for the
DWARF C/C++ extension, but also serves as the basis for various
aspects of the Basic Wasm Developer Experience.

In order to pay down technical debt and to keep the maintenance
overhead low, we should remove the initial prototype now, also
to ensure that we don't accidentally attract other users of CDP
to rely on this unsupported API (despite it being marked as
"experimental").

[1]: https://docs.google.com/document/d/1VZOJrU2VsqOZe3IUzbwQWQQSZwgGySsm5119Ust1gUA

Fixed: chromium:1162062
Bug: chromium:1020120, chromium:1068571, chromium:1127914
Change-Id: I6dba8c906a8675ce6c29a52e3c32bb6626a27247
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2605186
Auto-Submit: Benedikt Meurer <bmeurer@chromium.org>
Commit-Queue: Yang Guo <yangguo@chromium.org>
Reviewed-by: Yang Guo <yangguo@chromium.org>
Cr-Commit-Position: refs/heads/master@{#71882}
This commit is contained in:
Benedikt Meurer 2020-12-28 15:43:10 +01:00 committed by Commit Bot
parent 76d6f06abb
commit 39645430e2
15 changed files with 0 additions and 1333 deletions

View File

@ -3430,8 +3430,6 @@ v8_source_set("v8_base_without_compiler") {
"src/wasm/wasm-code-manager.cc",
"src/wasm/wasm-code-manager.h",
"src/wasm/wasm-constants.h",
"src/wasm/wasm-debug-evaluate.cc",
"src/wasm/wasm-debug-evaluate.h",
"src/wasm/wasm-debug.cc",
"src/wasm/wasm-engine.cc",
"src/wasm/wasm-engine.h",

View File

@ -211,21 +211,6 @@ domain Debugger
# Exception details.
optional Runtime.ExceptionDetails exceptionDetails
# Execute a Wasm Evaluator module on a given call frame.
experimental command executeWasmEvaluator
parameters
# WebAssembly call frame identifier to evaluate on.
CallFrameId callFrameId
# Code of the evaluator module.
binary evaluator
# Terminate execution after timing out (number of milliseconds).
experimental optional Runtime.TimeDelta timeout
returns
# Object wrapper for the evaluation result.
Runtime.RemoteObject result
# Exception details.
optional Runtime.ExceptionDetails exceptionDetails
# Returns possible locations for breakpoint. scriptId in start and end range locations should be
# the same.
command getPossibleBreakpoints

View File

@ -455,7 +455,6 @@ class V8_EXPORT_PRIVATE ScopeIterator {
class V8_EXPORT_PRIVATE StackTraceIterator {
public:
static bool SupportsWasmDebugEvaluate();
static std::unique_ptr<StackTraceIterator> Create(Isolate* isolate,
int index = 0);
StackTraceIterator() = default;
@ -478,8 +477,6 @@ class V8_EXPORT_PRIVATE StackTraceIterator {
virtual bool Restart() = 0;
virtual v8::MaybeLocal<v8::Value> Evaluate(v8::Local<v8::String> source,
bool throw_on_side_effect) = 0;
virtual v8::MaybeLocal<v8::String> EvaluateWasm(
internal::Vector<const internal::byte> source, int frame_index) = 0;
};
class QueryObjectPredicate {

View File

@ -6,22 +6,14 @@
#include "src/api/api-inl.h"
#include "src/debug/debug-evaluate.h"
#include "src/debug/debug-interface.h"
#include "src/debug/debug-scope-iterator.h"
#include "src/debug/debug.h"
#include "src/debug/liveedit.h"
#include "src/execution/frames-inl.h"
#include "src/execution/frames.h"
#include "src/execution/isolate.h"
#include "src/wasm/wasm-debug-evaluate.h"
#include "src/wasm/wasm-debug.h"
namespace v8 {
bool debug::StackTraceIterator::SupportsWasmDebugEvaluate() {
return i::FLAG_wasm_expose_debug_eval;
}
std::unique_ptr<debug::StackTraceIterator> debug::StackTraceIterator::Create(
v8::Isolate* isolate, int index) {
return std::unique_ptr<debug::StackTraceIterator>(
@ -209,25 +201,5 @@ v8::MaybeLocal<v8::Value> DebugStackTraceIterator::Evaluate(
return Utils::ToLocal(value);
}
v8::MaybeLocal<v8::String> DebugStackTraceIterator::EvaluateWasm(
internal::Vector<const internal::byte> source, int frame_index) {
DCHECK(!Done());
if (!i::FLAG_wasm_expose_debug_eval || !iterator_.is_wasm()) {
return v8::MaybeLocal<v8::String>();
}
Handle<String> value;
i::SafeForInterruptsScope safe_for_interrupt_scope(isolate_);
FrameSummary summary = FrameSummary::Get(iterator_.frame(), 0);
const FrameSummary::WasmFrameSummary& wasmSummary = summary.AsWasm();
Handle<WasmInstanceObject> instance = wasmSummary.wasm_instance();
if (!v8::internal::wasm::DebugEvaluate(source, instance, iterator_.frame())
.ToHandle(&value)) {
isolate_->OptionalRescheduleException(false);
return v8::MaybeLocal<v8::String>();
}
return Utils::ToLocal(value);
}
} // namespace internal
} // namespace v8

View File

@ -34,8 +34,6 @@ class DebugStackTraceIterator final : public debug::StackTraceIterator {
bool Restart() override;
v8::MaybeLocal<v8::Value> Evaluate(v8::Local<v8::String> source,
bool throw_on_side_effect) override;
v8::MaybeLocal<v8::String> EvaluateWasm(
internal::Vector<const internal::byte> source, int frame_index) override;
private:
Isolate* isolate_;

View File

@ -836,9 +836,6 @@ DEFINE_BOOL(trace_wasm_memory, false,
DEFINE_INT(wasm_tier_mask_for_testing, 0,
"bitmask of functions to compile with TurboFan instead of Liftoff")
DEFINE_BOOL(wasm_expose_debug_eval, true,
"Expose wasm evaluator support on the CDP")
DEFINE_BOOL(validate_asm, true, "validate asm.js modules before compiling")
DEFINE_BOOL(suppress_asm_messages, false,
"don't emit asm.js related messages (for golden file testing)")

View File

@ -1241,55 +1241,6 @@ Response V8DebuggerAgentImpl::evaluateOnCallFrame(
result, exceptionDetails);
}
Response V8DebuggerAgentImpl::executeWasmEvaluator(
const String16& callFrameId, const protocol::Binary& evaluator,
Maybe<double> timeout,
std::unique_ptr<protocol::Runtime::RemoteObject>* result,
Maybe<protocol::Runtime::ExceptionDetails>* exceptionDetails) {
if (!v8::debug::StackTraceIterator::SupportsWasmDebugEvaluate()) {
return Response::ServerError(
"--wasm-expose-debug-eval is required to execte evaluator modules");
}
if (!isPaused()) return Response::ServerError(kDebuggerNotPaused);
InjectedScript::CallFrameScope scope(m_session, callFrameId);
Response response = scope.initialize();
if (!response.IsSuccess()) return response;
int frameOrdinal = static_cast<int>(scope.frameOrdinal());
std::unique_ptr<v8::debug::StackTraceIterator> it =
v8::debug::StackTraceIterator::Create(m_isolate, frameOrdinal);
if (it->Done()) {
return Response::ServerError("Could not find call frame with given id");
}
if (!it->GetScript()->IsWasm()) {
return Response::ServerError(
"executeWasmEvaluator can only be called on WebAssembly frames");
}
v8::MaybeLocal<v8::Value> maybeResultValue;
{
V8InspectorImpl::EvaluateScope evaluateScope(scope);
if (timeout.isJust()) {
response = evaluateScope.setTimeout(timeout.fromJust() / 1000.0);
if (!response.IsSuccess()) return response;
}
v8::MaybeLocal<v8::String> eval_result =
it->EvaluateWasm({evaluator.data(), evaluator.size()}, frameOrdinal);
if (!eval_result.IsEmpty()) maybeResultValue = eval_result.ToLocalChecked();
}
// Re-initialize after running client's code, as it could have destroyed
// context or session.
response = scope.initialize();
if (!response.IsSuccess()) return response;
String16 object_group = "";
InjectedScript* injected_script = scope.injectedScript();
return injected_script->wrapEvaluateResult(maybeResultValue, scope.tryCatch(),
object_group, WrapMode::kNoPreview,
result, exceptionDetails);
}
Response V8DebuggerAgentImpl::setVariableValue(
int scopeNumber, const String16& variableName,
std::unique_ptr<protocol::Runtime::CallArgument> newValueArgument,

View File

@ -118,11 +118,6 @@ class V8DebuggerAgentImpl : public protocol::Debugger::Backend {
Maybe<double> timeout,
std::unique_ptr<protocol::Runtime::RemoteObject>* result,
Maybe<protocol::Runtime::ExceptionDetails>*) override;
Response executeWasmEvaluator(
const String16& callFrameId, const protocol::Binary& evaluator,
Maybe<double> timeout,
std::unique_ptr<protocol::Runtime::RemoteObject>* result,
Maybe<protocol::Runtime::ExceptionDetails>* exceptionDetails) override;
Response setVariableValue(
int scopeNumber, const String16& variableName,
std::unique_ptr<protocol::Runtime::CallArgument> newValue,

View File

@ -1,447 +0,0 @@
// 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 "src/wasm/wasm-debug-evaluate.h"
#include <algorithm>
#include <limits>
#include "src/api/api-inl.h"
#include "src/base/platform/wrappers.h"
#include "src/codegen/machine-type.h"
#include "src/compiler/wasm-compiler.h"
#include "src/execution/frames-inl.h"
#include "src/wasm/value-type.h"
#include "src/wasm/wasm-arguments.h"
#include "src/wasm/wasm-constants.h"
#include "src/wasm/wasm-debug.h"
#include "src/wasm/wasm-module.h"
#include "src/wasm/wasm-objects.h"
#include "src/wasm/wasm-result.h"
#include "src/wasm/wasm-value.h"
namespace v8 {
namespace internal {
namespace wasm {
namespace {
static Handle<String> V8String(Isolate* isolate, const char* str) {
return isolate->factory()->NewStringFromAsciiChecked(str);
}
static bool CheckSignature(ValueType return_type,
std::initializer_list<ValueType> argument_types,
const FunctionSig* sig, ErrorThrower* thrower) {
if (sig->return_count() != 1 && return_type != kWasmBottom) {
thrower->CompileError("Invalid return type. Got none, expected %s",
return_type.name().c_str());
return false;
}
if (sig->return_count() == 1) {
if (sig->GetReturn(0) != return_type) {
thrower->CompileError("Invalid return type. Got %s, expected %s",
sig->GetReturn(0).name().c_str(),
return_type.name().c_str());
return false;
}
}
if (sig->parameter_count() != argument_types.size()) {
thrower->CompileError("Invalid number of arguments. Expected %zu, got %zu",
sig->parameter_count(), argument_types.size());
return false;
}
size_t p = 0;
for (ValueType argument_type : argument_types) {
if (sig->GetParam(p) != argument_type) {
thrower->CompileError(
"Invalid argument type for argument %zu. Got %s, expected %s", p,
sig->GetParam(p).name().c_str(), argument_type.name().c_str());
return false;
}
++p;
}
return true;
}
static bool CheckRangeOutOfBounds(uint32_t offset, uint32_t size,
size_t allocation_size,
wasm::ErrorThrower* thrower) {
if (size > std::numeric_limits<uint32_t>::max() - offset) {
thrower->RuntimeError("Overflowing memory range\n");
return true;
}
if (offset + size > allocation_size) {
thrower->RuntimeError("Illegal access to out-of-bounds memory");
return true;
}
return false;
}
class DebugEvaluatorProxy {
public:
explicit DebugEvaluatorProxy(Isolate* isolate, CommonFrame* frame)
: isolate_(isolate), frame_(frame) {}
static void GetMemoryTrampoline(
const v8::FunctionCallbackInfo<v8::Value>& args) {
DebugEvaluatorProxy& proxy = GetProxy(args);
uint32_t offset = proxy.GetArgAsUInt32(args, 0);
uint32_t size = proxy.GetArgAsUInt32(args, 1);
uint32_t result = proxy.GetArgAsUInt32(args, 2);
proxy.GetMemory(offset, size, result);
}
// void __getMemory(uint32_t offset, uint32_t size, void* result);
void GetMemory(uint32_t offset, uint32_t size, uint32_t result) {
wasm::ScheduledErrorThrower thrower(isolate_, "debug evaluate proxy");
// Check all overflows.
if (CheckRangeOutOfBounds(offset, size, debuggee_->memory_size(),
&thrower) ||
CheckRangeOutOfBounds(result, size, evaluator_->memory_size(),
&thrower)) {
return;
}
std::memcpy(&evaluator_->memory_start()[result],
&debuggee_->memory_start()[offset], size);
}
// void* __sbrk(intptr_t increment);
uint32_t Sbrk(uint32_t increment) {
if (increment > 0 && evaluator_->memory_size() <=
std::numeric_limits<uint32_t>::max() - increment) {
Handle<WasmMemoryObject> memory(evaluator_->memory_object(), isolate_);
uint32_t new_pages =
(increment - 1 + wasm::kWasmPageSize) / wasm::kWasmPageSize;
WasmMemoryObject::Grow(isolate_, memory, new_pages);
}
return static_cast<uint32_t>(evaluator_->memory_size());
}
static void SbrkTrampoline(const v8::FunctionCallbackInfo<v8::Value>& args) {
DebugEvaluatorProxy& proxy = GetProxy(args);
uint32_t size = proxy.GetArgAsUInt32(args, 0);
uint32_t result = proxy.Sbrk(size);
args.GetReturnValue().Set(result);
}
// void __getLocal(uint32_t local, void* result);
void GetLocal(uint32_t local, uint32_t result_offset) {
DCHECK(frame_->is_wasm());
wasm::DebugInfo* debug_info =
WasmFrame::cast(frame_)->native_module()->GetDebugInfo();
WasmValue result = debug_info->GetLocalValue(
local, frame_->pc(), frame_->fp(), frame_->callee_fp());
WriteResult(result, result_offset);
}
void GetGlobal(uint32_t global, uint32_t result_offset) {
DCHECK(frame_->is_wasm());
const WasmGlobal& global_variable =
WasmFrame::cast(frame_)->native_module()->module()->globals.at(global);
Handle<WasmInstanceObject> instance(
WasmFrame::cast(frame_)->wasm_instance(), isolate_);
WasmValue result =
WasmInstanceObject::GetGlobalValue(instance, global_variable);
WriteResult(result, result_offset);
}
void GetOperand(uint32_t operand, uint32_t result_offset) {
DCHECK(frame_->is_wasm());
wasm::DebugInfo* debug_info =
WasmFrame::cast(frame_)->native_module()->GetDebugInfo();
WasmValue result = debug_info->GetStackValue(
operand, frame_->pc(), frame_->fp(), frame_->callee_fp());
WriteResult(result, result_offset);
}
static void GetLocalTrampoline(
const v8::FunctionCallbackInfo<v8::Value>& args) {
DebugEvaluatorProxy& proxy = GetProxy(args);
uint32_t local = proxy.GetArgAsUInt32(args, 0);
uint32_t result = proxy.GetArgAsUInt32(args, 1);
proxy.GetLocal(local, result);
}
static void GetGlobalTrampoline(
const v8::FunctionCallbackInfo<v8::Value>& args) {
DebugEvaluatorProxy& proxy = GetProxy(args);
uint32_t global = proxy.GetArgAsUInt32(args, 0);
uint32_t result = proxy.GetArgAsUInt32(args, 1);
proxy.GetGlobal(global, result);
}
static void GetOperandTrampoline(
const v8::FunctionCallbackInfo<v8::Value>& args) {
DebugEvaluatorProxy& proxy = GetProxy(args);
uint32_t operand = proxy.GetArgAsUInt32(args, 0);
uint32_t result = proxy.GetArgAsUInt32(args, 1);
proxy.GetOperand(operand, result);
}
Handle<JSObject> CreateImports() {
Handle<JSObject> imports_obj =
isolate_->factory()->NewJSObject(isolate_->object_function());
Handle<JSObject> import_module_obj =
isolate_->factory()->NewJSObject(isolate_->object_function());
Object::SetProperty(isolate_, imports_obj, V8String(isolate_, "env"),
import_module_obj)
.Assert();
AddImport(import_module_obj, "__getOperand",
DebugEvaluatorProxy::GetOperandTrampoline);
AddImport(import_module_obj, "__getGlobal",
DebugEvaluatorProxy::GetGlobalTrampoline);
AddImport(import_module_obj, "__getLocal",
DebugEvaluatorProxy::GetLocalTrampoline);
AddImport(import_module_obj, "__getMemory",
DebugEvaluatorProxy::GetMemoryTrampoline);
AddImport(import_module_obj, "__sbrk", DebugEvaluatorProxy::SbrkTrampoline);
return imports_obj;
}
void SetInstances(Handle<WasmInstanceObject> evaluator,
Handle<WasmInstanceObject> debuggee) {
evaluator_ = evaluator;
debuggee_ = debuggee;
}
private:
template <typename T>
void WriteResultImpl(const WasmValue& result, uint32_t result_offset) {
wasm::ScheduledErrorThrower thrower(isolate_, "debug evaluate proxy");
T val = result.to<T>();
STATIC_ASSERT(static_cast<uint32_t>(sizeof(T)) == sizeof(T));
if (CheckRangeOutOfBounds(result_offset, sizeof(T),
evaluator_->memory_size(), &thrower)) {
return;
}
base::Memcpy(&evaluator_->memory_start()[result_offset], &val, sizeof(T));
}
void WriteResult(const WasmValue& result, uint32_t result_offset) {
switch (result.type().kind()) {
case ValueType::kI32:
WriteResultImpl<uint32_t>(result, result_offset);
break;
case ValueType::kI64:
WriteResultImpl<int64_t>(result, result_offset);
break;
case ValueType::kF32:
WriteResultImpl<float>(result, result_offset);
break;
case ValueType::kF64:
WriteResultImpl<double>(result, result_offset);
break;
default:
UNIMPLEMENTED();
}
}
uint32_t GetArgAsUInt32(const v8::FunctionCallbackInfo<v8::Value>& args,
int index) {
// No type/range checks needed on his because this is only called for {args}
// where we have performed a signature check via {VerifyEvaluatorInterface}
double number = Utils::OpenHandle(*args[index])->Number();
return static_cast<uint32_t>(number);
}
static DebugEvaluatorProxy& GetProxy(
const v8::FunctionCallbackInfo<v8::Value>& args) {
return *reinterpret_cast<DebugEvaluatorProxy*>(
args.Data().As<v8::External>()->Value());
}
template <typename CallableT>
void AddImport(Handle<JSObject> import_module_obj, const char* function_name,
CallableT callback) {
v8::Isolate* api_isolate = reinterpret_cast<v8::Isolate*>(isolate_);
v8::Local<v8::Context> context = api_isolate->GetCurrentContext();
std::string data;
v8::Local<v8::Function> v8_function =
v8::Function::New(context, callback,
v8::External::New(api_isolate, this))
.ToLocalChecked();
Handle<JSReceiver> wrapped_function = Utils::OpenHandle(*v8_function);
Object::SetProperty(isolate_, import_module_obj,
V8String(isolate_, function_name), wrapped_function)
.Assert();
}
Isolate* isolate_;
CommonFrame* frame_;
Handle<WasmInstanceObject> evaluator_;
Handle<WasmInstanceObject> debuggee_;
};
static bool VerifyEvaluatorInterface(const WasmModule* raw_module,
const ModuleWireBytes& bytes,
ErrorThrower* thrower) {
for (const WasmImport imported : raw_module->import_table) {
if (imported.kind != ImportExportKindCode::kExternalFunction) continue;
const WasmFunction& F = raw_module->functions.at(imported.index);
std::string module_name(bytes.start() + imported.module_name.offset(),
bytes.start() + imported.module_name.end_offset());
std::string field_name(bytes.start() + imported.field_name.offset(),
bytes.start() + imported.field_name.end_offset());
if (module_name == "env") {
if (field_name == "__getMemory") {
// void __getMemory(uint32_t offset, uint32_t size, void* result);
if (CheckSignature(kWasmBottom, {kWasmI32, kWasmI32, kWasmI32}, F.sig,
thrower)) {
continue;
}
} else if (field_name == "__getOperand") {
// void __getOperand(uint32_t local, void* result)
if (CheckSignature(kWasmBottom, {kWasmI32, kWasmI32}, F.sig, thrower)) {
continue;
}
} else if (field_name == "__getGlobal") {
// void __getGlobal(uint32_t local, void* result)
if (CheckSignature(kWasmBottom, {kWasmI32, kWasmI32}, F.sig, thrower)) {
continue;
}
} else if (field_name == "__getLocal") {
// void __getLocal(uint32_t local, void* result)
if (CheckSignature(kWasmBottom, {kWasmI32, kWasmI32}, F.sig, thrower)) {
continue;
}
} else if (field_name == "__debug") {
// void __debug(uint32_t flag, uint32_t value)
if (CheckSignature(kWasmBottom, {kWasmI32, kWasmI32}, F.sig, thrower)) {
continue;
}
} else if (field_name == "__sbrk") {
// uint32_t __sbrk(uint32_t increment)
if (CheckSignature(kWasmI32, {kWasmI32}, F.sig, thrower)) {
continue;
}
}
}
if (!thrower->error()) {
thrower->LinkError("Unknown import \"%s\" \"%s\"", module_name.c_str(),
field_name.c_str());
}
return false;
}
for (const WasmExport& exported : raw_module->export_table) {
if (exported.kind != ImportExportKindCode::kExternalFunction) continue;
const WasmFunction& F = raw_module->functions.at(exported.index);
std::string field_name(bytes.start() + exported.name.offset(),
bytes.start() + exported.name.end_offset());
if (field_name == "wasm_format") {
if (!CheckSignature(kWasmI32, {}, F.sig, thrower)) return false;
}
}
return true;
}
} // namespace
Maybe<std::string> DebugEvaluateImpl(
Vector<const byte> snippet, Handle<WasmInstanceObject> debuggee_instance,
CommonFrame* frame) {
Isolate* isolate = debuggee_instance->GetIsolate();
HandleScope handle_scope(isolate);
WasmEngine* engine = isolate->wasm_engine();
wasm::ErrorThrower thrower(isolate, "wasm debug evaluate");
// Create module object.
wasm::ModuleWireBytes bytes(snippet);
wasm::WasmFeatures features = wasm::WasmFeatures::FromIsolate(isolate);
Handle<WasmModuleObject> evaluator_module;
if (!engine->SyncCompile(isolate, features, &thrower, bytes)
.ToHandle(&evaluator_module)) {
return Nothing<std::string>();
}
// Verify interface.
const WasmModule* raw_module = evaluator_module->module();
if (!VerifyEvaluatorInterface(raw_module, bytes, &thrower)) {
return Nothing<std::string>();
}
// Set up imports.
DebugEvaluatorProxy proxy(isolate, frame);
Handle<JSObject> imports = proxy.CreateImports();
// Instantiate Module.
Handle<WasmInstanceObject> evaluator_instance;
if (!engine->SyncInstantiate(isolate, &thrower, evaluator_module, imports, {})
.ToHandle(&evaluator_instance)) {
return Nothing<std::string>();
}
proxy.SetInstances(evaluator_instance, debuggee_instance);
Handle<JSObject> exports_obj(evaluator_instance->exports_object(), isolate);
Handle<Object> entry_point_obj;
bool get_property_success =
Object::GetProperty(isolate, exports_obj,
V8String(isolate, "wasm_format"))
.ToHandle(&entry_point_obj);
if (!get_property_success ||
!WasmExportedFunction::IsWasmExportedFunction(*entry_point_obj)) {
thrower.LinkError("Missing export: \"wasm_format\"");
return Nothing<std::string>();
}
Handle<WasmExportedFunction> entry_point =
Handle<WasmExportedFunction>::cast(entry_point_obj);
// TODO(wasm): Cache this code.
Handle<Code> wasm_entry = compiler::CompileCWasmEntry(
isolate, entry_point->sig(), debuggee_instance->module());
CWasmArgumentsPacker packer(4 /* uint32_t return value, no parameters. */);
Execution::CallWasm(isolate, wasm_entry, entry_point->GetWasmCallTarget(),
evaluator_instance, packer.argv());
if (isolate->has_pending_exception()) return Nothing<std::string>();
uint32_t offset = packer.Pop<uint32_t>();
if (CheckRangeOutOfBounds(offset, 0, evaluator_instance->memory_size(),
&thrower)) {
return Nothing<std::string>();
}
// Copy the zero-terminated string result but don't overflow.
std::string result;
byte* heap = evaluator_instance->memory_start() + offset;
for (; offset < evaluator_instance->memory_size(); ++offset, ++heap) {
if (*heap == 0) return Just(result);
result.push_back(*heap);
}
thrower.RuntimeError("The evaluation returned an invalid result");
return Nothing<std::string>();
}
MaybeHandle<String> DebugEvaluate(Vector<const byte> snippet,
Handle<WasmInstanceObject> debuggee_instance,
CommonFrame* frame) {
Maybe<std::string> result =
DebugEvaluateImpl(snippet, debuggee_instance, frame);
if (result.IsNothing()) return {};
std::string result_str = result.ToChecked();
return V8String(debuggee_instance->GetIsolate(), result_str.c_str());
}
} // namespace wasm
} // namespace internal
} // namespace v8

View File

@ -1,24 +0,0 @@
// 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.
#ifndef V8_WASM_WASM_DEBUG_EVALUATE_H_
#define V8_WASM_WASM_DEBUG_EVALUATE_H_
#include "src/base/macros.h"
#include "src/handles/maybe-handles.h"
#include "src/wasm/wasm-objects.h"
namespace v8 {
namespace internal {
namespace wasm {
MaybeHandle<String> V8_EXPORT_PRIVATE
DebugEvaluate(Vector<const byte> snippet,
Handle<WasmInstanceObject> debuggee_instance, CommonFrame* frame);
} // namespace wasm
} // namespace internal
} // namespace v8
#endif // V8_WASM_WASM_DEBUG_EVALUATE_H_

View File

@ -317,8 +317,6 @@ v8_source_set("cctest_sources") {
"wasm/test-streaming-compilation.cc",
"wasm/test-wasm-breakpoints.cc",
"wasm/test-wasm-codegen.cc",
"wasm/test-wasm-debug-evaluate.cc",
"wasm/test-wasm-debug-evaluate.h",
"wasm/test-wasm-import-wrapper-cache.cc",
"wasm/test-wasm-metrics.cc",
"wasm/test-wasm-serialization.cc",

View File

@ -418,7 +418,6 @@
# Liftoff is not currently supported on ppc and s390
'test-liftoff-*': [SKIP],
'test-wasm-breakpoints/*' : [SKIP],
'test-wasm-debug-evaluate/*': [SKIP],
'test-run-wasm-module/Run_WasmModule_CompilationHintsNoTiering': [SKIP],
'test-wasm-serialization/TierDownAfterDeserialization': [SKIP],
'test-gc/RunWasmLiftoff*': [SKIP],
@ -514,7 +513,6 @@
'test-streaming-compilation/*': [SKIP],
'test-wasm-breakpoints/*': [SKIP],
'test-wasm-codegen/*': [SKIP],
'test-wasm-debug-evaluate/*': [SKIP],
'test-wasm-import-wrapper-cache/*': [SKIP],
'test-wasm-metrics/*': [SKIP],
'test-wasm-serialization/*': [SKIP],

View File

@ -1,605 +0,0 @@
// 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, std::vector<Handle<String>> snippets)
: isolate_(isolate), snippets_(std::move(snippets)) {
v8::debug::SetDebugDelegate(reinterpret_cast<v8::Isolate*>(isolate_), this);
}
WasmJSBreakHandler(Isolate* isolate, Handle<String> snippet)
: WasmJSBreakHandler(isolate, std::vector<Handle<String>>({snippet})) {}
~WasmJSBreakHandler() override {
v8::debug::SetDebugDelegate(reinterpret_cast<v8::Isolate*>(isolate_),
nullptr);
}
const std::vector<EvaluationResult>& results() const { return results_; }
private:
Isolate* isolate_;
std::vector<Handle<String>> snippets_;
std::vector<EvaluationResult> results_;
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_};
for (Handle<String> snippet : snippets_) {
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();
results_.emplace_back(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_LOCAL_SET(0, WASM_I32V_2('A')), WASM_RETURN1(WASM_LOCAL_GET(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_GLOBAL_SET(0, WASM_I32V_1('4')),
WASM_GLOBAL_SET(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_LOCAL_SET(0, WASM_I32V_1('4')), WASM_LOCAL_GET(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_GLOBAL_SET(0, WASM_I32V_2('B')),
WASM_LOCAL_SET(0, WASM_I64V_2('A')), WASM_RETURN1(WASM_LOCAL_GET(0))},
{ValueType::kI64});
code.BreakOnReturn(&runner);
Isolate* isolate = runner.main_isolate();
Handle<String> snippet =
V8String(isolate,
"JSON.stringify(["
"$global0, "
"$table0, "
"$var0, "
"$main, "
"$memory0, "
"globals.$nope, "
"globals[0], "
"globals[1], "
"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.results().front();
CHECK_WITH_MSG(result.error.IsNothing(), result.error.ToChecked().c_str());
CHECK_EQ(result.result.ToChecked(),
"[\"66\",{},\"65\",\"function 0() { [native code] "
"}\",{},null,\"66\",null,{},\"65\",\"function 0() { [native code] "
"}\",{},{},{},{\"0\":\"65\"},{},{},{},{},{}]");
}
WASM_COMPILED_EXEC_TEST(WasmDebugEvaluate_JavaScript_Caching) {
WasmRunner<int> runner(execution_tier);
runner.builder().AddGlobal<int32_t>();
TestCode<int32_t> code(
&runner,
{WASM_GLOBAL_SET(0, WASM_I32V_2('B')), WASM_RETURN1(WASM_GLOBAL_GET(0))},
{});
code.BreakOnReturn(&runner);
Isolate* isolate = runner.main_isolate();
Handle<String> snippet = V8String(isolate, "JSON.stringify($global0)");
WasmJSBreakHandler break_handler(isolate, {snippet, snippet});
CHECK(!code.Run(&runner).is_null());
auto results = break_handler.results();
CHECK_EQ(results.size(), 2);
CHECK_WITH_MSG(results[0].error.IsNothing(),
results[0].error.ToChecked().c_str());
CHECK_EQ(results[0].result.ToChecked(), "66");
CHECK_WITH_MSG(results[1].error.IsNothing(),
results[1].error.ToChecked().c_str());
CHECK_EQ(results[1].result.ToChecked(), "66");
}
} // namespace
} // namespace wasm
} // namespace internal
} // namespace v8

View File

@ -1,5 +0,0 @@
Tests wasm debug-evaluate
Test: TestGetMemory
Result: 2
Expected: 2
Finished!

View File

@ -1,141 +0,0 @@
// 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.
// Flags: --expose-wasm --wasm-expose-debug-eval
utils.load('test/inspector/wasm-inspector-test.js');
const {session, contextGroup, Protocol} =
InspectorTest.start('Tests wasm debug-evaluate');
function printFailure(message) {
if (!message.result) {
InspectorTest.logMessage(message);
}
return message;
}
async function getWasmScript() {
while (true) {
const script = await Protocol.Debugger.onceScriptParsed();
if (script.params.url.startsWith('wasm://')) return script.params;
}
}
async function handleDebuggerPaused(data, messageObject) {
const topFrameId = messageObject.params.callFrames[0].callFrameId;
const params = {callFrameId: topFrameId, evaluator: data};
try {
const evalResult = await Protocol.Debugger.executeWasmEvaluator(params);
InspectorTest.log('Result: ' + evalResult.result.result.value);
} catch (err) {
InspectorTest.log(
'Eval failed: ' + err + '\nGot: ' + JSON.stringify(evalResult));
}
await Protocol.Debugger.resume();
}
async function runTest(testName, breakLine, debuggeeBytes, snippetBytes) {
try {
await Protocol.Debugger.onPaused(
handleDebuggerPaused.bind(null, snippetBytes));
InspectorTest.log('Test: ' + testName);
const scriptListener = getWasmScript();
await WasmInspectorTest.instantiate(debuggeeBytes);
const script = await scriptListener;
const msg = await Protocol.Debugger.setBreakpoint({
'location': {
'scriptId': script.scriptId,
'lineNumber': 0,
'columnNumber': breakLine
}
});
printFailure(msg);
const eval = await Protocol.Runtime.evaluate(
{'expression': 'instance.exports.main()'});
InspectorTest.log(
'Expected: ' + String.fromCharCode(eval.result.result.value));
InspectorTest.log('Finished!');
} catch (err) {
InspectorTest.log(err.message);
}
}
// copied from v8
function encode64(data) {
const BASE =
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
const PAD = '=';
var ret = '';
var leftchar = 0;
var leftbits = 0;
for (var i = 0; i < data.length; i++) {
leftchar = (leftchar << 8) | data[i];
leftbits += 8;
while (leftbits >= 6) {
const curr = (leftchar >> (leftbits - 6)) & 0x3f;
leftbits -= 6;
ret += BASE[curr];
}
}
if (leftbits == 2) {
ret += BASE[(leftchar & 3) << 4];
ret += PAD + PAD;
} else if (leftbits == 4) {
ret += BASE[(leftchar & 0xf) << 2];
ret += PAD;
}
return ret;
}
(async () => {
try {
await Protocol.Debugger.enable();
await (async function TestGetMemory() {
const debuggee_builder = new WasmModuleBuilder();
debuggee_builder.addMemory(256, 256);
const mainFunc =
debuggee_builder.addFunction('main', kSig_i_v)
.addBody([
// clang-format off
kExprI32Const, 32,
kExprI32Const, 50,
kExprI32StoreMem, 0, 0,
kExprI32Const, 32,
kExprI32LoadMem, 0, 0,
kExprReturn
// clang-format on
])
.exportAs('main');
const snippet_builder = new WasmModuleBuilder();
snippet_builder.addMemory(1, 1);
const getMemoryIdx = snippet_builder.addImport(
'env', '__getMemory', makeSig([kWasmI32, kWasmI32, kWasmI32], []));
const heapBase = 32; // Just pick some position in memory
snippet_builder.addFunction('wasm_format', kSig_i_v)
.addBody([
// clang-format off
// __getMemory(32, 4, heapBase)
kExprI32Const, 32, kExprI32Const, 4, kExprI32Const, heapBase,
kExprCallFunction, getMemoryIdx,
// return heapBase
kExprI32Const, heapBase,
kExprReturn
// clang-format on
])
.exportAs('wasm_format');
const debuggeeModule = debuggee_builder.toArray();
await runTest(
'TestGetMemory', mainFunc.body_offset + 9, debuggeeModule,
encode64(snippet_builder.toArray()));
})();
} catch (err) {
InspectorTest.log(err)
}
InspectorTest.completeTest();
})();