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

[wasm-gc] JS interop: Allow wasm objects in prototype chain

This change allows to put Wasm structs / arrays into prototype
chains. While this isn't particularly useful (as any access lookup
on the wasm object will throw a TypeError), there isn't any reason
not to allow it.

Bug: v8:7748
Change-Id: I81cf709d2e8403b545bbba9ad9c538c1e9748c74
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3901979Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Reviewed-by: 's avatarNico Hartmann <nicohartmann@chromium.org>
Commit-Queue: Matthias Liedtke <mliedtke@chromium.org>
Cr-Commit-Position: refs/heads/main@{#83329}
parent 3f998527
......@@ -870,7 +870,11 @@ PropertyAccessInfo AccessInfoFactory::ComputePropertyAccessInfo(
if (!map_prototype_map.object()->IsJSObjectMap()) {
// Don't allow proxies on the prototype chain.
if (!prototype.IsNull()) {
DCHECK(prototype.object()->IsJSProxy());
DCHECK(prototype.object()->IsJSProxy()
#if V8_ENABLE_WEBASSEMBLY
|| prototype.object()->IsWasmObject()
#endif
); // NOLINT
return Invalid();
}
......
......@@ -1535,9 +1535,10 @@ bool StoreIC::LookupForWrite(LookupIterator* it, Handle<Object> value,
if (it->state() != LookupIterator::TRANSITION) {
for (; it->IsFound(); it->Next()) {
switch (it->state()) {
case LookupIterator::WASM_OBJECT:
return false;
case LookupIterator::NOT_FOUND:
case LookupIterator::TRANSITION:
case LookupIterator::WASM_OBJECT:
UNREACHABLE();
case LookupIterator::JSPROXY:
return true;
......
......@@ -5157,13 +5157,6 @@ Maybe<bool> JSObject::SetPrototype(Isolate* isolate, Handle<JSObject> object,
}
}
#if V8_ENABLE_WEBASSEMBLY
if (value->IsWasmObject()) {
RETURN_FAILURE(isolate, should_throw,
NewTypeError(MessageTemplate::kWasmObjectsAreOpaque));
}
#endif
// Set the new prototype of the object.
isolate->UpdateNoElementsProtectorOnSetPrototype(real_receiver);
......
......@@ -53,6 +53,9 @@ RELEASE_ACQUIRE_WEAK_ACCESSORS(Map, raw_transitions,
ACCESSORS_CHECKED2(Map, prototype, HeapObject, kPrototypeOffset, true,
value.IsNull() || value.IsJSProxy() ||
#if V8_ENABLE_WEBASSEMBLY
value.IsWasmObject() ||
#endif
(value.IsJSObject() && value.map().is_prototype_map()))
DEF_GETTER(Map, prototype_info, Object) {
......
......@@ -2296,7 +2296,11 @@ void Map::SetPrototype(Isolate* isolate, Handle<Map> map,
Handle<JSObject> prototype_jsobj = Handle<JSObject>::cast(prototype);
JSObject::OptimizeAsPrototype(prototype_jsobj, enable_prototype_setup_mode);
} else {
DCHECK(prototype->IsNull(isolate) || prototype->IsJSProxy());
DCHECK(prototype->IsNull(isolate) || prototype->IsJSProxy()
#if V8_ENABLE_WEBASSEMBLY
|| prototype->IsWasmObject()
#endif
);
}
WriteBarrierMode wb_mode =
......
......@@ -518,14 +518,6 @@ RUNTIME_FUNCTION(Runtime_ObjectCreate) {
isolate, NewTypeError(MessageTemplate::kProtoObjectOrNull, prototype));
}
// Wasm objects may not be used as prototypes.
#if V8_ENABLE_WEBASSEMBLY
if (prototype->IsWasmObject()) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kWasmObjectsAreOpaque));
}
#endif
// 2. Let obj be ObjectCreate(O).
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, obj, JSObject::ObjectCreate(isolate, prototype));
......
......@@ -50,8 +50,8 @@ for (const wasm_obj of [struct, array]) {
Object.assign(tgt, wasm_obj);
assertEquals({}, tgt);
});
testThrowsRepeated(() => Object.create(wasm_obj), TypeError);
testThrowsRepeated(() => ({}).__proto__ = wasm_obj, TypeError);
repeated(() => Object.create(wasm_obj));
repeated(() => ({}).__proto__ = wasm_obj);
testThrowsRepeated(
() => Object.defineProperties(wasm_obj, {prop: {value: 1}}), TypeError);
testThrowsRepeated(
......@@ -64,6 +64,32 @@ for (const wasm_obj of [struct, array]) {
repeated(() => assertEquals([], Object.values(wasm_obj)));
testThrowsRepeated(() => wasm_obj.toString(), TypeError);
// Test prototype chain containing a wasm object.
{
let obj = Object.create(wasm_obj);
repeated(() => assertSame(wasm_obj, Object.getPrototypeOf(obj)));
repeated(() => assertSame(wasm_obj, Reflect.getPrototypeOf(obj)));
testThrowsRepeated(() => obj.__proto__, TypeError);
testThrowsRepeated(() => obj.__proto__ = wasm_obj, TypeError);
// Property access fails.
testThrowsRepeated(() => obj[0], TypeError);
testThrowsRepeated(() => obj.prop, TypeError);
testThrowsRepeated(() => obj.toString(), TypeError);
// Most conversions fail as it will use .toString(), .valueOf(), ...
testThrowsRepeated(() => `${obj}`, TypeError);
testThrowsRepeated(() => obj + 1, TypeError);
repeated(() => assertTrue(!!obj));
}
repeated(() => {
let obj = {};
Object.setPrototypeOf(obj, wasm_obj);
assertSame(wasm_obj, Object.getPrototypeOf(obj));
Object.setPrototypeOf(obj, null);
assertSame(null, Object.getPrototypeOf(obj));
Reflect.setPrototypeOf(obj, wasm_obj);
assertSame(wasm_obj, Reflect.getPrototypeOf(obj));
})
// Test Reflect.
{
let fct = function(x) {
......@@ -111,6 +137,7 @@ for (const wasm_obj of [struct, array]) {
testThrowsRepeated(() => Reflect.set(wasm_obj, 'prop', 123), TypeError);
testThrowsRepeated(
() => Reflect.setPrototypeOf(wasm_obj, Object.prototype), TypeError);
repeated(() => Reflect.setPrototypeOf({}, wasm_obj));
// Test Proxy.
{
......
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