Commit ee9b0f9f authored by Matthias Liedtke's avatar Matthias Liedtke Committed by V8 LUCI CQ

[wasm-gc] Debugger: Provide type info for structs and arrays in tables

This change also modifies the way references are typed: Instead of
using the static type (which may be a generic type like anyref) the
actual type based on the referenced object is used.
While this is very useful for arrays and structs (and somewhat nice for
i31 not just being a number but also having some type information), it
means for non-null values that the reference type is "not nullable",
so it will show e.g. "ref $type0" although the static type  might be
"ref null $type0".

Bug: v8:7748
Change-Id: I00c3258b0da6f89ec5efffd2a963889b1f341c3a
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3852485Reviewed-by: 's avatarBenedikt Meurer <bmeurer@chromium.org>
Commit-Queue: Matthias Liedtke <mliedtke@chromium.org>
Cr-Commit-Position: refs/heads/main@{#82753}
parent b592c968
...@@ -908,9 +908,21 @@ Handle<WasmValueObject> WasmValueObject::New( ...@@ -908,9 +908,21 @@ Handle<WasmValueObject> WasmValueObject::New(
t = GetRefTypeName(isolate, value.type(), module_object->native_module()); t = GetRefTypeName(isolate, value.type(), module_object->native_module());
Handle<Object> ref = value.to_ref(); Handle<Object> ref = value.to_ref();
if (ref->IsWasmStruct()) { if (ref->IsWasmStruct()) {
WasmTypeInfo type_info = ref->GetHeapObject().map().wasm_type_info();
wasm::ValueType type = wasm::ValueType::FromIndex(
wasm::ValueKind::kRef, type_info.type_index());
t = GetRefTypeName(
isolate, type,
type_info.instance().module_object().native_module());
v = StructProxy::Create(isolate, Handle<WasmStruct>::cast(ref), v = StructProxy::Create(isolate, Handle<WasmStruct>::cast(ref),
module_object); module_object);
} else if (ref->IsWasmArray()) { } else if (ref->IsWasmArray()) {
WasmTypeInfo type_info = ref->GetHeapObject().map().wasm_type_info();
wasm::ValueType type = wasm::ValueType::FromIndex(
wasm::ValueKind::kRef, type_info.type_index());
t = GetRefTypeName(
isolate, type,
type_info.instance().module_object().native_module());
v = ArrayProxy::Create(isolate, Handle<WasmArray>::cast(ref), v = ArrayProxy::Create(isolate, Handle<WasmArray>::cast(ref),
module_object); module_object);
} else if (ref->IsWasmInternalFunction()) { } else if (ref->IsWasmInternalFunction()) {
...@@ -1023,10 +1035,14 @@ Handle<ArrayList> AddWasmTableObjectInternalProperties( ...@@ -1023,10 +1035,14 @@ Handle<ArrayList> AddWasmTableObjectInternalProperties(
int length = table->current_length(); int length = table->current_length();
Handle<FixedArray> entries = isolate->factory()->NewFixedArray(length); Handle<FixedArray> entries = isolate->factory()->NewFixedArray(length);
for (int i = 0; i < length; ++i) { for (int i = 0; i < length; ++i) {
// TODO(mliedtke): Allow inspecting non-JS-exportable elements.
Handle<Object> entry = Handle<Object> entry =
WasmTableObject::Get(isolate, table, i, WasmTableObject::kJS); WasmTableObject::Get(isolate, table, i, WasmTableObject::kWasm);
entries->set(i, *entry); wasm::WasmValue wasm_value(entry, table->type());
Handle<WasmModuleObject> module(
WasmInstanceObject::cast(table->instance()).module_object(), isolate);
Handle<Object> debug_value =
WasmValueObject::New(isolate, wasm_value, module);
entries->set(i, *debug_value);
} }
Handle<JSArray> final_entries = isolate->factory()->NewJSArrayWithElements( Handle<JSArray> final_entries = isolate->factory()->NewJSArrayWithElements(
entries, i::PACKED_ELEMENTS, length); entries, i::PACKED_ELEMENTS, length);
......
...@@ -7,23 +7,28 @@ Got wasm script! ...@@ -7,23 +7,28 @@ Got wasm script!
Setting breakpoint Setting breakpoint
Module instantiated. Module instantiated.
{ {
columnNumber : 61 columnNumber : 138
lineNumber : 0 lineNumber : 0
scriptId : <scriptId> scriptId : <scriptId>
} }
Table populated.
Paused: Paused:
Script wasm://wasm/f6eebe1a byte offset 61: Wasm opcode 0x01 (kExprNop) Script wasm://wasm/0e116a66 byte offset 138: Wasm opcode 0x01 (kExprNop)
Scope: Scope:
at $main (0:61): at $main (0:138):
- scope (wasm-expression-stack): - scope (wasm-expression-stack):
stack: stack:
- scope (local): - scope (local):
$anyref_local: Struct (anyref) $anyref_local: Struct ((ref $type0))
$anyref_local2: Array (anyref) $anyref_local2: Array ((ref $type1))
$anyref_local_i31: null (anyref)
$anyref_local_null: null (anyref)
- scope (module): - scope (module):
instance: exports: "main" (Function) instance: exports: "exported_ref_table" (Table), "fill_ref_table" (Function), "main" (Function)
module: Module module: Module
functions: "$main": (Function) functions: "$fill_ref_table": (Function), "$main": (Function)
tables:
$exported_ref_table: 0: Struct ((ref $type0)), 1: Array ((ref $type1)), 2: undefined (anyref), 3: undefined (anyref)
at (anonymous) (0:17): at (anonymous) (0:17):
- scope (global): - scope (global):
-- skipped globals -- skipped globals
......
...@@ -66,6 +66,24 @@ async function instantiateWasm() { ...@@ -66,6 +66,24 @@ async function instantiateWasm() {
var builder = new WasmModuleBuilder(); var builder = new WasmModuleBuilder();
let struct_type = builder.addStruct([makeField(kWasmI32, false)]); let struct_type = builder.addStruct([makeField(kWasmI32, false)]);
let array_type = builder.addArray(kWasmI32); let array_type = builder.addArray(kWasmI32);
let ref_table = builder.addTable(kWasmAnyRef, 4)
.exportAs('exported_ref_table');
builder.addFunction('fill_ref_table', kSig_v_v)
.addBody([
...wasmI32Const(0), ...wasmI32Const(123),
kGCPrefix, kExprStructNew, struct_type, kExprTableSet, ref_table.index,
...wasmI32Const(1), ...wasmI32Const(20), ...wasmI32Const(21),
kGCPrefix, kExprArrayNewFixed, array_type, 2,
kExprTableSet, ref_table.index,
// TODO(7748): Reactivate this test when JS interop between i31refs and
// JS SMIs is fixed. The problem right now is the 33-bit shift for i31ref
// values on non-pointer-compressed platforms, which means i31refs and
// Smis have different encodings there but it's impossible to tell them
// apart.
// ...wasmI32Const(2), ...wasmI32Const(30),
// kGCPrefix, kExprI31New, kExprTableSet, ref_table.index,
]).exportFunc();
let body = [ let body = [
// Set local anyref_local to new struct. // Set local anyref_local to new struct.
...@@ -76,11 +94,19 @@ async function instantiateWasm() { ...@@ -76,11 +94,19 @@ async function instantiateWasm() {
...wasmI32Const(21), ...wasmI32Const(21),
kGCPrefix, kExprArrayNewFixed, array_type, 1, kGCPrefix, kExprArrayNewFixed, array_type, 1,
kExprLocalSet, 1, kExprLocalSet, 1,
// Set local anyref_local_i31.
// TODO(7748): Reactivate this test when JS interop between i31refs and JS
// SMIs is fixed (same issue as above).
// ...wasmI32Const(30),
// kGCPrefix, kExprI31New,
// kExprLocalSet, 2,
kExprNop, kExprNop,
]; ];
let main = builder.addFunction('main', kSig_v_v) let main = builder.addFunction('main', kSig_v_v)
.addLocals(kWasmAnyRef, 1, ['anyref_local']) .addLocals(kWasmAnyRef, 1, ['anyref_local'])
.addLocals(kWasmAnyRef, 1, ['anyref_local2']) .addLocals(kWasmAnyRef, 1, ['anyref_local2'])
.addLocals(kWasmAnyRef, 1, ['anyref_local_i31'])
.addLocals(kWasmAnyRef, 1, ['anyref_local_null'])
.addBody(body) .addBody(body)
.exportFunc(); .exportFunc();
...@@ -90,6 +116,9 @@ async function instantiateWasm() { ...@@ -90,6 +116,9 @@ async function instantiateWasm() {
InspectorTest.log('Calling instantiate function.'); InspectorTest.log('Calling instantiate function.');
await WasmInspectorTest.instantiate(module_bytes); await WasmInspectorTest.instantiate(module_bytes);
InspectorTest.log('Module instantiated.'); InspectorTest.log('Module instantiated.');
await WasmInspectorTest.evalWithUrl(
'instance.exports.fill_ref_table()', 'fill_ref_table');
InspectorTest.log('Table populated.');
} }
async function waitForWasmScripts() { async function waitForWasmScripts() {
......
...@@ -13,11 +13,11 @@ at $main (0:103): ...@@ -13,11 +13,11 @@ at $main (0:103):
0: Array ((ref $ArrC)) 0: Array ((ref $ArrC))
1: hello world (stringref) 1: hello world (stringref)
object details: object details:
0: Struct ((ref null $StrA)) 0: Struct ((ref $StrA))
length: 1 (number) length: 1 (number)
- scope (local): - scope (local):
$var0: hello world (stringref) $var0: hello world (stringref)
$varA: Struct ((ref null $StrA)) $varA: Struct ((ref $StrA))
$varB: null ((ref null $ArrC)) $varB: null ((ref null $ArrC))
object details: object details:
$byte: 127 (i8) $byte: 127 (i8)
......
...@@ -154,7 +154,16 @@ async function dumpTables(tablesObj) { ...@@ -154,7 +154,16 @@ async function dumpTables(tablesObj) {
let functions = []; let functions = [];
for (let entry of entries.result.result) { for (let entry of entries.result.result) {
if (entry.name === 'length') continue; if (entry.name === 'length') continue;
functions.push(`${entry.name}: ${entry.value.description}`); let description = entry.value.description;
// For tables containing references, explore one level into the ref.
if (entry.value.objectId != null) {
let referencedObj = await Protocol.Runtime.getProperties(
{objectId: entry.value.objectId});
let value = referencedObj.result.result
.filter(prop => prop.name == "value")[0].value.description;
description = `${value} (${description})`;
}
functions.push(`${entry.name}: ${description}`);
} }
const functions_str = functions.join(', '); const functions_str = functions.join(', ');
tables_str.push(` ${table.name}: ${functions_str}`); tables_str.push(` ${table.name}: ${functions_str}`);
......
...@@ -218,3 +218,9 @@ for (let type of ["struct", "i31", "array"]) { ...@@ -218,3 +218,9 @@ for (let type of ["struct", "i31", "array"]) {
} }
} }
} }
// Differently to structs and arrays, the i31 value is directly accessible in
// JavaScript. Similarly, a JS smi can be internalized as an i31ref.
// TODO(7748): Fix i31 interop with disabled pointer compression.
// assertEquals(12345, instance.exports.i31_externalize(12345));
// assertEquals([12345, 0], instance.exports.i31_internalize(12345));
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment