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

[wasm-gc] Basic JS interop handling for wasm objects

This change tests all JavaScript language constructs and builtins in
combination with the unwrapped Wasm objects.
For JavaScript, excluding some basic introspection (e.g.
`Object.isExtensible`) WebAssembly GC objects are treated opaque.
They can be passed around freely but don't allow any access to
properties, elements etc.

This behavior is currently exposed only if the `wasm-gc-js-interop`
flag is set.

Bug: v8:7748
Change-Id: If0dc368f99d4097e3eaf53edde4e244e3081e334
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3879616Reviewed-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@{#83299}
parent 253f4004
......@@ -437,6 +437,7 @@ extern enum MessageTemplate {
kWasmTrapArrayOutOfBounds,
kWasmTrapArrayTooLarge,
kWasmTrapStringOffsetOutOfBounds,
kWasmObjectsAreOpaque,
kWeakRefsRegisterTargetAndHoldingsMustNotBeSame,
kWeakRefsRegisterTargetMustBeObject,
kWeakRefsUnregisterTokenMustBeObject,
......
......@@ -161,6 +161,12 @@ ResolvePromise(implicit context: Context)(
}
goto Slow;
} label Slow deferred {
// Skip "then" lookup for Wasm objects as they are opaque.
@if(V8_ENABLE_WEBASSEMBLY)
if (Is<WasmObject>(resolution)) {
return FulfillPromise(promise, resolution);
}
// 9. Let then be Get(resolution, "then").
// 10. If then is an abrupt completion, then
try {
......
......@@ -38,6 +38,13 @@ transitioning javascript builtin ReflectSetPrototypeOf(
const objectJSReceiver = Cast<JSReceiver>(object)
otherwise ThrowTypeError(
MessageTemplate::kCalledOnNonObject, 'Reflect.setPrototypeOf');
// Wasm objects do not support having prototypes.
@if(V8_ENABLE_WEBASSEMBLY)
if (Is<WasmObject>(objectJSReceiver)) {
ThrowTypeError(MessageTemplate::kWasmObjectsAreOpaque);
}
typeswitch (proto) {
case (proto: JSReceiver|Null): {
return object::ObjectSetPrototypeOfDontThrow(objectJSReceiver, proto);
......
......@@ -277,12 +277,10 @@ Type::bitset BitsetType::Lub(const MapRefLike& map) {
case JS_TEMPORAL_TIME_ZONE_TYPE:
case JS_TEMPORAL_ZONED_DATE_TIME_TYPE:
#if V8_ENABLE_WEBASSEMBLY
case WASM_ARRAY_TYPE:
case WASM_GLOBAL_OBJECT_TYPE:
case WASM_INSTANCE_OBJECT_TYPE:
case WASM_MEMORY_OBJECT_TYPE:
case WASM_MODULE_OBJECT_TYPE:
case WASM_STRUCT_TYPE:
case WASM_SUSPENDER_OBJECT_TYPE:
case WASM_TABLE_OBJECT_TYPE:
case WASM_TAG_OBJECT_TYPE:
......@@ -293,6 +291,11 @@ Type::bitset BitsetType::Lub(const MapRefLike& map) {
DCHECK(!map.is_callable());
DCHECK(!map.is_undetectable());
return kOtherObject;
#if V8_ENABLE_WEBASSEMBLY
case WASM_STRUCT_TYPE:
case WASM_ARRAY_TYPE:
return kWasmObject;
#endif // V8_ENABLE_WEBASSEMBLY
case JS_BOUND_FUNCTION_TYPE:
DCHECK(!map.is_undetectable());
return kBoundFunction;
......
......@@ -99,6 +99,15 @@ MaybeHandle<HeapObject> JSReceiver::GetPrototype(Isolate* isolate,
Handle<JSReceiver> receiver) {
// We don't expect access checks to be needed on JSProxy objects.
DCHECK(!receiver->IsAccessCheckNeeded() || receiver->IsJSObject());
#if V8_ENABLE_WEBASSEMBLY
if (receiver->IsWasmObject()) {
THROW_NEW_ERROR(isolate,
NewTypeError(MessageTemplate::kWasmObjectsAreOpaque),
HeapObject);
}
#endif
PrototypeIterator iter(isolate, receiver, kStartAtReceiver,
PrototypeIterator::END_AT_NON_HIDDEN);
do {
......
......@@ -152,7 +152,6 @@ Handle<Object> JSReceiver::GetDataProperty(LookupIterator* it,
case LookupIterator::INTERCEPTOR:
case LookupIterator::NOT_FOUND:
case LookupIterator::TRANSITION:
case LookupIterator::WASM_OBJECT:
UNREACHABLE();
case LookupIterator::ACCESS_CHECK:
// Support calling this method without an active context, but refuse
......@@ -160,6 +159,7 @@ Handle<Object> JSReceiver::GetDataProperty(LookupIterator* it,
if (!it->isolate()->context().is_null() && it->HasAccess()) continue;
V8_FALLTHROUGH;
case LookupIterator::JSPROXY:
case LookupIterator::WASM_OBJECT:
it->NotFound();
return it->isolate()->factory()->undefined_value();
case LookupIterator::ACCESSOR:
......@@ -2312,6 +2312,13 @@ Maybe<bool> JSReceiver::SetPrototype(Isolate* isolate,
Handle<JSReceiver> object,
Handle<Object> value, bool from_javascript,
ShouldThrow should_throw) {
#if V8_ENABLE_WEBASSEMBLY
if (object->IsWasmObject()) {
RETURN_FAILURE(isolate, should_throw,
NewTypeError(MessageTemplate::kWasmObjectsAreOpaque));
}
#endif
if (object->IsJSProxy()) {
return JSProxy::SetPrototype(isolate, Handle<JSProxy>::cast(object), value,
from_javascript, should_throw);
......@@ -5150,6 +5157,13 @@ 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);
......
......@@ -517,6 +517,15 @@ RUNTIME_FUNCTION(Runtime_ObjectCreate) {
THROW_NEW_ERROR_RETURN_FAILURE(
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));
......
......@@ -34,6 +34,7 @@
'harmony/shadowrealm-skip*': [SKIP],
'regress/modules-skip*': [SKIP],
'wasm/exceptions-utils': [SKIP],
'wasm/gc-js-interop-helpers': [SKIP],
'wasm/wasm-module-builder': [SKIP],
'compiler/fast-api-helpers': [SKIP],
'typedarray-helpers': [SKIP],
......
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --experimental-wasm-gc --wasm-gc-js-interop --allow-natives-syntax
d8.file.execute('test/mjsunit/wasm/gc-js-interop-helpers.js');
let {struct, array} = CreateWasmObjects();
for (const wasm_obj of [struct, array]) {
repeated(() => assertThrowsAsync(Promise.all(wasm_obj), TypeError));
repeated(() => Promise.all([wasm_obj]));
repeated(() => assertThrowsAsync(Promise.allSettled(wasm_obj), TypeError));
repeated(
() => Promise.allSettled([wasm_obj])
.then((info) => assertEquals('fulfilled', info[0].status)));
repeated(() => assertThrowsAsync(Promise.any(wasm_obj), TypeError));
repeated(() => Promise.any([wasm_obj]));
repeated(() => assertThrowsAsync(Promise.race(wasm_obj), TypeError));
repeated(() => Promise.race([wasm_obj]));
// Using wasm objects in Promise.resolve and Promise.reject should work as
// for any other object.
repeated(
() => (new Promise((resolve, reject) => resolve(wasm_obj)))
.then((v) => assertSame(wasm_obj, v)));
repeated(
() => (new Promise((resolve, reject) => reject(wasm_obj)))
.then(() => assertUnreachable())
.catch((v) => assertSame(wasm_obj, v)));
// Wasm objects can also be passed as a result in a then chain.
repeated(
() => (new Promise((resolve) => resolve({})))
.then(() => wasm_obj)
.then((v) => assertSame(wasm_obj, v)));
// If the `then` argument isn't a callback, it will simply be replaced with
// an identity function (x) => x.
repeated(
() => (new Promise((resolve) => resolve({})))
.then(wasm_obj) // The value itself doesn't have any impact.
.then((v) => assertEquals({}, v), () => assertUnreachable()));
// If the `catch` argument isn't a callback, it will be replaced with a
// thrower function (x) => { throw x; }.
repeated(
() => (new Promise((resolve, reject) => reject({})))
.then(() => null)
.catch(wasm_obj) // The value itself doesn't have any impact.
.then(() => assertUnreachable(), (v) => assertEquals({}, v)));
// `finally(wasm_obj)` behaves just like `then(wasm_obj, wasm_obj)`
repeated(
() => (new Promise((resolve, reject) => resolve({})))
.finally(wasm_obj)
.then((v) => assertEquals({}, v), () => assertUnreachable()));
repeated(
() => (new Promise((resolve, reject) => reject({})))
.finally(wasm_obj)
.then(() => assertUnreachable(), (v) => assertEquals({}, v)));
// Ensure no statement re-assigned wasm_obj by accident.
assertTrue(wasm_obj == struct || wasm_obj == array);
}
repeated(async function testAsync() {
for (let wasm_obj of [struct, array]) {
let async_wasm_obj = await wasm_obj;
assertSame(wasm_obj, async_wasm_obj);
}
});
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --experimental-wasm-gc --wasm-gc-js-interop --allow-natives-syntax
d8.file.execute('test/mjsunit/wasm/gc-js-interop-helpers.js');
let {struct, array} = CreateWasmObjects();
for (const wasm_obj of [struct, array]) {
// Test Array.
testThrowsRepeated(() => Array.from(wasm_obj), TypeError);
repeated(() => assertFalse(Array.isArray(wasm_obj)));
repeated(() => assertEquals([wasm_obj], Array.of(wasm_obj)));
testThrowsRepeated(() => [1, 2].at(wasm_obj), TypeError);
repeated(() => assertEquals([1, wasm_obj], [1].concat(wasm_obj)));
testThrowsRepeated(() => [1, 2].copyWithin(wasm_obj), TypeError);
testThrowsRepeated(() => [1, 2].every(wasm_obj), TypeError);
repeated(
() => assertEquals([1, wasm_obj, 3], [1, 2, 3].fill(wasm_obj, 1, 2)));
testThrowsRepeated(() => [1, 2].filter(wasm_obj), TypeError);
repeated(
() => assertEquals(
[wasm_obj], [undefined, wasm_obj, null].filter(function(v) {
return v == this;
}, wasm_obj)));
testThrowsRepeated(() => [1, 2].find(wasm_obj), TypeError);
testThrowsRepeated(() => [1, 2].findIndex(wasm_obj), TypeError);
testThrowsRepeated(() => [1, 2].findLast(wasm_obj), TypeError);
testThrowsRepeated(() => [1, 2].findLastIndex(wasm_obj), TypeError);
testThrowsRepeated(() => [1, 2].flat(wasm_obj), TypeError);
testThrowsRepeated(() => [1, 2].flatMap(wasm_obj), TypeError);
testThrowsRepeated(() => [1, 2].forEach(wasm_obj), TypeError);
repeated(() => {
let res = [];
[1, 2].forEach(function(x) {
res.push(this);
}, wasm_obj);
assertEquals([wasm_obj, wasm_obj], res);
});
repeated(() => assertTrue([wasm_obj].includes(wasm_obj)));
repeated(() => assertFalse([1].includes(wasm_obj)));
repeated(() => assertEquals(1, [0, wasm_obj, 2].indexOf(wasm_obj)));
testThrowsRepeated(() => ['a', 'b'].join(wasm_obj), TypeError);
repeated(() => assertEquals(1, [0, wasm_obj, 2].lastIndexOf(wasm_obj)));
testThrowsRepeated(() => [1, 2].map(wasm_obj), TypeError);
repeated(() => assertEquals([wasm_obj, wasm_obj], [1, 2].map(function() {
return this;
}, wasm_obj)));
repeated(() => {
let arr = [1];
arr.push(wasm_obj, 3);
assertEquals([1, wasm_obj, 3], arr);
});
testThrowsRepeated(() => [1, 2].reduce(wasm_obj), TypeError);
repeated(() => assertSame(wasm_obj, [].reduce(() => null, wasm_obj)));
testThrowsRepeated(() => [1, 2].reduceRight(wasm_obj), TypeError);
testThrowsRepeated(() => [1, 2].slice(wasm_obj, 2), TypeError);
testThrowsRepeated(() => [1, 2].some(wasm_obj), TypeError);
testThrowsRepeated(() => [1, 2].sort(wasm_obj), TypeError);
testThrowsRepeated(() => [1, 2].splice(1, wasm_obj), TypeError);
repeated(() => {
let arr = [1, 2];
arr.unshift(wasm_obj);
assertEquals([wasm_obj, 1, 2], arr);
});
testThrowsRepeated(() => Int8Array.from(wasm_obj), TypeError);
testThrowsRepeated(() => Int8Array.of(wasm_obj), TypeError);
for (let ArrayType
of [Int8Array, Int16Array, Int32Array, Uint8Array, Uint16Array,
Uint32Array]) {
let array = ArrayType.of(1, 2, 3);
testThrowsRepeated(() => array.at(wasm_obj), TypeError);
testThrowsRepeated(() => array.copyWithin(wasm_obj), TypeError);
testThrowsRepeated(() => array.fill(wasm_obj, 0, 1), TypeError);
testThrowsRepeated(() => array.filter(wasm_obj), TypeError);
testThrowsRepeated(() => array.find(wasm_obj), TypeError);
testThrowsRepeated(() => array.findIndex(wasm_obj), TypeError);
testThrowsRepeated(() => array.findLast(wasm_obj), TypeError);
testThrowsRepeated(() => array.findLastIndex(wasm_obj), TypeError);
testThrowsRepeated(() => array.forEach(wasm_obj), TypeError);
repeated(() => assertFalse(array.includes(wasm_obj)));
repeated(() => assertEquals(-1, array.indexOf(wasm_obj)));
testThrowsRepeated(() => array.join(wasm_obj), TypeError);
repeated(() => assertEquals(-1, array.lastIndexOf(wasm_obj)));
testThrowsRepeated(() => array.map(wasm_obj), TypeError);
testThrowsRepeated(() => array.map(() => wasm_obj), TypeError);
testThrowsRepeated(() => array.reduce(wasm_obj), TypeError);
testThrowsRepeated(() => array.reduceRight(wasm_obj), TypeError);
testThrowsRepeated(() => array.set(wasm_obj), TypeError);
testThrowsRepeated(() => array.set([wasm_obj]), TypeError);
testThrowsRepeated(() => array.slice(wasm_obj, 1), TypeError);
testThrowsRepeated(() => array.some(wasm_obj), TypeError);
testThrowsRepeated(() => array.sort(wasm_obj), TypeError);
testThrowsRepeated(() => array.subarray(0, wasm_obj), TypeError);
}
// Test Map.
for (let MapType of [Map, WeakMap]) {
repeated(() => {
let val = new String('a');
let map = new MapType([[val, wasm_obj], [wasm_obj, val]]);
assertSame(wasm_obj, map.get(val));
assertEquals(val, map.get(wasm_obj));
assertTrue(map.has(wasm_obj));
map.delete(wasm_obj);
assertFalse(map.has(wasm_obj));
assertThrows(() => map.forEach(wasm_obj), TypeError);
map.set(wasm_obj, wasm_obj);
assertSame(wasm_obj, map.get(wasm_obj));
});
}
// Test Set.
for (let SetType of [Set, WeakSet]) {
repeated(() => {
let set = new SetType([new String('a'), wasm_obj]);
set.add(wasm_obj);
assertTrue(set.has(wasm_obj));
set.delete(wasm_obj);
assertFalse(set.has(wasm_obj));
});
}
// Test ArrayBuffer.
repeated(() => assertFalse(ArrayBuffer.isView(wasm_obj)));
testThrowsRepeated(
() => (new ArrayBuffer(32)).slice(wasm_obj, wasm_obj), TypeError);
testThrowsRepeated(
() => (new SharedArrayBuffer(32)).slice(wasm_obj, wasm_obj), TypeError);
// Test Dataview.
let arrayBuf = new ArrayBuffer(32);
let dataView = new DataView(arrayBuf);
testThrowsRepeated(() => dataView.getBigInt64(wasm_obj), TypeError);
testThrowsRepeated(() => dataView.getBigUint64(wasm_obj), TypeError);
testThrowsRepeated(() => dataView.getFloat32(wasm_obj), TypeError);
testThrowsRepeated(() => dataView.getFloat64(wasm_obj), TypeError);
testThrowsRepeated(() => dataView.getInt8(wasm_obj), TypeError);
testThrowsRepeated(() => dataView.getInt16(wasm_obj), TypeError);
testThrowsRepeated(() => dataView.getInt32(wasm_obj), TypeError);
testThrowsRepeated(() => dataView.getUint8(wasm_obj), TypeError);
testThrowsRepeated(() => dataView.getUint16(wasm_obj), TypeError);
testThrowsRepeated(() => dataView.getUint32(wasm_obj), TypeError);
testThrowsRepeated(() => dataView.setBigInt64(wasm_obj), TypeError);
testThrowsRepeated(() => dataView.setBigUint64(wasm_obj), TypeError);
testThrowsRepeated(() => dataView.setFloat32(wasm_obj), TypeError);
testThrowsRepeated(() => dataView.setFloat64(0, wasm_obj), TypeError);
testThrowsRepeated(() => dataView.setInt8(wasm_obj), TypeError);
testThrowsRepeated(() => dataView.setInt16(0, wasm_obj), TypeError);
testThrowsRepeated(() => dataView.setInt32(wasm_obj), TypeError);
testThrowsRepeated(() => dataView.setUint8(0, wasm_obj), TypeError);
testThrowsRepeated(() => dataView.setUint16(wasm_obj), TypeError);
testThrowsRepeated(() => dataView.setUint32(0, wasm_obj), TypeError);
// Ensure no statement re-assigned wasm_obj by accident.
assertTrue(wasm_obj == struct || wasm_obj == array);
}
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --experimental-wasm-gc --wasm-gc-js-interop --allow-natives-syntax
d8.file.execute('test/mjsunit/wasm/gc-js-interop-helpers.js');
let {struct, array} = CreateWasmObjects();
for (const wasm_obj of [struct, array]) {
// Test constructors of the global object as function.
testThrowsRepeated(() => AggregateError(wasm_obj), TypeError);
repeated(() => assertSame(wasm_obj, Array(wasm_obj)[0]));
testThrowsRepeated(() => ArrayBuffer(wasm_obj), TypeError);
testThrowsRepeated(() => BigInt(wasm_obj), TypeError);
testThrowsRepeated(() => BigInt64Array(wasm_obj), TypeError);
testThrowsRepeated(() => BigUint64Array(wasm_obj), TypeError);
repeated(() => assertEquals(true, Boolean(wasm_obj)));
testThrowsRepeated(() => DataView(wasm_obj), TypeError);
repeated(() => {
let date = Date(wasm_obj);
assertEquals('string', typeof date);
});
testThrowsRepeated(() => Error(wasm_obj), TypeError);
testThrowsRepeated(() => EvalError(wasm_obj), TypeError);
testThrowsRepeated(() => Float64Array(wasm_obj), TypeError);
testThrowsRepeated(() => Function(wasm_obj), TypeError);
testThrowsRepeated(() => Int8Array(wasm_obj), TypeError);
testThrowsRepeated(() => Int16Array(wasm_obj), TypeError);
testThrowsRepeated(() => Int32Array(wasm_obj), TypeError);
testThrowsRepeated(() => Map(wasm_obj), TypeError);
testThrowsRepeated(() => Number(wasm_obj), TypeError);
repeated(() => assertSame(wasm_obj, Object(wasm_obj)));
testThrowsRepeated(() => Promise(wasm_obj), TypeError);
testThrowsRepeated(() => Proxy(wasm_obj), TypeError);
testThrowsRepeated(() => RangeError(wasm_obj), TypeError);
testThrowsRepeated(() => ReferenceError(wasm_obj), TypeError);
testThrowsRepeated(() => RegExp(wasm_obj), TypeError);
testThrowsRepeated(() => Set(wasm_obj), TypeError);
testThrowsRepeated(() => SharedArrayBuffer(wasm_obj), TypeError);
testThrowsRepeated(() => String(wasm_obj), TypeError);
testThrowsRepeated(() => Symbol(wasm_obj), TypeError);
testThrowsRepeated(() => SyntaxError(wasm_obj), TypeError);
testThrowsRepeated(() => TypeError(wasm_obj), TypeError);
testThrowsRepeated(() => Uint8Array(wasm_obj), TypeError);
testThrowsRepeated(() => Uint16Array(wasm_obj), TypeError);
testThrowsRepeated(() => Uint32Array(wasm_obj), TypeError);
testThrowsRepeated(() => URIError(wasm_obj), TypeError);
testThrowsRepeated(() => WeakMap(wasm_obj), TypeError);
testThrowsRepeated(() => WeakRef(wasm_obj), TypeError);
testThrowsRepeated(() => WeakSet(wasm_obj), TypeError);
// Test constructors of the global object with new.
testThrowsRepeated(() => new AggregateError(wasm_obj), TypeError);
repeated(() => assertSame(wasm_obj, new Array(wasm_obj)[0]));
testThrowsRepeated(() => new ArrayBuffer(wasm_obj), TypeError);
testThrowsRepeated(() => new BigInt(wasm_obj), TypeError);
testThrowsRepeated(() => new BigInt64Array(wasm_obj), TypeError);
testThrowsRepeated(() => new BigUint64Array(wasm_obj), TypeError);
repeated(() => assertEquals(true, (new Boolean(wasm_obj)).valueOf()));
testThrowsRepeated(() => new DataView(wasm_obj), TypeError);
testThrowsRepeated(() => new Date(wasm_obj), TypeError);
testThrowsRepeated(() => new Error(wasm_obj), TypeError);
testThrowsRepeated(() => new EvalError(wasm_obj), TypeError);
testThrowsRepeated(() => new Float64Array(wasm_obj), TypeError);
testThrowsRepeated(() => new Function(wasm_obj), TypeError);
testThrowsRepeated(() => new Int8Array(wasm_obj), TypeError);
testThrowsRepeated(() => new Int16Array(wasm_obj), TypeError);
testThrowsRepeated(() => new Int32Array(wasm_obj), TypeError);
testThrowsRepeated(() => new Map(wasm_obj), TypeError);
testThrowsRepeated(() => new Number(wasm_obj), TypeError);
repeated(() => assertSame(wasm_obj, new Object(wasm_obj)));
testThrowsRepeated(() => new Promise(wasm_obj), TypeError);
testThrowsRepeated(() => new Proxy(wasm_obj), TypeError);
testThrowsRepeated(() => new RangeError(wasm_obj), TypeError);
testThrowsRepeated(() => new ReferenceError(wasm_obj), TypeError);
testThrowsRepeated(() => new RegExp(wasm_obj), TypeError);
testThrowsRepeated(() => new Set(wasm_obj), TypeError);
testThrowsRepeated(() => new SharedArrayBuffer(wasm_obj), TypeError);
testThrowsRepeated(() => new String(wasm_obj), TypeError);
testThrowsRepeated(() => new Symbol(wasm_obj), TypeError);
testThrowsRepeated(() => new SyntaxError(wasm_obj), TypeError);
testThrowsRepeated(() => new TypeError(wasm_obj), TypeError);
testThrowsRepeated(() => new Uint8Array(wasm_obj), TypeError);
testThrowsRepeated(() => new Uint16Array(wasm_obj), TypeError);
testThrowsRepeated(() => new Uint32Array(wasm_obj), TypeError);
testThrowsRepeated(() => new URIError(wasm_obj), TypeError);
testThrowsRepeated(() => new WeakMap(wasm_obj), TypeError);
repeated(() => assertSame(wasm_obj, new WeakRef(wasm_obj).deref()));
testThrowsRepeated(() => new WeakSet(wasm_obj), TypeError);
// Ensure no statement re-assigned wasm_obj by accident.
assertTrue(wasm_obj == struct || wasm_obj == array);
}
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Helpers to test interoperability of Wasm objects in JavaScript.
d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
function CreateWasmObjects() {
let builder = new WasmModuleBuilder();
let struct_type = builder.addStruct([makeField(kWasmI32, true)]);
let array_type = builder.addArray(kWasmI32, true);
builder.addFunction('MakeStruct', makeSig([], [kWasmExternRef]))
.exportFunc()
.addBody([
kExprI32Const, 42, // --
kGCPrefix, kExprStructNew, struct_type, // --
kGCPrefix, kExprExternExternalize // --
]);
builder.addFunction('MakeArray', makeSig([], [kWasmExternRef]))
.exportFunc()
.addBody([
kExprI32Const, 2, // length
kGCPrefix, kExprArrayNewDefault, array_type, // --
kGCPrefix, kExprExternExternalize // --
]);
let instance = builder.instantiate();
return {
struct: instance.exports.MakeStruct(),
array: instance.exports.MakeArray(),
};
}
function testThrowsRepeated(fn, ErrorType) {
%PrepareFunctionForOptimization(fn);
for (let i = 0; i < 5; i++) assertThrows(fn, ErrorType);
%OptimizeFunctionOnNextCall(fn);
assertThrows(fn, ErrorType);
// TODO(7748): This assertion doesn't hold true, as some cases run into
// deopt loops.
// assertTrue(%ActiveTierIsTurbofan(fn));
}
function repeated(fn) {
%PrepareFunctionForOptimization(fn);
for (let i = 0; i < 5; i++) fn();
%OptimizeFunctionOnNextCall(fn);
fn();
// TODO(7748): This assertion doesn't hold true, as some cases run into
// deopt loops.
// assertTrue(%ActiveTierIsTurbofan(fn));
}
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --experimental-wasm-gc --wasm-gc-js-interop --allow-natives-syntax
d8.file.execute('test/mjsunit/wasm/gc-js-interop-helpers.js');
let {struct, array} = CreateWasmObjects();
for (const wasm_obj of [struct, array]) {
// Test numeric operators.
testThrowsRepeated(() => ++wasm_obj, TypeError);
testThrowsRepeated(() => wasm_obj--, TypeError);
testThrowsRepeated(() => +wasm_obj, TypeError);
testThrowsRepeated(() => -wasm_obj, TypeError);
testThrowsRepeated(() => ~wasm_obj, TypeError);
testThrowsRepeated(() => wasm_obj - 2, TypeError);
testThrowsRepeated(() => wasm_obj * 2, TypeError);
testThrowsRepeated(() => wasm_obj / 2, TypeError);
testThrowsRepeated(() => wasm_obj ** 2, TypeError);
testThrowsRepeated(() => wasm_obj << 2, TypeError);
testThrowsRepeated(() => wasm_obj >> 2, TypeError);
testThrowsRepeated(() => 2 >>> wasm_obj, TypeError);
testThrowsRepeated(() => 2 % wasm_obj, TypeError);
testThrowsRepeated(() => wasm_obj | 1, TypeError);
testThrowsRepeated(() => 1 & wasm_obj, TypeError);
testThrowsRepeated(() => wasm_obj ^ wasm_obj, TypeError);
testThrowsRepeated(() => wasm_obj += 1, TypeError);
let tmp = 1;
testThrowsRepeated(() => tmp += wasm_obj, TypeError);
testThrowsRepeated(() => tmp <<= wasm_obj, TypeError);
testThrowsRepeated(() => tmp &= wasm_obj, TypeError);
testThrowsRepeated(() => tmp **= wasm_obj, TypeError);
// Test numeric functions of the global object.
testThrowsRepeated(() => isFinite(wasm_obj), TypeError);
testThrowsRepeated(() => isNaN(wasm_obj), TypeError);
testThrowsRepeated(() => parseFloat(wasm_obj), TypeError);
testThrowsRepeated(() => parseInt(wasm_obj), TypeError);
// Test Number.
repeated(() => assertFalse(Number.isFinite(wasm_obj)));
repeated(() => assertFalse(Number.isInteger(wasm_obj)));
repeated(() => assertFalse(Number.isNaN(wasm_obj)));
repeated(() => assertFalse(Number.isSafeInteger(wasm_obj)));
testThrowsRepeated(() => Number.parseFloat(wasm_obj), TypeError);
testThrowsRepeated(() => Number.parseInt(wasm_obj), TypeError);
// Test BigInt.
testThrowsRepeated(() => BigInt.asIntN(2, wasm_obj), TypeError);
testThrowsRepeated(
() => BigInt.asUintN(wasm_obj, 123n), TypeError);
// Test Math.
testThrowsRepeated(() => Math.abs(wasm_obj), TypeError);
testThrowsRepeated(() => Math.acos(wasm_obj), TypeError);
testThrowsRepeated(() => Math.acosh(wasm_obj), TypeError);
testThrowsRepeated(() => Math.asin(wasm_obj), TypeError);
testThrowsRepeated(() => Math.asinh(wasm_obj), TypeError);
testThrowsRepeated(() => Math.atan(wasm_obj), TypeError);
testThrowsRepeated(() => Math.atanh(wasm_obj), TypeError);
testThrowsRepeated(() => Math.atan2(wasm_obj), TypeError);
testThrowsRepeated(() => Math.cbrt(wasm_obj), TypeError);
testThrowsRepeated(() => Math.ceil(wasm_obj), TypeError);
testThrowsRepeated(() => Math.clz32(wasm_obj), TypeError);
testThrowsRepeated(() => Math.cos(wasm_obj), TypeError);
testThrowsRepeated(() => Math.cosh(wasm_obj), TypeError);
testThrowsRepeated(() => Math.exp(wasm_obj), TypeError);
testThrowsRepeated(() => Math.expm1(wasm_obj), TypeError);
testThrowsRepeated(() => Math.floor(wasm_obj), TypeError);
testThrowsRepeated(() => Math.fround(wasm_obj), TypeError);
testThrowsRepeated(() => Math.hypot(wasm_obj), TypeError);
testThrowsRepeated(() => Math.imul(wasm_obj, wasm_obj), TypeError);
testThrowsRepeated(() => Math.log(wasm_obj), TypeError);
testThrowsRepeated(() => Math.log1p(wasm_obj), TypeError);
testThrowsRepeated(() => Math.log10(wasm_obj), TypeError);
testThrowsRepeated(() => Math.log2(wasm_obj), TypeError);
testThrowsRepeated(() => Math.max(2, wasm_obj), TypeError);
testThrowsRepeated(() => Math.min(2, wasm_obj), TypeError);
testThrowsRepeated(() => Math.pow(2, wasm_obj), TypeError);
testThrowsRepeated(() => Math.pow(wasm_obj, 2), TypeError);
testThrowsRepeated(() => Math.round(wasm_obj), TypeError);
testThrowsRepeated(() => Math.sign(wasm_obj), TypeError);
testThrowsRepeated(() => Math.sin(wasm_obj), TypeError);
testThrowsRepeated(() => Math.sinh(wasm_obj), TypeError);
testThrowsRepeated(() => Math.sqrt(wasm_obj), TypeError);
testThrowsRepeated(() => Math.tan(wasm_obj), TypeError);
testThrowsRepeated(() => Math.tanh(wasm_obj), TypeError);
testThrowsRepeated(() => Math.trunc(wasm_obj), TypeError);
// Test boolean.
repeated(() => assertFalse(!wasm_obj));
repeated(() => assertTrue(wasm_obj ? true : false));
tmp = true;
repeated(() => assertSame(wasm_obj, tmp &&= wasm_obj));
tmp = 0;
repeated(() => assertSame(wasm_obj, tmp ||= wasm_obj));
tmp = null;
repeated(() => assertSame(wasm_obj, tmp ??= wasm_obj));
// Ensure no statement re-assigned wasm_obj by accident.
assertTrue(wasm_obj == struct || wasm_obj == array);
}
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --experimental-wasm-gc --wasm-gc-js-interop --allow-natives-syntax
d8.file.execute('test/mjsunit/wasm/gc-js-interop-helpers.js');
let {struct, array} = CreateWasmObjects();
for (const wasm_obj of [struct, array]) {
// Test Object.
testThrowsRepeated(() => Object.freeze(wasm_obj), TypeError);
testThrowsRepeated(() => Object.seal(wasm_obj), TypeError);
testThrowsRepeated(
() => Object.prototype.__lookupGetter__.call(wasm_obj, 'foo'), TypeError);
testThrowsRepeated(
() => Object.prototype.__lookupSetter__.call(wasm_obj, 'foo'), TypeError);
testThrowsRepeated(
() => Object.prototype.__defineGetter__.call(wasm_obj, 'foo', () => 42),
TypeError);
testThrowsRepeated(
() => Object.prototype.__defineSetter__.call(wasm_obj, 'foo', () => {}),
TypeError);
testThrowsRepeated(
() => Object.defineProperty(wasm_obj, 'foo', {value: 42}), TypeError);
repeated(() => assertEquals([], Object.getOwnPropertyNames(wasm_obj)));
repeated(() => assertEquals([], Object.getOwnPropertySymbols(wasm_obj)));
repeated(() => assertEquals({}, Object.getOwnPropertyDescriptors(wasm_obj)));
repeated(() => assertEquals([], Object.keys(wasm_obj)));
repeated(() => assertEquals([], Object.entries(wasm_obj)));
repeated(
() => assertEquals(
undefined, Object.getOwnPropertyDescriptor(wasm_obj, 'foo')));
repeated(() => assertEquals(false, 'foo' in wasm_obj));
repeated(
() => assertEquals(
false, Object.prototype.hasOwnProperty.call(wasm_obj, 'foo')));
repeated(() => assertEquals(true, Object.isSealed(wasm_obj)));
repeated(() => assertEquals(true, Object.isFrozen(wasm_obj)));
repeated(() => assertEquals(false, Object.isExtensible(wasm_obj)));
repeated(() => assertEquals('object', typeof wasm_obj));
repeated(
() => assertEquals(
'[object Object]', Object.prototype.toString.call(wasm_obj)));
repeated(() => {
let tgt = {};
Object.assign(tgt, wasm_obj);
assertEquals({}, tgt);
});
testThrowsRepeated(() => Object.create(wasm_obj), TypeError);
testThrowsRepeated(() => ({}).__proto__ = wasm_obj, TypeError);
testThrowsRepeated(
() => Object.defineProperties(wasm_obj, {prop: {value: 1}}), TypeError);
testThrowsRepeated(
() => Object.defineProperty(wasm_obj, 'prop', {value: 1}), TypeError);
testThrowsRepeated(() => Object.fromEntries(wasm_obj), TypeError);
testThrowsRepeated(() => Object.getPrototypeOf(wasm_obj), TypeError);
repeated(() => assertFalse(Object.hasOwn(wasm_obj, 'test')));
testThrowsRepeated(() => Object.preventExtensions(wasm_obj), TypeError);
testThrowsRepeated(() => Object.setPrototypeOf(wasm_obj, Object), TypeError);
repeated(() => assertEquals([], Object.values(wasm_obj)));
testThrowsRepeated(() => wasm_obj.toString(), TypeError);
// Test Reflect.
{
let fct = function(x) {
return [this, x]
};
repeated(
() => assertEquals([wasm_obj, 1], Reflect.apply(fct, wasm_obj, [1])));
repeated(
() => assertEquals([{}, wasm_obj], Reflect.apply(fct, {}, [wasm_obj])));
testThrowsRepeated(() => Reflect.apply(fct, 1, wasm_obj), TypeError);
testThrowsRepeated(() => Reflect.apply(wasm_obj, null, []), TypeError);
}
testThrowsRepeated(() => Reflect.construct(wasm_obj, []), TypeError);
testThrowsRepeated(() => Reflect.construct(Object, wasm_obj), TypeError);
testThrowsRepeated(() => Reflect.construct(Object, [], wasm_obj), TypeError);
testThrowsRepeated(
() => Reflect.defineProperty(wasm_obj, 'prop', {value: 1}), TypeError);
testThrowsRepeated(
() => Reflect.defineProperty({}, wasm_obj, {value: 1}), TypeError);
// Reflect.defineProperty performs ToPropertyDescriptor on the third
// argument which checks whether {value} etc. exist before accessing them.
// Therefore it does not throw but add the property with value undefined.
repeated(() => {
let obj = {};
assertTrue(Reflect.defineProperty(obj, 'prop', wasm_obj));
assertTrue(obj.hasOwnProperty('prop'));
assertEquals(undefined, obj.prop);
});
repeated(() => {
let obj = {};
assertTrue(Reflect.defineProperty(obj, 'prop2', {value: wasm_obj}));
assertSame(wasm_obj, obj.prop2);
});
testThrowsRepeated(() => Reflect.deleteProperty(wasm_obj, 'prop'), TypeError);
testThrowsRepeated(() => Reflect.deleteProperty({}, wasm_obj), TypeError);
testThrowsRepeated(() => Reflect.get(wasm_obj, 'prop'), TypeError);
testThrowsRepeated(() => Reflect.getPrototypeOf(wasm_obj), TypeError);
repeated(() => assertFalse(Reflect.has(wasm_obj, 'prop')));
repeated(() => assertTrue(Reflect.has({wasm_obj}, 'wasm_obj')));
repeated(() => assertFalse(Reflect.isExtensible(wasm_obj)));
repeated(() => assertEquals([], Reflect.ownKeys(wasm_obj)));
testThrowsRepeated(() => Reflect.preventExtensions(wasm_obj), TypeError);
testThrowsRepeated(() => Reflect.set(wasm_obj, 'prop', 123), TypeError);
testThrowsRepeated(
() => Reflect.setPrototypeOf(wasm_obj, Object.prototype), TypeError);
// Test Proxy.
{
const handler = {
get(target, prop, receiver) {
return 'proxied';
}
};
let proxy = new Proxy(wasm_obj, handler);
repeated(() => assertEquals('proxied', proxy.abc));
testThrowsRepeated(() => proxy.abc = 123, TypeError);
}
{
let proxy = new Proxy({}, wasm_obj);
testThrowsRepeated(() => proxy.abc, TypeError);
}
{
const handler = {
get(target, prop, receiver) {
return 'proxied';
}
};
let {proxy, revoke} = Proxy.revocable(wasm_obj, handler);
repeated(() => assertEquals('proxied', proxy.abc));
testThrowsRepeated(() => proxy.abc = 123, TypeError);
revoke();
testThrowsRepeated(() => proxy.abc, TypeError);
}
{
let proxy = Proxy.revocable({}, wasm_obj).proxy;
testThrowsRepeated(() => proxy.abc, TypeError);
}
// Ensure no statement re-assigned wasm_obj by accident.
assertTrue(wasm_obj == struct || wasm_obj == array);
}
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --experimental-wasm-gc --wasm-gc-js-interop --wasm-test-streaming
// Flags: --allow-natives-syntax
d8.file.execute('test/mjsunit/wasm/gc-js-interop-helpers.js');
let {struct, array} = CreateWasmObjects();
for (const wasm_obj of [struct, array]) {
testThrowsRepeated(() => new WebAssembly.Global(wasm_obj), TypeError);
testThrowsRepeated(
() => new WebAssembly.Global({value: wasm_obj}), TypeError);
testThrowsRepeated(
() => new WebAssembly.Global({value: 'i32'}, wasm_obj), TypeError);
repeated(
() => assertSame(
wasm_obj,
(new WebAssembly.Global({value: 'anyref'}, wasm_obj)).value));
testThrowsRepeated(() => new WebAssembly.Module(wasm_obj), TypeError);
let module = () => {
let buffer = (new Uint8Array((new WasmModuleBuilder()).toArray())).buffer;
return new WebAssembly.Module(buffer);
};
testThrowsRepeated(
() => WebAssembly.Module.customSections(wasm_obj), TypeError);
testThrowsRepeated(
() => WebAssembly.Module.customSections(module, wasm_obj), TypeError);
testThrowsRepeated(() => WebAssembly.Module.exports(wasm_obj), TypeError);
testThrowsRepeated(() => WebAssembly.Module.imports(wasm_obj), TypeError);
testThrowsRepeated(() => new WebAssembly.Instance(wasm_obj), TypeError);
testThrowsRepeated(
() => new WebAssembly.Instance(module, wasm_obj), TypeError);
repeated(() => assertThrowsAsync(WebAssembly.compile(wasm_obj), TypeError));
repeated(
() =>
assertThrowsAsync(WebAssembly.compileStreaming(wasm_obj), TypeError));
repeated(
() => assertThrowsAsync(WebAssembly.instantiate(wasm_obj), TypeError));
repeated(
() => assertThrowsAsync(
WebAssembly.instantiateStreaming(wasm_obj), TypeError));
testThrowsRepeated(() => WebAssembly.validate(wasm_obj), TypeError);
testThrowsRepeated(() => new WebAssembly.Memory(wasm_obj), TypeError);
testThrowsRepeated(
() => new WebAssembly.Memory({initial: wasm_obj}), TypeError);
testThrowsRepeated(
() => new WebAssembly.Memory({initial: 1, shared: wasm_obj}), TypeError);
let memory = new WebAssembly.Memory({initial: 1});
testThrowsRepeated(() => memory.grow(wasm_obj), TypeError);
testThrowsRepeated(() => new WebAssembly.Table(wasm_obj), TypeError);
testThrowsRepeated(
() => new WebAssembly.Table({element: wasm_obj, initial: wasm_obj}),
TypeError);
let table = new WebAssembly.Table({initial: 1, element: 'externref'});
testThrowsRepeated(() => table.get(wasm_obj), TypeError);
testThrowsRepeated(() => table.grow(wasm_obj), TypeError);
testThrowsRepeated(() => table.set(wasm_obj, null), TypeError);
repeated(() => table.set(0, wasm_obj));
testThrowsRepeated(() => new WebAssembly.Tag(wasm_obj), TypeError);
testThrowsRepeated(
() => new WebAssembly.Tag({parameters: wasm_obj}), TypeError);
testThrowsRepeated(
() => new WebAssembly.Tag({parameters: [wasm_obj]}), TypeError);
let tag = new WebAssembly.Tag({parameters: ['dataref']});
testThrowsRepeated(() => new WebAssembly.Exception(wasm_obj), TypeError);
testThrowsRepeated(() => new WebAssembly.Exception(tag, wasm_obj), TypeError);
repeated(() => new WebAssembly.Exception(tag, [wasm_obj]));
let exception = new WebAssembly.Exception(tag, [wasm_obj]);
testThrowsRepeated(() => exception.is(wasm_obj), TypeError);
testThrowsRepeated(() => exception.getArg(wasm_obj), TypeError);
testThrowsRepeated(() => exception.getArg(tag, wasm_obj), TypeError);
testThrowsRepeated(() => new WebAssembly.CompileError(wasm_obj), TypeError);
testThrowsRepeated(() => new WebAssembly.LinkError(wasm_obj), TypeError);
testThrowsRepeated(() => new WebAssembly.RuntimeError(wasm_obj), TypeError);
// Ensure no statement re-assigned wasm_obj by accident.
assertTrue(wasm_obj == struct || wasm_obj == array);
}
This diff is collapsed.
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