diff --git a/src/wasm/wasm-debug.cc b/src/wasm/wasm-debug.cc index 5b75139082..f0c5678fd8 100644 --- a/src/wasm/wasm-debug.cc +++ b/src/wasm/wasm-debug.cc @@ -60,6 +60,8 @@ Handle WasmValueToValueObject(Isolate* isolate, WasmValue value) { return isolate->factory()->NewNumber(value.to()); case kWasmF64: return isolate->factory()->NewNumber(value.to()); + case kWasmAnyRef: + return value.to_anyref(); default: UNIMPLEMENTED(); return isolate->factory()->undefined_value(); @@ -359,7 +361,6 @@ class InterpreterHandle { Isolate* isolate = isolate_; Handle instance(debug_info->wasm_instance(), isolate); - // TODO(clemensh): Add globals to the global scope. Handle global_scope_object = isolate_->factory()->NewJSObjectWithNullProto(); if (instance->has_memory_object()) { @@ -373,6 +374,32 @@ class InterpreterHandle { uint8_array, NONE) .Assert(); } + + DCHECK_EQ(1, interpreter()->GetThreadCount()); + WasmInterpreter::Thread* thread = interpreter()->GetThread(0); + + uint32_t global_count = thread->GetGlobalCount(); + if (global_count > 0) { + Handle globals_obj = + isolate_->factory()->NewJSObjectWithNullProto(); + Handle globals_name = + isolate_->factory()->InternalizeOneByteString( + StaticCharVector("globals")); + JSObject::SetOwnPropertyIgnoreAttributes(global_scope_object, + globals_name, globals_obj, NONE) + .Assert(); + + for (uint32_t i = 0; i < global_count; ++i) { + const char* label = "global#%d"; + Handle name = PrintFToOneByteString(isolate_, label, i); + WasmValue value = thread->GetGlobalValue(i); + Handle value_obj = WasmValueToValueObject(isolate_, value); + JSObject::SetOwnPropertyIgnoreAttributes(globals_obj, name, value_obj, + NONE) + .Assert(); + } + } + return global_scope_object; } diff --git a/src/wasm/wasm-interpreter.cc b/src/wasm/wasm-interpreter.cc index 99adfcbf74..ede1260319 100644 --- a/src/wasm/wasm-interpreter.cc +++ b/src/wasm/wasm-interpreter.cc @@ -1262,6 +1262,37 @@ class ThreadImpl { return WasmInterpreter::Thread::HANDLED; } + uint32_t GetGlobalCount() { + return static_cast(module()->globals.size()); + } + + WasmValue GetGlobalValue(uint32_t index) { + const WasmGlobal* global = &module()->globals[index]; + switch (global->type) { +#define CASE_TYPE(wasm, ctype) \ + case kWasm##wasm: { \ + byte* ptr = GetGlobalPtr(global); \ + return WasmValue( \ + ReadLittleEndianValue(reinterpret_cast
(ptr))); \ + break; \ + } + WASM_CTYPES(CASE_TYPE) +#undef CASE_TYPE + case kWasmAnyRef: + case kWasmAnyFunc: + case kWasmExceptRef: { + HandleScope handle_scope(isolate_); // Avoid leaking handles. + Handle global_buffer; // The buffer of the global. + uint32_t global_index = 0; // The index into the buffer. + GetGlobalBufferAndIndex(global, &global_buffer, &global_index); + Handle value(global_buffer->get(global_index), isolate_); + return WasmValue(handle_scope.CloseAndEscape(value)); + } + default: + UNREACHABLE(); + } + } + private: // Handle a thrown exception. Returns whether the exception was handled inside // the current activation. Unwinds the interpreted stack accordingly. @@ -3055,31 +3086,8 @@ class ThreadImpl { case kExprGetGlobal: { GlobalIndexImmediate imm(&decoder, code->at(pc)); - const WasmGlobal* global = &module()->globals[imm.index]; - switch (global->type) { -#define CASE_TYPE(wasm, ctype) \ - case kWasm##wasm: { \ - byte* ptr = GetGlobalPtr(global); \ - Push(WasmValue( \ - ReadLittleEndianValue(reinterpret_cast
(ptr)))); \ - break; \ - } - WASM_CTYPES(CASE_TYPE) -#undef CASE_TYPE - case kWasmAnyRef: - case kWasmAnyFunc: - case kWasmExceptRef: { - HandleScope handle_scope(isolate_); // Avoid leaking handles. - Handle global_buffer; // The buffer of the global. - uint32_t global_index = 0; // The index into the buffer. - GetGlobalBufferAndIndex(global, &global_buffer, &global_index); - Handle value(global_buffer->get(global_index), isolate_); - Push(WasmValue(value)); - break; - } - default: - UNREACHABLE(); - } + HandleScope handle_scope(isolate_); + Push(GetGlobalValue(imm.index)); len = 1 + imm.length; break; } @@ -3821,6 +3829,12 @@ WasmValue WasmInterpreter::Thread::GetReturnValue(int index) { TrapReason WasmInterpreter::Thread::GetTrapReason() { return ToImpl(this)->GetTrapReason(); } +uint32_t WasmInterpreter::Thread::GetGlobalCount() { + return ToImpl(this)->GetGlobalCount(); +} +WasmValue WasmInterpreter::Thread::GetGlobalValue(uint32_t index) { + return ToImpl(this)->GetGlobalValue(index); +} bool WasmInterpreter::Thread::PossibleNondeterminism() { return ToImpl(this)->PossibleNondeterminism(); } diff --git a/src/wasm/wasm-interpreter.h b/src/wasm/wasm-interpreter.h index 9432446fb8..da0ce01835 100644 --- a/src/wasm/wasm-interpreter.h +++ b/src/wasm/wasm-interpreter.h @@ -138,6 +138,9 @@ class V8_EXPORT_PRIVATE WasmInterpreter { WasmValue GetReturnValue(int index = 0); TrapReason GetTrapReason(); + uint32_t GetGlobalCount(); + WasmValue GetGlobalValue(uint32_t index); + // Returns true if the thread executed an instruction which may produce // nondeterministic results, e.g. float div, float sqrt, and float mul, // where the sign bit of a NaN is nondeterministic. diff --git a/test/inspector/debugger/wasm-anyref-global-expected.txt b/test/inspector/debugger/wasm-anyref-global-expected.txt new file mode 100644 index 0000000000..d7b67a93de --- /dev/null +++ b/test/inspector/debugger/wasm-anyref-global-expected.txt @@ -0,0 +1,7 @@ +Test wasm scope information with anyref globals +Waiting for wasm script to be parsed. +Setting breakpoint in wasm. +Running main. +Paused in debugger. + globals: {"global#0": hello, world} +Finished. diff --git a/test/inspector/debugger/wasm-anyref-global.js b/test/inspector/debugger/wasm-anyref-global.js new file mode 100644 index 0000000000..d4c88ac694 --- /dev/null +++ b/test/inspector/debugger/wasm-anyref-global.js @@ -0,0 +1,83 @@ +// Copyright 2019 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: --experimental-wasm-anyref + +let {session, contextGroup, Protocol} = + InspectorTest.start('Test wasm scope information with anyref globals'); + +(async function() { + try { + utils.load('test/mjsunit/wasm/wasm-module-builder.js'); + + let builder = new WasmModuleBuilder(); + builder.addImportedGlobal('m', 'global', kWasmAnyRef, false); + builder.addFunction('func', kSig_v_v) + .addBody([ + kExprGetGlobal, 0, // + kExprDrop, // + ]) + .exportAs('main'); + let moduleBytes = JSON.stringify(builder.toArray()); + + function test(moduleBytes) { + let module = new WebAssembly.Module((new Uint8Array(moduleBytes)).buffer); + let global = 'hello, world'; + instance = new WebAssembly.Instance(module, {m: {global}}); + } + + Protocol.Debugger.enable(); + Protocol.Runtime.evaluate({ + expression: ` + let instance; + ${test.toString()} + test(${moduleBytes});` + }); + + InspectorTest.log('Waiting for wasm script to be parsed.'); + let scriptId; + while (true) { + let msg = await Protocol.Debugger.onceScriptParsed(); + if (msg.params.url.startsWith('wasm://')) { + scriptId = msg.params.scriptId; + break; + } + } + + InspectorTest.log('Setting breakpoint in wasm.'); + await Protocol.Debugger.setBreakpoint( + {location: {scriptId, lineNumber: 2}}); + + InspectorTest.log('Running main.'); + Protocol.Runtime.evaluate({expression: 'instance.exports.main()'}); + + let msg = await Protocol.Debugger.oncePaused(); + let callFrames = msg.params.callFrames; + InspectorTest.log('Paused in debugger.'); + let scopeChain = callFrames[0].scopeChain; + for (let scope of scopeChain) { + if (scope.type != 'global') continue; + + let globalObjectProps = (await Protocol.Runtime.getProperties({ + 'objectId': scope.object.objectId + })).result.result; + + for (let prop of globalObjectProps) { + let subProps = (await Protocol.Runtime.getProperties({ + objectId: prop.value.objectId + })).result.result; + let values = + subProps.map((value) => `"${value.name}": ${value.value.value}`) + .join(', '); + InspectorTest.log(` ${prop.name}: {${values}}`); + } + } + + InspectorTest.log('Finished.'); + } catch (exc) { + InspectorTest.log(`Failed with exception: ${exc}.`); + } finally { + InspectorTest.completeTest(); + } +})(); diff --git a/test/inspector/debugger/wasm-scope-info-expected.txt b/test/inspector/debugger/wasm-scope-info-expected.txt index f9e900d0d1..650d934958 100644 --- a/test/inspector/debugger/wasm-scope-info-expected.txt +++ b/test/inspector/debugger/wasm-scope-info-expected.txt @@ -17,7 +17,7 @@ Paused: Scope: at func (2:2): - scope (global): - -- skipped globals + globals: "global#0": 0 (number) - scope (local): locals: "i32Arg": 4 (number), "local#1": 0 (number), "i64_local": 0 (number), "unicode☼f64": 0 (number) stack: @@ -33,7 +33,7 @@ Paused: Scope: at func (3:2): - scope (global): - -- skipped globals + globals: "global#0": 0 (number) - scope (local): locals: "i32Arg": 4 (number), "local#1": 0 (number), "i64_local": 0 (number), "unicode☼f64": 0 (number) stack: "0": 11 (number) @@ -49,7 +49,7 @@ Paused: Scope: at func (4:2): - scope (global): - -- skipped globals + globals: "global#0": 0 (number) - scope (local): locals: "i32Arg": 11 (number), "local#1": 0 (number), "i64_local": 0 (number), "unicode☼f64": 0 (number) stack: @@ -65,7 +65,7 @@ Paused: Scope: at func (5:2): - scope (global): - -- skipped globals + globals: "global#0": 0 (number) - scope (local): locals: "i32Arg": 11 (number), "local#1": 0 (number), "i64_local": 0 (number), "unicode☼f64": 0 (number) stack: "0": 47 (number) @@ -81,7 +81,7 @@ Paused: Scope: at func (6:2): - scope (global): - -- skipped globals + globals: "global#0": 0 (number) - scope (local): locals: "i32Arg": 11 (number), "local#1": 47 (number), "i64_local": 0 (number), "unicode☼f64": 0 (number) stack: @@ -97,7 +97,7 @@ Paused: Scope: at func (7:2): - scope (global): - -- skipped globals + globals: "global#0": 0 (number) - scope (local): locals: "i32Arg": 11 (number), "local#1": 47 (number), "i64_local": 0 (number), "unicode☼f64": 0 (number) stack: "0": 9223372036854775807 (string) @@ -113,7 +113,7 @@ Paused: Scope: at func (8:2): - scope (global): - -- skipped globals + globals: "global#0": 0 (number) - scope (local): locals: "i32Arg": 11 (number), "local#1": 47 (number), "i64_local": 9223372036854775807 (string), "unicode☼f64": 0 (number) stack: @@ -129,7 +129,7 @@ Paused: Scope: at func (9:2): - scope (global): - -- skipped globals + globals: "global#0": 0 (number) - scope (local): locals: "i32Arg": 11 (number), "local#1": 47 (number), "i64_local": 9223372036854775807 (string), "unicode☼f64": 0 (number) stack: "0": -9223372036854775808 (string) @@ -145,7 +145,7 @@ Paused: Scope: at func (10:2): - scope (global): - -- skipped globals + globals: "global#0": 0 (number) - scope (local): locals: "i32Arg": 11 (number), "local#1": 47 (number), "i64_local": -9223372036854775808 (string), "unicode☼f64": 0 (number) stack: @@ -161,7 +161,7 @@ Paused: Scope: at func (11:2): - scope (global): - -- skipped globals + globals: "global#0": 0 (number) - scope (local): locals: "i32Arg": 11 (number), "local#1": 47 (number), "i64_local": -9223372036854775808 (string), "unicode☼f64": 0 (number) stack: "0": 1 (number) @@ -177,7 +177,7 @@ Paused: Scope: at func (12:2): - scope (global): - -- skipped globals + globals: "global#0": 0 (number) - scope (local): locals: "i32Arg": 11 (number), "local#1": 47 (number), "i64_local": -9223372036854775808 (string), "unicode☼f64": 0 (number) stack: "0": 1 (number) @@ -193,7 +193,7 @@ Paused: Scope: at func (13:2): - scope (global): - -- skipped globals + globals: "global#0": 0 (number) - scope (local): locals: "i32Arg": 11 (number), "local#1": 47 (number), "i64_local": -9223372036854775808 (string), "unicode☼f64": 0 (number) stack: "0": 1 (number), "1": 7 (number) @@ -209,7 +209,7 @@ Paused: Scope: at func (14:2): - scope (global): - -- skipped globals + globals: "global#0": 0 (number) - scope (local): locals: "i32Arg": 11 (number), "local#1": 47 (number), "i64_local": -9223372036854775808 (string), "unicode☼f64": 0 (number) stack: "0": 1 (number), "1": 7 (number) @@ -220,12 +220,12 @@ at (anonymous) (0:17): Paused: f64.div #set_local 3 -end + i32.const 15 Scope: at func (15:2): - scope (global): - -- skipped globals + globals: "global#0": 0 (number) - scope (local): locals: "i32Arg": 11 (number), "local#1": 47 (number), "i64_local": -9223372036854775808 (string), "unicode☼f64": 0 (number) stack: "0": 0.14285714285714285 (number) @@ -235,13 +235,45 @@ at (anonymous) (0:17): Paused: set_local 3 + #i32.const 15 + set_global 0 + +Scope: +at func (16:2): + - scope (global): + globals: "global#0": 0 (number) + - scope (local): + locals: "i32Arg": 11 (number), "local#1": 47 (number), "i64_local": -9223372036854775808 (string), "unicode☼f64": 0.14285714285714285 (number) + stack: +at (anonymous) (0:17): + - scope (global): + -- skipped globals + +Paused: + i32.const 15 + #set_global 0 +end + +Scope: +at func (17:2): + - scope (global): + globals: "global#0": 0 (number) + - scope (local): + locals: "i32Arg": 11 (number), "local#1": 47 (number), "i64_local": -9223372036854775808 (string), "unicode☼f64": 0.14285714285714285 (number) + stack: "0": 15 (number) +at (anonymous) (0:17): + - scope (global): + -- skipped globals + +Paused: + set_global 0 #end Scope: -at func (16:0): +at func (18:0): - scope (global): - -- skipped globals + globals: "global#0": 15 (number) - scope (local): locals: "i32Arg": 11 (number), "local#1": 47 (number), "i64_local": -9223372036854775808 (string), "unicode☼f64": 0.14285714285714285 (number) stack: diff --git a/test/inspector/debugger/wasm-scope-info.js b/test/inspector/debugger/wasm-scope-info.js index f7a0df497f..116b0ce146 100644 --- a/test/inspector/debugger/wasm-scope-info.js +++ b/test/inspector/debugger/wasm-scope-info.js @@ -33,6 +33,7 @@ async function instantiateWasm() { utils.load('test/mjsunit/wasm/wasm-module-builder.js'); var builder = new WasmModuleBuilder(); + builder.addGlobal(kWasmI32, true); builder.addFunction('func', kSig_v_i) .addLocals( @@ -51,7 +52,10 @@ async function instantiateWasm() { kExprSetLocal, 2, // Set local 3 to 1/7. kExprI32Const, 1, kExprF64UConvertI32, kExprI32Const, 7, - kExprF64UConvertI32, kExprF64Div, kExprSetLocal, 3 + kExprF64UConvertI32, kExprF64Div, kExprSetLocal, 3, + + // Set global 0 to 15 + kExprI32Const, 15, kExprSetGlobal, 0, ]) .exportAs('main'); @@ -129,13 +133,15 @@ async function dumpScopeProperties(message) { async function dumpScopeChainsOnPause(message) { InspectorTest.log(`Scope:`); for (var frame of message.params.callFrames) { + var isWasmFrame = /^wasm/.test(frame.url); var functionName = frame.functionName || '(anonymous)'; var lineNumber = frame.location ? frame.location.lineNumber : frame.lineNumber; var columnNumber = frame.location ? frame.location.columnNumber : frame.columnNumber; InspectorTest.log(`at ${functionName} (${lineNumber}:${columnNumber}):`); for (var scope of frame.scopeChain) { InspectorTest.logObject(' - scope (' + scope.type + '):'); - if (scope.type == 'global') { + if (!isWasmFrame && scope.type == 'global') { + // Skip global scope for non wasm-functions. InspectorTest.logObject(' -- skipped globals'); continue; }