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

[wasm] Fix inspection of imported wasm tables created in JS

Fixed: chromium:1365101
Change-Id: Ie6f5fa08416348e827de9a389af5d63eba118ceb
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3909810Reviewed-by: 's avatarPhilip Pfaffe <pfaffe@chromium.org>
Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Commit-Queue: Matthias Liedtke <mliedtke@chromium.org>
Cr-Commit-Position: refs/heads/main@{#83385}
parent 98c6c367
...@@ -729,6 +729,18 @@ Handle<String> GetRefTypeName(Isolate* isolate, wasm::ValueType type, ...@@ -729,6 +729,18 @@ Handle<String> GetRefTypeName(Isolate* isolate, wasm::ValueType type,
return ToInternalString(name, isolate); return ToInternalString(name, isolate);
} }
// Returns the type name for the given value. Uses the module object for
// providing user-defined type names if available, otherwise falls back
// to numbers for indexed types.
Handle<String> GetRefTypeName(Isolate* isolate, wasm::ValueType type,
Handle<WasmModuleObject> module_object) {
if (!module_object.is_null()) {
return GetRefTypeName(isolate, type, module_object->native_module());
}
std::string name = type.name();
return isolate->factory()->InternalizeString({name.data(), name.length()});
}
} // namespace } // namespace
// static // static
...@@ -905,38 +917,49 @@ Handle<WasmValueObject> WasmValueObject::New( ...@@ -905,38 +917,49 @@ Handle<WasmValueObject> WasmValueObject::New(
} }
case wasm::kRefNull: case wasm::kRefNull:
case wasm::kRef: { case wasm::kRef: {
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(); WasmTypeInfo type_info = ref->GetHeapObject().map().wasm_type_info();
wasm::ValueType type = wasm::ValueType::FromIndex( wasm::ValueType type = wasm::ValueType::FromIndex(
wasm::ValueKind::kRef, type_info.type_index()); wasm::ValueKind::kRef, type_info.type_index());
t = GetRefTypeName( Handle<WasmModuleObject> module(type_info.instance().module_object(),
isolate, type, isolate);
type_info.instance().module_object().native_module()); t = GetRefTypeName(isolate, type, module->native_module());
v = StructProxy::Create(isolate, Handle<WasmStruct>::cast(ref), v = StructProxy::Create(isolate, Handle<WasmStruct>::cast(ref), module);
module_object);
} else if (ref->IsWasmArray()) { } else if (ref->IsWasmArray()) {
WasmTypeInfo type_info = ref->GetHeapObject().map().wasm_type_info(); WasmTypeInfo type_info = ref->GetHeapObject().map().wasm_type_info();
wasm::ValueType type = wasm::ValueType::FromIndex( wasm::ValueType type = wasm::ValueType::FromIndex(
wasm::ValueKind::kRef, type_info.type_index()); wasm::ValueKind::kRef, type_info.type_index());
t = GetRefTypeName( Handle<WasmModuleObject> module(type_info.instance().module_object(),
isolate, type, isolate);
type_info.instance().module_object().native_module()); t = GetRefTypeName(isolate, type, module->native_module());
v = ArrayProxy::Create(isolate, Handle<WasmArray>::cast(ref), v = ArrayProxy::Create(isolate, Handle<WasmArray>::cast(ref), module);
module_object);
} else if (ref->IsWasmInternalFunction()) { } else if (ref->IsWasmInternalFunction()) {
v = handle(Handle<WasmInternalFunction>::cast(ref)->external(), auto internal_fct = Handle<WasmInternalFunction>::cast(ref);
isolate); v = handle(internal_fct->external(), isolate);
// If the module is not provided by the caller, retrieve it from the
// instance object. If the function was created in JavaScript using
// `new WebAssembly.Function(...)`, a module for name resolution is not
// available.
if (module_object.is_null() &&
internal_fct->ref().IsWasmInstanceObject()) {
module_object = handle(
WasmInstanceObject::cast(internal_fct->ref()).module_object(),
isolate);
}
t = GetRefTypeName(isolate, value.type(), module_object);
} else if (ref->IsJSFunction() || ref->IsSmi() || ref->IsNull() || } else if (ref->IsJSFunction() || ref->IsSmi() || ref->IsNull() ||
ref->IsString() || ref->IsString() ||
value.type().is_reference_to(wasm::HeapType::kExtern)) { value.type().is_reference_to(wasm::HeapType::kExtern) ||
value.type().is_reference_to(wasm::HeapType::kAny)) {
t = GetRefTypeName(isolate, value.type(), module_object);
v = ref; v = ref;
} else { } else {
// Fail gracefully. // Fail gracefully.
base::EmbeddedVector<char, 64> error; base::EmbeddedVector<char, 64> error;
int len = SNPrintF(error, "unimplemented object type: %d", int len = SNPrintF(error, "unimplemented object type: %d",
HeapObject::cast(*ref).map().instance_type()); HeapObject::cast(*ref).map().instance_type());
t = GetRefTypeName(isolate, value.type(), module_object);
v = isolate->factory()->InternalizeString(error.SubVector(0, len)); v = isolate->factory()->InternalizeString(error.SubVector(0, len));
} }
break; break;
...@@ -1037,8 +1060,11 @@ Handle<ArrayList> AddWasmTableObjectInternalProperties( ...@@ -1037,8 +1060,11 @@ Handle<ArrayList> AddWasmTableObjectInternalProperties(
for (int i = 0; i < length; ++i) { for (int i = 0; i < length; ++i) {
Handle<Object> entry = WasmTableObject::Get(isolate, table, i); Handle<Object> entry = WasmTableObject::Get(isolate, table, i);
wasm::WasmValue wasm_value(entry, table->type()); wasm::WasmValue wasm_value(entry, table->type());
Handle<WasmModuleObject> module( Handle<WasmModuleObject> module;
WasmInstanceObject::cast(table->instance()).module_object(), isolate); if (table->instance().IsWasmInstanceObject()) {
module = Handle<WasmModuleObject>(
WasmInstanceObject::cast(table->instance()).module_object(), isolate);
}
Handle<Object> debug_value = Handle<Object> debug_value =
WasmValueObject::New(isolate, wasm_value, module); WasmValueObject::New(isolate, wasm_value, module);
entries->set(i, *debug_value); entries->set(i, *debug_value);
......
...@@ -4,18 +4,18 @@ Running test: test ...@@ -4,18 +4,18 @@ Running test: test
Calling instantiate function. Calling instantiate function.
Waiting for wasm script to be parsed. Waiting for wasm script to be parsed.
Got wasm script! Got wasm script!
Setting breakpoint
Module instantiated. Module instantiated.
Tables populated.
Setting breakpoint
{ {
columnNumber : 138 columnNumber : 246
lineNumber : 0 lineNumber : 0
scriptId : <scriptId> scriptId : <scriptId>
} }
Table populated.
Paused: Paused:
Script wasm://wasm/0e116a66 byte offset 138: Wasm opcode 0x01 (kExprNop) Script wasm://wasm/739f5f0a byte offset 246: Wasm opcode 0x01 (kExprNop)
Scope: Scope:
at $main (0:138): at $main (0:246):
- scope (wasm-expression-stack): - scope (wasm-expression-stack):
stack: stack:
- scope (local): - scope (local):
...@@ -24,11 +24,15 @@ at $main (0:138): ...@@ -24,11 +24,15 @@ at $main (0:138):
$anyref_local_i31: null (anyref) $anyref_local_i31: null (anyref)
$anyref_local_null: null (anyref) $anyref_local_null: null (anyref)
- scope (module): - scope (module):
instance: exports: "exported_ref_table" (Table), "fill_ref_table" (Function), "main" (Function) instance: exports: "exported_ref_table" (Table), "exported_func_table" (Table), "fill_tables" (Function), "main" (Function)
module: Module module: Module
functions: "$fill_ref_table": (Function), "$main": (Function) functions: "$my_func": (Function), "$fill_tables": (Function), "$main": (Function)
globals: "$global0": function $my_func() { [native code] } (funcref)
tables: tables:
$import.any_table: 0: Array(2) (anyref), 1: Struct ((ref $type0)), 2: undefined (anyref)
$import.func_table: 0: function () { [native code] } (funcref), 1: function $my_func() { [native code] } (funcref), 2: undefined (funcref)
$exported_ref_table: 0: Struct ((ref $type0)), 1: Array ((ref $type1)), 2: undefined (anyref), 3: undefined (anyref) $exported_ref_table: 0: Struct ((ref $type0)), 1: Array ((ref $type1)), 2: undefined (anyref), 3: undefined (anyref)
$exported_func_table: 0: function external_fct() { [native code] } (funcref), 1: function $my_func() { [native code] } (funcref), 2: undefined (funcref)
at (anonymous) (0:17): at (anonymous) (0:17):
- scope (global): - scope (global):
-- skipped globals -- skipped globals
......
...@@ -2,7 +2,8 @@ ...@@ -2,7 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// Flags: --experimental-wasm-gc // Flags: --experimental-wasm-gc --experimental-wasm-typed-funcref
// Flags: --experimental-wasm-type-reflection
utils.load('test/inspector/wasm-inspector-test.js'); utils.load('test/inspector/wasm-inspector-test.js');
...@@ -17,8 +18,9 @@ let breakpointLocation = -1; ...@@ -17,8 +18,9 @@ let breakpointLocation = -1;
InspectorTest.runAsyncTestSuite([ InspectorTest.runAsyncTestSuite([
async function test() { async function test() {
instantiateWasm(); let wasm_promise = instantiateWasm();
let scriptIds = await waitForWasmScripts(); let scriptIds = await waitForWasmScripts();
await wasm_promise; // Make sure the instantiation is finished.
// Set a breakpoint. // Set a breakpoint.
InspectorTest.log('Setting breakpoint'); InspectorTest.log('Setting breakpoint');
...@@ -66,10 +68,20 @@ async function instantiateWasm() { ...@@ -66,10 +68,20 @@ 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 imported_ref_table =
builder.addImportedTable('import', 'any_table', 3, 3, kWasmAnyRef);
let imported_func_table =
builder.addImportedTable('import', 'func_table', 3, 3, kWasmFuncRef);
let ref_table = builder.addTable(kWasmAnyRef, 4) let ref_table = builder.addTable(kWasmAnyRef, 4)
.exportAs('exported_ref_table'); .exportAs('exported_ref_table');
let func_table = builder.addTable(kWasmFuncRef, 3)
.exportAs('exported_func_table');
builder.addFunction('fill_ref_table', kSig_v_v) let func = builder.addFunction('my_func', kSig_v_v).addBody([kExprNop]);
// Make the function "declared".
builder.addGlobal(kWasmFuncRef, false, [kExprRefFunc, func.index]);
builder.addFunction('fill_tables', kSig_v_v)
.addBody([ .addBody([
...wasmI32Const(0), ...wasmI32Const(123), ...wasmI32Const(0), ...wasmI32Const(123),
kGCPrefix, kExprStructNew, struct_type, kExprTableSet, ref_table.index, kGCPrefix, kExprStructNew, struct_type, kExprTableSet, ref_table.index,
...@@ -83,6 +95,21 @@ async function instantiateWasm() { ...@@ -83,6 +95,21 @@ async function instantiateWasm() {
// apart. // apart.
// ...wasmI32Const(2), ...wasmI32Const(30), // ...wasmI32Const(2), ...wasmI32Const(30),
// kGCPrefix, kExprI31New, kExprTableSet, ref_table.index, // kGCPrefix, kExprI31New, kExprTableSet, ref_table.index,
// Fill imported any table.
...wasmI32Const(1),
...wasmI32Const(123), kGCPrefix, kExprStructNew, struct_type,
kExprTableSet, imported_ref_table,
// Fill imported func table.
...wasmI32Const(1),
kExprRefFunc, func.index,
kExprTableSet, imported_func_table,
// Fill func table.
...wasmI32Const(1),
kExprRefFunc, func.index,
kExprTableSet, func_table.index,
]).exportFunc(); ]).exportFunc();
let body = [ let body = [
...@@ -114,11 +141,32 @@ async function instantiateWasm() { ...@@ -114,11 +141,32 @@ async function instantiateWasm() {
breakpointLocation = main.body_offset + body.length - 1; breakpointLocation = main.body_offset + body.length - 1;
InspectorTest.log('Calling instantiate function.'); InspectorTest.log('Calling instantiate function.');
await WasmInspectorTest.instantiate(module_bytes); let imports = `{'import' : {
'any_table': (() => {
let js_table =
new WebAssembly.Table({element: 'anyref', initial: 3, maximum: 3});
js_table.set(0, ['JavaScript', 'value']);
return js_table;
})(),
'func_table': (() => {
let func_table =
new WebAssembly.Table({element: 'anyfunc', initial: 3, maximum: 3});
func_table.set(0, new WebAssembly.Function(
{parameters:['i32', 'i32'], results: ['i32']},
function /*anonymous*/ (a, b) { return a * b; }));
return func_table;
})(),
}}`;
await WasmInspectorTest.instantiate(module_bytes, 'instance', imports);
InspectorTest.log('Module instantiated.'); InspectorTest.log('Module instantiated.');
await WasmInspectorTest.evalWithUrl( await WasmInspectorTest.evalWithUrl(
'instance.exports.fill_ref_table()', 'fill_ref_table'); 'instance.exports.fill_tables();', 'fill_tables');
InspectorTest.log('Table populated.'); await WasmInspectorTest.evalWithUrl(
`instance.exports.exported_func_table.set(0, new WebAssembly.Function(
{parameters:['i32', 'i32'], results: ['i32']},
function external_fct(a, b) { return a * b; }))`,
'add_func_to_table');
InspectorTest.log('Tables populated.');
} }
async function waitForWasmScripts() { async function waitForWasmScripts() {
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include "include/v8-object.h" #include "include/v8-object.h"
#include "include/v8-template.h" #include "include/v8-template.h"
#include "src/api/api.h" #include "src/api/api.h"
#include "src/objects/js-array-inl.h"
#include "src/objects/objects-inl.h" #include "src/objects/objects-inl.h"
#include "src/runtime/runtime.h" #include "src/runtime/runtime.h"
#include "test/unittests/test-utils.h" #include "test/unittests/test-utils.h"
...@@ -56,6 +57,27 @@ TEST_F(RuntimeTest, DoesNotReturnPrototypeWhenInacessible) { ...@@ -56,6 +57,27 @@ TEST_F(RuntimeTest, DoesNotReturnPrototypeWhenInacessible) {
EXPECT_EQ(0u, result->Length()); EXPECT_EQ(0u, result->Length());
} }
#if V8_ENABLE_WEBASSEMBLY
TEST_F(RuntimeTest, WasmTableWithoutInstance) {
uint32_t initial = 1u;
bool has_maximum = false;
uint32_t maximum = std::numeric_limits<uint32_t>::max();
Handle<FixedArray> elements;
Handle<WasmTableObject> table = WasmTableObject::New(
i_isolate(), Handle<WasmInstanceObject>(), wasm::kWasmAnyRef, initial,
has_maximum, maximum, &elements, i_isolate()->factory()->null_value());
MaybeHandle<JSArray> result =
Runtime::GetInternalProperties(i_isolate(), table);
ASSERT_FALSE(result.is_null());
// ["[[Prototype]]", <map>, "[[Entries]]", <entries>]
ASSERT_EQ(4, result.ToHandleChecked()->elements().length());
Handle<Object> entries =
FixedArrayBase::GetElement(i_isolate(), result.ToHandleChecked(), 3)
.ToHandleChecked();
EXPECT_EQ(1, JSArray::cast(*entries).elements().length());
}
#endif
} // namespace } // namespace
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
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