[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/+/3909810
Reviewed-by: Philip Pfaffe <pfaffe@chromium.org>
Reviewed-by: Jakob Kummerow <jkummerow@chromium.org>
Commit-Queue: Matthias Liedtke <mliedtke@chromium.org>
Cr-Commit-Position: refs/heads/main@{#83385}
This commit is contained in:
Matthias Liedtke 2022-09-22 10:15:06 +02:00 committed by V8 LUCI CQ
parent 98c6c367b5
commit 3665fbaaf5
4 changed files with 129 additions and 29 deletions

View File

@ -729,6 +729,18 @@ Handle<String> GetRefTypeName(Isolate* isolate, wasm::ValueType type,
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
// static
@ -905,38 +917,49 @@ Handle<WasmValueObject> WasmValueObject::New(
}
case wasm::kRefNull:
case wasm::kRef: {
t = GetRefTypeName(isolate, value.type(), module_object->native_module());
Handle<Object> ref = value.to_ref();
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),
module_object);
Handle<WasmModuleObject> module(type_info.instance().module_object(),
isolate);
t = GetRefTypeName(isolate, type, module->native_module());
v = StructProxy::Create(isolate, Handle<WasmStruct>::cast(ref), module);
} 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),
module_object);
Handle<WasmModuleObject> module(type_info.instance().module_object(),
isolate);
t = GetRefTypeName(isolate, type, module->native_module());
v = ArrayProxy::Create(isolate, Handle<WasmArray>::cast(ref), module);
} else if (ref->IsWasmInternalFunction()) {
v = handle(Handle<WasmInternalFunction>::cast(ref)->external(),
isolate);
auto internal_fct = Handle<WasmInternalFunction>::cast(ref);
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() ||
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;
} else {
// Fail gracefully.
base::EmbeddedVector<char, 64> error;
int len = SNPrintF(error, "unimplemented object type: %d",
HeapObject::cast(*ref).map().instance_type());
t = GetRefTypeName(isolate, value.type(), module_object);
v = isolate->factory()->InternalizeString(error.SubVector(0, len));
}
break;
@ -1037,8 +1060,11 @@ Handle<ArrayList> AddWasmTableObjectInternalProperties(
for (int i = 0; i < length; ++i) {
Handle<Object> entry = WasmTableObject::Get(isolate, table, i);
wasm::WasmValue wasm_value(entry, table->type());
Handle<WasmModuleObject> module(
WasmInstanceObject::cast(table->instance()).module_object(), isolate);
Handle<WasmModuleObject> module;
if (table->instance().IsWasmInstanceObject()) {
module = Handle<WasmModuleObject>(
WasmInstanceObject::cast(table->instance()).module_object(), isolate);
}
Handle<Object> debug_value =
WasmValueObject::New(isolate, wasm_value, module);
entries->set(i, *debug_value);

View File

@ -4,18 +4,18 @@ Running test: test
Calling instantiate function.
Waiting for wasm script to be parsed.
Got wasm script!
Setting breakpoint
Module instantiated.
Tables populated.
Setting breakpoint
{
columnNumber : 138
columnNumber : 246
lineNumber : 0
scriptId : <scriptId>
}
Table populated.
Paused:
Script wasm://wasm/0e116a66 byte offset 138: Wasm opcode 0x01 (kExprNop)
Script wasm://wasm/739f5f0a byte offset 246: Wasm opcode 0x01 (kExprNop)
Scope:
at $main (0:138):
at $main (0:246):
- scope (wasm-expression-stack):
stack:
- scope (local):
@ -24,11 +24,15 @@ at $main (0:138):
$anyref_local_i31: null (anyref)
$anyref_local_null: null (anyref)
- 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
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:
$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_func_table: 0: function external_fct() { [native code] } (funcref), 1: function $my_func() { [native code] } (funcref), 2: undefined (funcref)
at (anonymous) (0:17):
- scope (global):
-- skipped globals

View File

@ -2,7 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// 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');
@ -17,8 +18,9 @@ let breakpointLocation = -1;
InspectorTest.runAsyncTestSuite([
async function test() {
instantiateWasm();
let wasm_promise = instantiateWasm();
let scriptIds = await waitForWasmScripts();
await wasm_promise; // Make sure the instantiation is finished.
// Set a breakpoint.
InspectorTest.log('Setting breakpoint');
@ -66,10 +68,20 @@ async function instantiateWasm() {
var builder = new WasmModuleBuilder();
let struct_type = builder.addStruct([makeField(kWasmI32, false)]);
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)
.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([
...wasmI32Const(0), ...wasmI32Const(123),
kGCPrefix, kExprStructNew, struct_type, kExprTableSet, ref_table.index,
@ -83,6 +95,21 @@ async function instantiateWasm() {
// apart.
// ...wasmI32Const(2), ...wasmI32Const(30),
// 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();
let body = [
@ -114,11 +141,32 @@ async function instantiateWasm() {
breakpointLocation = main.body_offset + body.length - 1;
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.');
await WasmInspectorTest.evalWithUrl(
'instance.exports.fill_ref_table()', 'fill_ref_table');
InspectorTest.log('Table populated.');
'instance.exports.fill_tables();', 'fill_tables');
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() {

View File

@ -7,6 +7,7 @@
#include "include/v8-object.h"
#include "include/v8-template.h"
#include "src/api/api.h"
#include "src/objects/js-array-inl.h"
#include "src/objects/objects-inl.h"
#include "src/runtime/runtime.h"
#include "test/unittests/test-utils.h"
@ -56,6 +57,27 @@ TEST_F(RuntimeTest, DoesNotReturnPrototypeWhenInacessible) {
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 internal
} // namespace v8