Commit 051a29cc authored by Manos Koukoutos's avatar Manos Koukoutos Committed by Commit Bot

[wasm-gc] Implement JS roundtrip for anyref

We use the same temporary mechanism as with eqref, in anticipation of
standardization of the wasm-gc JS API.

Bug: v8:7748
Change-Id: I224a043e5450ce489fc7f3b2f07f277a0444b8e0
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2546695
Commit-Queue: Manos Koukoutos <manoskouk@chromium.org>
Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/master@{#71339}
parent c875ab35
......@@ -6201,12 +6201,19 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder {
return node;
}
if (representation == wasm::HeapType::kEq) {
// TODO(7748): Update this when JS interop is settled.
return BuildAllocateObjectWrapper(node);
}
if (representation == wasm::HeapType::kAny) {
// TODO(7748): Add wrapping for arrays/structs, or proper JS API when
// available.
return node;
// Only wrap {node} if it is an array or struct.
// TODO(7748): Update this when JS interop is settled.
auto done = gasm_->MakeLabel(MachineRepresentation::kTaggedPointer);
gasm_->GotoIfNot(gasm_->IsDataRefMap(gasm_->LoadMap(node)), &done,
node);
Node* wrapped = BuildAllocateObjectWrapper(node);
gasm_->Goto(&done, wrapped);
gasm_->Bind(&done);
return done.PhiAt(0);
}
if (type.has_index() && module_->has_signature(type.ref_index())) {
// Typed function
......@@ -6236,7 +6243,9 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder {
LOAD_INSTANCE_FIELD(NativeContext, MachineType::TaggedPointer()));
}
Node* BuildUnpackObjectWrapper(Node* input) {
enum UnpackFailureBehavior : bool { kReturnInput, kReturnNull };
Node* BuildUnpackObjectWrapper(Node* input, UnpackFailureBehavior failure) {
Node* obj = CALL_BUILTIN(
WasmGetOwnProperty, input,
LOAD_FULL_POINTER(BuildLoadIsolateRoot(),
......@@ -6244,7 +6253,8 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder {
RootIndex::kwasm_wrapped_object_symbol)),
LOAD_INSTANCE_FIELD(NativeContext, MachineType::TaggedPointer()));
// Invalid object wrappers (i.e. any other JS object that doesn't have the
// magic hidden property) will return {undefined}. Map that to {null}.
// magic hidden property) will return {undefined}. Map that to {null} or
// {input}, depending on the value of {failure}.
Node* undefined = LOAD_FULL_POINTER(
BuildLoadIsolateRoot(),
IsolateData::root_slot_offset(RootIndex::kUndefinedValue));
......@@ -6252,7 +6262,8 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder {
Diamond check(graph(), mcgraph()->common(), is_undefined,
BranchHint::kFalse);
check.Chain(control());
return check.Phi(MachineRepresentation::kTagged, RefNull(), obj);
return check.Phi(MachineRepresentation::kTagged,
failure == kReturnInput ? input : RefNull(), obj);
}
Node* BuildChangeInt64ToBigInt(Node* input) {
......@@ -6327,16 +6338,18 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder {
switch (type.heap_representation()) {
case wasm::HeapType::kExtern:
case wasm::HeapType::kExn:
// TODO(7748): Possibly implement different cases for 'any' when the
// JS API has settled.
case wasm::HeapType::kAny:
return input;
case wasm::HeapType::kAny:
// If this is a wrapper for arrays/structs, unpack it.
// TODO(7748): Update this when JS interop has settled.
return BuildUnpackObjectWrapper(input, kReturnInput);
case wasm::HeapType::kFunc:
BuildCheckValidRefValue(input, js_context, type);
return input;
case wasm::HeapType::kEq:
// TODO(7748): Update this when JS interop has settled.
BuildCheckValidRefValue(input, js_context, type);
return BuildUnpackObjectWrapper(input);
return BuildUnpackObjectWrapper(input, kReturnNull);
case wasm::HeapType::kI31:
// If this is reached, then IsJSCompatibleSignature() is too
// permissive.
......
......@@ -1276,71 +1276,73 @@ TEST(IndirectNullSetManually) {
}
TEST(JsAccess) {
WasmGCTester tester;
const byte type_index = tester.DefineStruct({F(wasm::kWasmI32, true)});
ValueType kRefTypes[] = {ref(type_index)};
ValueType kEqRefTypes[] = {kWasmEqRef};
ValueType kEqToI[] = {kWasmI32, kWasmEqRef};
FunctionSig sig_t_v(1, 0, kRefTypes);
FunctionSig sig_q_v(1, 0, kEqRefTypes);
FunctionSig sig_i_q(1, 1, kEqToI);
tester.DefineExportedFunction(
"disallowed", &sig_t_v,
{WASM_STRUCT_NEW_WITH_RTT(type_index, WASM_I32V(42),
WASM_RTT_CANON(type_index)),
kExprEnd});
// Same code, different signature.
tester.DefineExportedFunction(
"producer", &sig_q_v,
{WASM_STRUCT_NEW_WITH_RTT(type_index, WASM_I32V(42),
WASM_RTT_CANON(type_index)),
kExprEnd});
tester.DefineExportedFunction(
"consumer", &sig_i_q,
{WASM_STRUCT_GET(type_index, 0,
WASM_REF_CAST(kEqRefCode, type_index, WASM_GET_LOCAL(0),
WASM_RTT_CANON(type_index))),
kExprEnd});
tester.CompileModule();
Isolate* isolate = tester.isolate();
TryCatch try_catch(reinterpret_cast<v8::Isolate*>(isolate));
MaybeHandle<Object> maybe_result =
tester.CallExportedFunction("disallowed", 0, nullptr);
CHECK(maybe_result.is_null());
CHECK(try_catch.HasCaught());
try_catch.Reset();
isolate->clear_pending_exception();
maybe_result = tester.CallExportedFunction("producer", 0, nullptr);
if (maybe_result.is_null()) {
FATAL("Calling 'producer' failed: %s",
*v8::String::Utf8Value(reinterpret_cast<v8::Isolate*>(isolate),
try_catch.Message()->Get()));
}
{
Handle<Object> args[] = {maybe_result.ToHandleChecked()};
maybe_result = tester.CallExportedFunction("consumer", 1, args);
}
if (maybe_result.is_null()) {
FATAL("Calling 'consumer' failed: %s",
*v8::String::Utf8Value(reinterpret_cast<v8::Isolate*>(isolate),
try_catch.Message()->Get()));
}
Handle<Object> result = maybe_result.ToHandleChecked();
CHECK(result->IsSmi());
CHECK_EQ(42, Smi::cast(*result).value());
// Calling {consumer} with any other object (e.g. the Smi we just got as
// {result}) should trap.
{
Handle<Object> args[] = {result};
maybe_result = tester.CallExportedFunction("consumer", 1, args);
for (ValueType supertype : {kWasmEqRef, kWasmAnyRef}) {
WasmGCTester tester;
const byte type_index = tester.DefineStruct({F(wasm::kWasmI32, true)});
ValueType kRefType = ref(type_index);
ValueType kSupertypeToI[] = {kWasmI32, supertype};
FunctionSig sig_t_v(1, 0, &kRefType);
FunctionSig sig_super_v(1, 0, &supertype);
FunctionSig sig_i_super(1, 1, kSupertypeToI);
tester.DefineExportedFunction(
"disallowed", &sig_t_v,
{WASM_STRUCT_NEW_WITH_RTT(type_index, WASM_I32V(42),
WASM_RTT_CANON(type_index)),
kExprEnd});
// Same code, different signature.
tester.DefineExportedFunction(
"producer", &sig_super_v,
{WASM_STRUCT_NEW_WITH_RTT(type_index, WASM_I32V(42),
WASM_RTT_CANON(type_index)),
kExprEnd});
tester.DefineExportedFunction(
"consumer", &sig_i_super,
{WASM_STRUCT_GET(
type_index, 0,
WASM_REF_CAST(supertype.value_type_code(), type_index,
WASM_GET_LOCAL(0), WASM_RTT_CANON(type_index))),
kExprEnd});
tester.CompileModule();
Isolate* isolate = tester.isolate();
TryCatch try_catch(reinterpret_cast<v8::Isolate*>(isolate));
MaybeHandle<Object> maybe_result =
tester.CallExportedFunction("disallowed", 0, nullptr);
CHECK(maybe_result.is_null());
CHECK(try_catch.HasCaught());
try_catch.Reset();
isolate->clear_pending_exception();
maybe_result = tester.CallExportedFunction("producer", 0, nullptr);
if (maybe_result.is_null()) {
FATAL("Calling 'producer' failed: %s",
*v8::String::Utf8Value(reinterpret_cast<v8::Isolate*>(isolate),
try_catch.Message()->Get()));
}
{
Handle<Object> args[] = {maybe_result.ToHandleChecked()};
maybe_result = tester.CallExportedFunction("consumer", 1, args);
}
if (maybe_result.is_null()) {
FATAL("Calling 'consumer' failed: %s",
*v8::String::Utf8Value(reinterpret_cast<v8::Isolate*>(isolate),
try_catch.Message()->Get()));
}
Handle<Object> result = maybe_result.ToHandleChecked();
CHECK(result->IsSmi());
CHECK_EQ(42, Smi::cast(*result).value());
// Calling {consumer} with any other object (e.g. the Smi we just got as
// {result}) should trap.
{
Handle<Object> args[] = {result};
maybe_result = tester.CallExportedFunction("consumer", 1, args);
}
CHECK(maybe_result.is_null());
CHECK(try_catch.HasCaught());
try_catch.Reset();
isolate->clear_pending_exception();
}
CHECK(maybe_result.is_null());
CHECK(try_catch.HasCaught());
try_catch.Reset();
isolate->clear_pending_exception();
}
} // namespace test_gc
......
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