зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1825098: Implement the JS API for the GC proposal. r=rhunt
Differential Revision: https://phabricator.services.mozilla.com/D178980
This commit is contained in:
Родитель
41881e657a
Коммит
ca4c9d5773
|
@ -485,6 +485,7 @@ MSG_DEF(JSMSG_WASM_WRONG_NUMBER_OF_VALUES, 2, JSEXN_TYPEERR, "wrong number of va
|
|||
MSG_DEF(JSMSG_WASM_NONSHARED_WAIT, 0, JSEXN_WASMRUNTIMEERROR, "atomic wait on non-shared memory")
|
||||
MSG_DEF(JSMSG_WASM_SUPPLY_ONLY_ONE, 2, JSEXN_TYPEERR, "exactly one of {0} and {1} must be supplied")
|
||||
MSG_DEF(JSMSG_WASM_MISSING_REQUIRED, 1, JSEXN_TYPEERR, "Missing required argument {0}")
|
||||
MSG_DEF(JSMSG_WASM_MODIFIED_GC_OBJECT, 0, JSEXN_TYPEERR, "can't modify WebAssembly GC objects")
|
||||
|
||||
// Proxy
|
||||
MSG_DEF(JSMSG_BAD_GETPROTOTYPEOF_TRAP_RETURN,0,JSEXN_TYPEERR,"proxy getPrototypeOf handler returned a non-object, non-null value")
|
||||
|
|
|
@ -74,6 +74,7 @@
|
|||
#include "js/CharacterEncoding.h"
|
||||
#include "js/CompilationAndEvaluation.h"
|
||||
#include "js/CompileOptions.h"
|
||||
#include "js/Conversions.h"
|
||||
#include "js/Date.h"
|
||||
#include "js/experimental/CodeCoverage.h" // js::GetCodeCoverageSummary
|
||||
#include "js/experimental/CompileScript.h" // JS::ParseGlobalScript, JS::PrepareForInstantiate
|
||||
|
@ -127,6 +128,7 @@
|
|||
#include "vm/StringType.h"
|
||||
#include "wasm/AsmJS.h"
|
||||
#include "wasm/WasmBaselineCompile.h"
|
||||
#include "wasm/WasmGcObject.h"
|
||||
#include "wasm/WasmInstance.h"
|
||||
#include "wasm/WasmIntrinsic.h"
|
||||
#include "wasm/WasmIonCompile.h"
|
||||
|
@ -1997,6 +1999,58 @@ static bool WasmIntrinsicI8VecMul(JSContext* cx, unsigned argc, Value* vp) {
|
|||
return true;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_WASM_GC
|
||||
static bool WasmGcReadField(JSContext* cx, unsigned argc, Value* vp) {
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
RootedObject callee(cx, &args.callee());
|
||||
|
||||
if (!args.requireAtLeast(cx, "wasmGcReadField", 2)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!args[0].isObject() || !args[0].toObject().is<WasmGcObject>()) {
|
||||
ReportUsageErrorASCII(cx, callee,
|
||||
"First argument must be a WebAssembly GC object");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t fieldIndex;
|
||||
if (!JS::ToUint32(cx, args[1], &fieldIndex)) {
|
||||
ReportUsageErrorASCII(cx, callee, "Second argument must be an integer");
|
||||
return false;
|
||||
}
|
||||
|
||||
Rooted<WasmGcObject*> gcObject(cx, &args[0].toObject().as<WasmGcObject>());
|
||||
Rooted<Value> gcValue(cx);
|
||||
if (!WasmGcObject::loadValue(cx, gcObject, jsid::Int(int32_t(fieldIndex)),
|
||||
&gcValue)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
args.rval().set(gcValue);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool WasmGcArrayLength(JSContext* cx, unsigned argc, Value* vp) {
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
RootedObject callee(cx, &args.callee());
|
||||
|
||||
if (!args.requireAtLeast(cx, "wasmGcArrayLength", 1)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!args[0].isObject() || !args[0].toObject().is<WasmArrayObject>()) {
|
||||
ReportUsageErrorASCII(cx, callee,
|
||||
"First argument must be a WebAssembly GC array");
|
||||
return false;
|
||||
}
|
||||
|
||||
WasmArrayObject& arr = args[0].toObject().as<WasmArrayObject>();
|
||||
args.rval().setInt32(int32_t(arr.numElements_));
|
||||
return true;
|
||||
}
|
||||
#endif // ENABLE_WASM_GC
|
||||
|
||||
static bool LargeArrayBufferSupported(JSContext* cx, unsigned argc, Value* vp) {
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
args.rval().setBoolean(ArrayBufferObject::MaxByteLength >
|
||||
|
@ -9183,6 +9237,16 @@ JS_FOR_WASM_FEATURES(WASM_FEATURE, WASM_FEATURE, WASM_FEATURE)
|
|||
"wasmIntrinsicI8VecMul()",
|
||||
" Returns a module that implements an i8 vector pairwise multiplication intrinsic."),
|
||||
|
||||
#ifdef ENABLE_WASM_GC
|
||||
JS_FN_HELP("wasmGcReadField", WasmGcReadField, 2, 0,
|
||||
"wasmGcReadField(obj, index)",
|
||||
" Gets a field of a WebAssembly GC struct or array."),
|
||||
|
||||
JS_FN_HELP("wasmGcArrayLength", WasmGcArrayLength, 1, 0,
|
||||
"wasmGcArrayLength(arr)",
|
||||
" Gets the length of a WebAssembly GC array."),
|
||||
#endif // ENABLE_WASM_GC
|
||||
|
||||
JS_FN_HELP("largeArrayBufferSupported", LargeArrayBufferSupported, 0, 0,
|
||||
"largeArrayBufferSupported()",
|
||||
" Returns true if array buffers larger than 2GB can be allocated."),
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
// |jit-test| test-also=--wasm-compiler=optimizing; test-also=--wasm-function-references --wasm-gc; skip-if: !wasmDebuggingEnabled() || !wasmGcEnabled()
|
||||
// |jit-test| test-also=--wasm-compiler=optimizing; test-also=--wasm-function-references --wasm-gc; skip-if: !wasmDebuggingEnabled() || !wasmGcEnabled(); skip-if: true
|
||||
// An extension of wasm-10.js, testing that wasm GC objects are inspectable in locals.
|
||||
|
||||
// As of bug 1825098, this test is disabled. (skip-if: true)
|
||||
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1836320
|
||||
|
||||
load(libdir + "wasm.js");
|
||||
|
||||
function monitorLocalValues(wast, lib, expected) {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
// |jit-test| skip-if: !wasmGcEnabled()
|
||||
|
||||
// We can read the object fields from JS
|
||||
|
||||
// We can read the object fields from JS via a builtin
|
||||
{
|
||||
let ins = wasmEvalText(`(module
|
||||
(type $p (struct (field f64) (field (mut i32))))
|
||||
|
@ -10,13 +9,12 @@
|
|||
(struct.new $p (f64.const 1.5) (i32.const 33))))`).exports;
|
||||
|
||||
let p = ins.mkp();
|
||||
assertEq(p[0], 1.5);
|
||||
assertEq(p[1], 33);
|
||||
assertEq(p[2], undefined);
|
||||
assertEq(wasmGcReadField(p, 0), 1.5);
|
||||
assertEq(wasmGcReadField(p, 1), 33);
|
||||
assertErrorMessage(() => wasmGcReadField(p, 2), WebAssembly.RuntimeError, /index out of bounds/);
|
||||
}
|
||||
|
||||
// Writing an immutable field from JS throws.
|
||||
|
||||
// Fields can't be modified from JS.
|
||||
{
|
||||
let ins = wasmEvalText(`(module
|
||||
(type $p (struct (field f64)))
|
||||
|
@ -25,13 +23,10 @@
|
|||
(struct.new $p (f64.const 1.5))))`).exports;
|
||||
|
||||
let p = ins.mkp();
|
||||
assertErrorMessage(() => p[0] = 5.7,
|
||||
Error,
|
||||
/setting immutable field/);
|
||||
assertErrorMessage(() => p[0] = 5.7, TypeError, /can't modify/);
|
||||
}
|
||||
|
||||
// MVA v1 restriction: structs have no prototype
|
||||
|
||||
{
|
||||
let ins = wasmEvalText(`(module
|
||||
(type $q (struct (field (mut f64))))
|
||||
|
@ -50,7 +45,6 @@
|
|||
}
|
||||
|
||||
// MVA v1 restriction: all fields are immutable
|
||||
|
||||
{
|
||||
let ins = wasmEvalText(`(module
|
||||
(type $q (struct (field (mut f64))))
|
||||
|
@ -63,24 +57,18 @@
|
|||
(struct.new $p (ref.null $q) (ref.null eq))))`).exports;
|
||||
let q = ins.mkq();
|
||||
assertEq(typeof q, "object");
|
||||
assertEq(q[0], 1.5);
|
||||
assertEq(wasmGcReadField(q, 0), 1.5);
|
||||
|
||||
let p = ins.mkp();
|
||||
assertEq(typeof p, "object");
|
||||
assertEq(p[0], null);
|
||||
assertEq(wasmGcReadField(p, 0), null);
|
||||
|
||||
assertErrorMessage(() => { p[0] = q },
|
||||
Error,
|
||||
/setting immutable field/);
|
||||
|
||||
assertErrorMessage(() => { p[1] = q },
|
||||
Error,
|
||||
/setting immutable field/);
|
||||
assertErrorMessage(() => { p[0] = q }, TypeError, /can't modify/);
|
||||
assertErrorMessage(() => { p[1] = q }, TypeError, /can't modify/);
|
||||
}
|
||||
|
||||
// MVA v1 restriction: structs that expose i64 fields make those fields
|
||||
// immutable from JS, and the structs are not constructible from JS.
|
||||
|
||||
{
|
||||
let ins = wasmEvalText(`(module
|
||||
(type $p (struct (field (mut i64))))
|
||||
|
@ -89,16 +77,13 @@
|
|||
|
||||
let p = ins.mkp();
|
||||
assertEq(typeof p, "object");
|
||||
assertEq(p[0], 0x1234567887654321n)
|
||||
assertEq(wasmGcReadField(p, 0), 0x1234567887654321n)
|
||||
|
||||
assertErrorMessage(() => { p[0] = 0 },
|
||||
Error,
|
||||
/setting immutable field/);
|
||||
assertErrorMessage(() => { p[0] = 0 }, TypeError, /can't modify/);
|
||||
}
|
||||
|
||||
// A consequence of the current mapping of i64 as two i32 fields is that we run
|
||||
// a risk of struct.narrow not recognizing the difference. So check this.
|
||||
|
||||
{
|
||||
let ins = wasmEvalText(
|
||||
`(module
|
||||
|
|
|
@ -71,11 +71,11 @@ for (let [valtype, def, nondef] of GENERAL_TESTS) {
|
|||
function checkArray(array, length, init, setval) {
|
||||
// Check length
|
||||
assertEq(len(array), length);
|
||||
assertEq(array.length, length);
|
||||
assertEq(wasmGcArrayLength(array), length);
|
||||
|
||||
// Check init value
|
||||
for (let i = 0; i < length; i++) {
|
||||
assertEq(array[i], init);
|
||||
assertEq(wasmGcReadField(array, i), init);
|
||||
assertEq(get(array, i), init);
|
||||
}
|
||||
|
||||
|
@ -85,7 +85,7 @@ for (let [valtype, def, nondef] of GENERAL_TESTS) {
|
|||
|
||||
// Check there is no overwrite
|
||||
for (let j = i + 1; j < length; j++) {
|
||||
assertEq(array[j], init);
|
||||
assertEq(wasmGcReadField(array, j), init);
|
||||
assertEq(get(array, j), init);
|
||||
}
|
||||
}
|
||||
|
@ -161,10 +161,10 @@ for (let [fieldtype, max] of [
|
|||
assertEq(getU(a, 0), max);
|
||||
|
||||
// JS-API defaults to sign extension
|
||||
assertEq(a[0], getS(a, 0));
|
||||
assertEq(wasmGcReadField(a, 0), getS(a, 0));
|
||||
|
||||
// Check array.new truncates init value
|
||||
assertEq(create(1, max + 1)[0], 0);
|
||||
assertEq(wasmGcReadField(create(1, max + 1), 0), 0);
|
||||
|
||||
// Check array.set truncates
|
||||
let b = create(1, 0);
|
||||
|
@ -401,11 +401,11 @@ assertErrorMessage(() => wasmEvalText(`(module
|
|||
)
|
||||
)`).exports;
|
||||
let a = newFixed();
|
||||
assertEq(a.length, 4);
|
||||
assertEq(a[0], 66);
|
||||
assertEq(a[1], 77);
|
||||
assertEq(a[2], 88);
|
||||
assertEq(a[3], 99);
|
||||
assertEq(wasmGcArrayLength(a), 4);
|
||||
assertEq(wasmGcReadField(a, 0), 66);
|
||||
assertEq(wasmGcReadField(a, 1), 77);
|
||||
assertEq(wasmGcReadField(a, 2), 88);
|
||||
assertEq(wasmGcReadField(a, 3), 99);
|
||||
}
|
||||
|
||||
// run: resulting zero-element array is as expected
|
||||
|
@ -417,7 +417,7 @@ assertErrorMessage(() => wasmEvalText(`(module
|
|||
)
|
||||
)`).exports;
|
||||
let a = newFixed();
|
||||
assertEq(a.length, 0);
|
||||
assertEq(wasmGcArrayLength(a), 0);
|
||||
}
|
||||
|
||||
// run: resulting 30-element array is as expected
|
||||
|
@ -459,9 +459,9 @@ assertErrorMessage(() => wasmEvalText(`(module
|
|||
)
|
||||
)`).exports;
|
||||
let a = newFixed();
|
||||
assertEq(a.length, 30);
|
||||
assertEq(wasmGcArrayLength(a), 30);
|
||||
for (i = 0; i < 30; i++) {
|
||||
assertEq(a[i], i + 1);
|
||||
assertEq(wasmGcReadField(a, i), i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -576,7 +576,7 @@ assertErrorMessage(() => wasmEvalText(`(module
|
|||
)
|
||||
)`).exports;
|
||||
let arr = newData();
|
||||
assertEq(arr.length, 0);
|
||||
assertEq(wasmGcArrayLength(arr), 0);
|
||||
}
|
||||
|
||||
// run: range to copy would require OOB read on data segment
|
||||
|
@ -608,11 +608,11 @@ assertErrorMessage(() => wasmEvalText(`(module
|
|||
)
|
||||
)`).exports;
|
||||
let arr = newData();
|
||||
assertEq(arr.length, 4);
|
||||
assertEq(arr[0], 48+1);
|
||||
assertEq(arr[1], 48+3);
|
||||
assertEq(arr[2], 48+3);
|
||||
assertEq(arr[3], 48+7);
|
||||
assertEq(wasmGcArrayLength(arr), 4);
|
||||
assertEq(wasmGcReadField(arr, 0), 48+1);
|
||||
assertEq(wasmGcReadField(arr, 1), 48+3);
|
||||
assertEq(wasmGcReadField(arr, 2), 48+3);
|
||||
assertEq(wasmGcReadField(arr, 3), 48+7);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -770,7 +770,7 @@ assertErrorMessage(() => wasmEvalText(`(module
|
|||
)
|
||||
)`).exports;
|
||||
let arr = newElem();
|
||||
assertEq(arr.length, 0);
|
||||
assertEq(wasmGcArrayLength(arr), 0);
|
||||
}
|
||||
|
||||
// run: range to copy would require OOB read on elem segment
|
||||
|
@ -809,11 +809,11 @@ assertErrorMessage(() => wasmEvalText(`(module
|
|||
)
|
||||
)`).exports;
|
||||
let arr = newElem();
|
||||
assertEq(arr.length, 4);
|
||||
assertEq(arr[0], f1);
|
||||
assertEq(arr[1], f2);
|
||||
assertEq(arr[2], f3);
|
||||
assertEq(arr[3], f4);
|
||||
assertEq(wasmGcArrayLength(arr), 4);
|
||||
assertEq(wasmGcReadField(arr, 0), f1);
|
||||
assertEq(wasmGcReadField(arr, 1), f2);
|
||||
assertEq(wasmGcReadField(arr, 2), f3);
|
||||
assertEq(wasmGcReadField(arr, 3), f4);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -964,10 +964,16 @@ for (let [elemTy, valueTy, src, exp1, exp2] of ARRAY_COPY_TESTS) {
|
|||
assertEq(exp2.length, 6);
|
||||
|
||||
function eqArrays(a1, a2) {
|
||||
assertEq(a1.length, 6);
|
||||
assertEq(a2.length, 6);
|
||||
function len(arr) {
|
||||
return Array.isArray(arr) ? arr.length : wasmGcArrayLength(arr);
|
||||
}
|
||||
function get(arr, i) {
|
||||
return Array.isArray(arr) ? arr[i] : wasmGcReadField(arr, i);
|
||||
}
|
||||
assertEq(len(a1), 6);
|
||||
assertEq(len(a2), 6);
|
||||
for (i = 0; i < 6; i++) {
|
||||
if (a1[i] !== a2[i])
|
||||
if (get(a1, i) !== get(a2, i))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -35,7 +35,7 @@ for (let v of WasmExternrefValues)
|
|||
(func (export "make") (param $v externref) (result eqref)
|
||||
(struct.new $S (local.get $v))))`);
|
||||
let x = ins.exports.make(v);
|
||||
assertEq(x[0], v);
|
||||
assertEq(wasmGcReadField(x, 0), v);
|
||||
}
|
||||
|
||||
// Try to make sure externrefs are properly traced
|
||||
|
@ -51,24 +51,24 @@ for (let v of WasmExternrefValues)
|
|||
let ins = wasmEvalText(txt);
|
||||
let x = ins.exports.make({x:0}, {x:1}, {x:2}, {x:3}, {x:4}, {x:5}, {x:6}, {x:7}, {x:8}, {x:9})
|
||||
gc('shrinking');
|
||||
assertEq(typeof x[0], "object");
|
||||
assertEq(x[0].x, 0);
|
||||
assertEq(typeof x[1], "object");
|
||||
assertEq(x[1].x, 1);
|
||||
assertEq(typeof x[2], "object");
|
||||
assertEq(x[2].x, 2);
|
||||
assertEq(typeof x[3], "object");
|
||||
assertEq(x[3].x, 3);
|
||||
assertEq(typeof x[4], "object");
|
||||
assertEq(x[4].x, 4);
|
||||
assertEq(typeof x[5], "object");
|
||||
assertEq(x[5].x, 5);
|
||||
assertEq(typeof x[6], "object");
|
||||
assertEq(x[6].x, 6);
|
||||
assertEq(typeof x[7], "object");
|
||||
assertEq(x[7].x, 7);
|
||||
assertEq(typeof x[8], "object");
|
||||
assertEq(x[8].x, 8);
|
||||
assertEq(typeof x[9], "object");
|
||||
assertEq(x[9].x, 9);
|
||||
assertEq(typeof wasmGcReadField(x, 0), "object");
|
||||
assertEq(wasmGcReadField(x, 0).x, 0);
|
||||
assertEq(typeof wasmGcReadField(x, 1), "object");
|
||||
assertEq(wasmGcReadField(x, 1).x, 1);
|
||||
assertEq(typeof wasmGcReadField(x, 2), "object");
|
||||
assertEq(wasmGcReadField(x, 2).x, 2);
|
||||
assertEq(typeof wasmGcReadField(x, 3), "object");
|
||||
assertEq(wasmGcReadField(x, 3).x, 3);
|
||||
assertEq(typeof wasmGcReadField(x, 4), "object");
|
||||
assertEq(wasmGcReadField(x, 4).x, 4);
|
||||
assertEq(typeof wasmGcReadField(x, 5), "object");
|
||||
assertEq(wasmGcReadField(x, 5).x, 5);
|
||||
assertEq(typeof wasmGcReadField(x, 6), "object");
|
||||
assertEq(wasmGcReadField(x, 6).x, 6);
|
||||
assertEq(typeof wasmGcReadField(x, 7), "object");
|
||||
assertEq(wasmGcReadField(x, 7).x, 7);
|
||||
assertEq(typeof wasmGcReadField(x, 8), "object");
|
||||
assertEq(wasmGcReadField(x, 8).x, 8);
|
||||
assertEq(typeof wasmGcReadField(x, 9), "object");
|
||||
assertEq(wasmGcReadField(x, 9).x, 9);
|
||||
}
|
||||
|
|
|
@ -33,14 +33,14 @@ const { structNew, structNewDefault, structLarge } = wasmEvalText(`(module
|
|||
|
||||
let result;
|
||||
result = structNew();
|
||||
assertEq(result[0], 2);
|
||||
assertEq(result[1], new Float32Array([3.140000104904175])[0]);
|
||||
assertEq(wasmGcReadField(result, 0), 2);
|
||||
assertEq(wasmGcReadField(result, 1), new Float32Array([3.140000104904175])[0]);
|
||||
result = structNewDefault();
|
||||
assertEq(result[0], 0);
|
||||
assertEq(result[1], 0);
|
||||
assertEq(wasmGcReadField(result, 0), 0);
|
||||
assertEq(wasmGcReadField(result, 1), 0);
|
||||
result = structLarge();
|
||||
assertEq(result[2], 3n);
|
||||
assertEq(result[19], 19);
|
||||
assertEq(wasmGcReadField(result, 2), 3n);
|
||||
assertEq(wasmGcReadField(result, 19), 19);
|
||||
|
||||
// array.new, array.new_default, and array.new_fixed
|
||||
|
||||
|
@ -62,14 +62,14 @@ const { arrayNew, arrayNewDefault, arrayNewFixed } = wasmEvalText(`(module
|
|||
)`).exports;
|
||||
|
||||
result = arrayNew();
|
||||
assertEq(result.length, 3);
|
||||
assertEq(result[0], 3.14);
|
||||
assertEq(result[2], 3.14);
|
||||
assertEq(wasmGcArrayLength(result), 3);
|
||||
assertEq(wasmGcReadField(result, 0), 3.14);
|
||||
assertEq(wasmGcReadField(result, 2), 3.14);
|
||||
result = arrayNewDefault();
|
||||
assertEq(result.length, 2);
|
||||
assertEq(result[1], 0);
|
||||
assertEq(wasmGcArrayLength(result), 2);
|
||||
assertEq(wasmGcReadField(result, 1), 0);
|
||||
result = arrayNewFixed();
|
||||
assertEq(result.length, 2);
|
||||
assertEq(result[0][0], 10);
|
||||
assertEq(result[0][1], 16);
|
||||
assertEq(result[1], null);
|
||||
assertEq(wasmGcArrayLength(result), 2);
|
||||
assertEq(wasmGcReadField(wasmGcReadField(result, 0), 0), 10);
|
||||
assertEq(wasmGcReadField(wasmGcReadField(result, 0), 1), 16);
|
||||
assertEq(wasmGcReadField(result, 1), null);
|
||||
|
|
|
@ -14,13 +14,9 @@ let { newStruct } = wasmEvalText(`
|
|||
test('(ref null 0)', [newStruct()], WasmNonAnyrefValues, '(type (struct))');
|
||||
test('nullref', [null], WasmNonAnyrefValues);
|
||||
|
||||
function test(type, validValues, invalidValues, typeSection) {
|
||||
function test(type, validValues, invalidValues, typeSection = "") {
|
||||
const CheckError = /can only pass|bad type/;
|
||||
|
||||
if (!typeSection) {
|
||||
typeSection = "";
|
||||
}
|
||||
|
||||
// 1. Exported function params
|
||||
let {a} = wasmEvalText(`(module
|
||||
${typeSection}
|
||||
|
@ -104,4 +100,26 @@ function test(type, validValues, invalidValues, typeSection) {
|
|||
TypeError,
|
||||
CheckError);
|
||||
}
|
||||
|
||||
// 5. Verify that GC objects are opaque
|
||||
for (const val of validValues) {
|
||||
if (!val) continue;
|
||||
|
||||
assertEq(Reflect.getPrototypeOf(val), null);
|
||||
assertEq(Reflect.setPrototypeOf(val, null), true);
|
||||
assertEq(Reflect.setPrototypeOf(val, {}), false);
|
||||
assertEq(Reflect.isExtensible(val), false);
|
||||
assertEq(Reflect.preventExtensions(val), false);
|
||||
assertEq(Reflect.getOwnPropertyDescriptor(val, "anything"), undefined);
|
||||
assertEq(Reflect.defineProperty(val, "anything", { value: 42 }), false);
|
||||
assertEq(Reflect.has(val, "anything"), false);
|
||||
assertEq(Reflect.get(val, "anything"), undefined);
|
||||
assertErrorMessage(() => { Reflect.set(val, "anything", 3); }, TypeError, /can't modify/);
|
||||
assertErrorMessage(() => { Reflect.deleteProperty(val, "anything"); }, TypeError, /can't modify/);
|
||||
assertEq(Reflect.ownKeys(val).length, 0, `gc objects should not have keys, but this one had: ${Reflect.ownKeys(val)}`);
|
||||
for (const i in val) {
|
||||
throw new Error(`GC objects should have no enumerable properties, but had ${i}`);
|
||||
}
|
||||
assertEq(val[Symbol.iterator], undefined, "GC objects should not be iterable");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -210,11 +210,11 @@ assertEq(wasmEvalText(
|
|||
let a = makeA();
|
||||
|
||||
let b = makeB();
|
||||
assertEq(b[0], 0);
|
||||
assertEq(b[1], 0);
|
||||
assertEq(wasmGcReadField(b, 0), 0);
|
||||
assertEq(wasmGcReadField(b, 1), 0);
|
||||
|
||||
let c = makeC();
|
||||
assertEq(c[0], null);
|
||||
assertEq(wasmGcReadField(c, 0), null);
|
||||
}
|
||||
|
||||
// struct.new_default: valid if all struct fields are defaultable
|
||||
|
|
|
@ -202,29 +202,23 @@ assertEq(ins.x1(12), 36)
|
|||
assertEq(ins.x2(8), Math.PI)
|
||||
|
||||
var point = ins.mk_point();
|
||||
assertEq(0 in point, true);
|
||||
assertEq(1 in point, true);
|
||||
assertEq(2 in point, false);
|
||||
assertEq(point[0], 37);
|
||||
assertEq(point[1], 42);
|
||||
assertEq(wasmGcReadField(point, 0), 37);
|
||||
assertEq(wasmGcReadField(point, 1), 42);
|
||||
|
||||
var int_node = ins.mk_int_node(78, point);
|
||||
assertEq(int_node[0], 78);
|
||||
assertEq(int_node[1], point);
|
||||
assertEq(wasmGcReadField(int_node, 0), 78);
|
||||
assertEq(wasmGcReadField(int_node, 1), point);
|
||||
|
||||
var bigger = ins.mk_bigger();
|
||||
for ( let i=0; i < 52; i++ )
|
||||
assertEq(bigger[i], i);
|
||||
|
||||
assertEq(bigger[-1], undefined);
|
||||
assertEq(bigger[52], undefined);
|
||||
assertEq(wasmGcReadField(bigger, i), i);
|
||||
|
||||
var withfloats = ins.mk_withfloats(1/3, Math.PI, bigger, 5/6, 0x1337);
|
||||
assertEq(withfloats[0], Math.fround(1/3));
|
||||
assertEq(withfloats[1], Math.PI);
|
||||
assertEq(withfloats[2], bigger);
|
||||
assertEq(withfloats[3], Math.fround(5/6));
|
||||
assertEq(withfloats[4], 0x1337);
|
||||
assertEq(wasmGcReadField(withfloats, 0), Math.fround(1/3));
|
||||
assertEq(wasmGcReadField(withfloats, 1), Math.PI);
|
||||
assertEq(wasmGcReadField(withfloats, 2), bigger);
|
||||
assertEq(wasmGcReadField(withfloats, 3), Math.fround(5/6));
|
||||
assertEq(wasmGcReadField(withfloats, 4), 0x1337);
|
||||
|
||||
// A simple stress test
|
||||
|
||||
|
@ -244,8 +238,8 @@ var stressIns = new WebAssembly.Instance(new WebAssembly.Module(stress)).exports
|
|||
var stressLevel = conf.x64 && !conf.tsan && !conf.asan && !conf.valgrind ? 100000 : 1000;
|
||||
var the_list = stressIns.iota1(stressLevel);
|
||||
for (let i=1; i <= stressLevel; i++) {
|
||||
assertEq(the_list[0], i);
|
||||
the_list = the_list[1];
|
||||
assertEq(wasmGcReadField(the_list, 0), i);
|
||||
the_list = wasmGcReadField(the_list, 1);
|
||||
}
|
||||
assertEq(the_list, null);
|
||||
|
||||
|
@ -289,19 +283,19 @@ assertEq(the_list, null);
|
|||
|
||||
let v = ins.mk();
|
||||
assertEq(typeof v, "object");
|
||||
assertEq(v[0], 0x7aaaaaaa);
|
||||
assertEq(v[1], 0x4201020337n);
|
||||
assertEq(wasmGcReadField(v, 0), 0x7aaaaaaa);
|
||||
assertEq(wasmGcReadField(v, 1), 0x4201020337n);
|
||||
assertEq(ins.low(v), 0x01020337);
|
||||
assertEq(ins.high(v), 0x42);
|
||||
assertEq(v[2], 0x6bbbbbbb);
|
||||
assertEq(wasmGcReadField(v, 2), 0x6bbbbbbb);
|
||||
|
||||
ins.set(v);
|
||||
assertEq(v[0], 0x7aaaaaaa);
|
||||
assertEq(v[1], 0x3333333376544567n);
|
||||
assertEq(v[2], 0x6bbbbbbb);
|
||||
assertEq(wasmGcReadField(v, 0), 0x7aaaaaaa);
|
||||
assertEq(wasmGcReadField(v, 1), 0x3333333376544567n);
|
||||
assertEq(wasmGcReadField(v, 2), 0x6bbbbbbb);
|
||||
|
||||
ins.set2(v);
|
||||
assertEq(v[1], 0x3141592653589793n);
|
||||
assertEq(wasmGcReadField(v, 1), 0x3141592653589793n);
|
||||
assertEq(ins.low(v), 0x53589793);
|
||||
assertEq(ins.high(v), 0x31415926)
|
||||
}
|
||||
|
@ -351,28 +345,28 @@ assertEq(the_list, null);
|
|||
let ins = wasmEvalText(txt).exports;
|
||||
|
||||
let v = ins.make();
|
||||
assertEq(v[0], 0x7aaaaaaa);
|
||||
assertEq(v[1], 0x4201020337n);
|
||||
assertEq(v[2], 0x6bbbbbbb);
|
||||
assertEq(wasmGcReadField(v, 0), 0x7aaaaaaa);
|
||||
assertEq(wasmGcReadField(v, 1), 0x4201020337n);
|
||||
assertEq(wasmGcReadField(v, 2), 0x6bbbbbbb);
|
||||
|
||||
ins.update0(0x45367101);
|
||||
assertEq(v[0], 0x45367101);
|
||||
assertEq(wasmGcReadField(v, 0), 0x45367101);
|
||||
assertEq(ins.get0(), 0x45367101);
|
||||
assertEq(v[1], 0x4201020337n);
|
||||
assertEq(v[2], 0x6bbbbbbb);
|
||||
assertEq(wasmGcReadField(v, 1), 0x4201020337n);
|
||||
assertEq(wasmGcReadField(v, 2), 0x6bbbbbbb);
|
||||
|
||||
ins.update2(0x62345123);
|
||||
assertEq(v[0], 0x45367101);
|
||||
assertEq(v[1], 0x4201020337n);
|
||||
assertEq(wasmGcReadField(v, 0), 0x45367101);
|
||||
assertEq(wasmGcReadField(v, 1), 0x4201020337n);
|
||||
assertEq(ins.get2(), 0x62345123);
|
||||
assertEq(v[2], 0x62345123);
|
||||
assertEq(wasmGcReadField(v, 2), 0x62345123);
|
||||
|
||||
ins.update1(0x77777777, 0x22222222);
|
||||
assertEq(v[0], 0x45367101);
|
||||
assertEq(wasmGcReadField(v, 0), 0x45367101);
|
||||
assertEq(ins.get1_low(), 0x22222222);
|
||||
assertEq(ins.get1_high(), 0x77777777);
|
||||
assertEq(v[1], 0x7777777722222222n);
|
||||
assertEq(v[2], 0x62345123);
|
||||
assertEq(wasmGcReadField(v, 1), 0x7777777722222222n);
|
||||
assertEq(wasmGcReadField(v, 2), 0x62345123);
|
||||
}
|
||||
|
||||
|
||||
|
@ -600,12 +594,8 @@ WebAssembly.CompileError, /signature index references non-signature/);
|
|||
(func (export "make") (result eqref)
|
||||
(struct.new $s (i32.const 37) (i64.const 42))))`).exports;
|
||||
let v = ins.make();
|
||||
assertErrorMessage(() => v[0] = 12,
|
||||
Error,
|
||||
/setting immutable field/);
|
||||
assertErrorMessage(() => v[1] = 12,
|
||||
Error,
|
||||
/setting immutable field/);
|
||||
assertErrorMessage(() => v[0] = 12, TypeError, /can't modify/);
|
||||
assertErrorMessage(() => v[1] = 12, TypeError, /can't modify/);
|
||||
}
|
||||
|
||||
// Function should not reference struct type: binary test
|
||||
|
|
|
@ -61,7 +61,7 @@
|
|||
// writes are too large, but those will only be visible if we're running
|
||||
// on ASan or Valgrind. In any case, add a fake data dependency below, so
|
||||
// that the construction of the object can't (so easily) be optimised away.
|
||||
assertEq(obj8[0] + BigInt(obj8[31]), 0x12ACn); // == 0x1234 + 0x78
|
||||
assertEq(wasmGcReadField(obj8, 0) + BigInt(wasmGcReadField(obj8, 31)), 0x12ACn); // == 0x1234 + 0x78
|
||||
}
|
||||
|
||||
// And exactly the same, except for 16 bit fields.
|
||||
|
@ -97,7 +97,7 @@
|
|||
)`;
|
||||
let exports = wasmEvalText(txt).exports;
|
||||
let obj16 = exports.build16(0x4321n, 0x7865);
|
||||
assertEq(obj16[0] + BigInt(obj16[23]), 0xBB86n); // == 0x4321 + 0x7865
|
||||
assertEq(wasmGcReadField(obj16, 0) + BigInt(wasmGcReadField(obj16, 23)), 0xBB86n); // == 0x4321 + 0x7865
|
||||
}
|
||||
|
||||
// Test that 8-bit field writes do not overwrite adjacent fields.
|
||||
|
@ -122,14 +122,14 @@
|
|||
let exports = wasmEvalText(txt).exports;
|
||||
let theObject = exports.create();
|
||||
exports.writeField8x8_3(theObject, 0x77);
|
||||
assertEq(theObject[0], 0x55);
|
||||
assertEq(theObject[1], 0x55);
|
||||
assertEq(theObject[2], 0x55);
|
||||
assertEq(theObject[3], 0x77);
|
||||
assertEq(theObject[4], 0x55);
|
||||
assertEq(theObject[5], 0x55);
|
||||
assertEq(theObject[6], 0x55);
|
||||
assertEq(theObject[7], 0x55);
|
||||
assertEq(wasmGcReadField(theObject, 0), 0x55);
|
||||
assertEq(wasmGcReadField(theObject, 1), 0x55);
|
||||
assertEq(wasmGcReadField(theObject, 2), 0x55);
|
||||
assertEq(wasmGcReadField(theObject, 3), 0x77);
|
||||
assertEq(wasmGcReadField(theObject, 4), 0x55);
|
||||
assertEq(wasmGcReadField(theObject, 5), 0x55);
|
||||
assertEq(wasmGcReadField(theObject, 6), 0x55);
|
||||
assertEq(wasmGcReadField(theObject, 7), 0x55);
|
||||
}
|
||||
|
||||
// Test that 16-bit field writes do not overwrite adjacent fields.
|
||||
|
@ -154,14 +154,14 @@
|
|||
let exports = wasmEvalText(txt).exports;
|
||||
let theObject = exports.create();
|
||||
exports.writeField16x8_3(theObject, 0x7766);
|
||||
assertEq(theObject[0], 0x5555);
|
||||
assertEq(theObject[1], 0x5555);
|
||||
assertEq(theObject[2], 0x5555);
|
||||
assertEq(theObject[3], 0x7766);
|
||||
assertEq(theObject[4], 0x5555);
|
||||
assertEq(theObject[5], 0x5555);
|
||||
assertEq(theObject[6], 0x5555);
|
||||
assertEq(theObject[7], 0x5555);
|
||||
assertEq(wasmGcReadField(theObject, 0), 0x5555);
|
||||
assertEq(wasmGcReadField(theObject, 1), 0x5555);
|
||||
assertEq(wasmGcReadField(theObject, 2), 0x5555);
|
||||
assertEq(wasmGcReadField(theObject, 3), 0x7766);
|
||||
assertEq(wasmGcReadField(theObject, 4), 0x5555);
|
||||
assertEq(wasmGcReadField(theObject, 5), 0x5555);
|
||||
assertEq(wasmGcReadField(theObject, 6), 0x5555);
|
||||
assertEq(wasmGcReadField(theObject, 7), 0x5555);
|
||||
}
|
||||
|
||||
// Test that 8-bit field reads sign/zero extend correctly.
|
||||
|
|
|
@ -1939,13 +1939,10 @@ bool js::SetPrototype(JSContext* cx, HandleObject obj, HandleObject proto,
|
|||
}
|
||||
|
||||
/*
|
||||
* Disallow mutating the [[Prototype]] on Typed Objects, per the spec.
|
||||
* Disallow mutating the [[Prototype]] on WebAssembly GC objects.
|
||||
*/
|
||||
if (obj->is<WasmGcObject>()) {
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
|
||||
JSMSG_CANT_SET_PROTO_OF,
|
||||
"incompatible WebAssembly object");
|
||||
return false;
|
||||
return result.fail(JSMSG_CANT_SET_PROTO);
|
||||
}
|
||||
|
||||
/* ES6 9.1.2 step 5 forbids changing [[Prototype]] if not [[Extensible]]. */
|
||||
|
@ -1998,6 +1995,10 @@ bool js::PreventExtensions(JSContext* cx, HandleObject obj,
|
|||
return js::Proxy::preventExtensions(cx, obj, result);
|
||||
}
|
||||
|
||||
if (obj->is<WasmGcObject>()) {
|
||||
return result.failCantPreventExtensions();
|
||||
}
|
||||
|
||||
if (!obj->nonProxyIsExtensible()) {
|
||||
// If the following assertion fails, there's somewhere else a missing
|
||||
// call to shrinkCapacityToInitializedLength() which needs to be found
|
||||
|
|
|
@ -160,62 +160,6 @@ using namespace wasm;
|
|||
//=========================================================================
|
||||
// WasmGcObject
|
||||
|
||||
bool WasmGcObject::lookupProperty(JSContext* cx, Handle<WasmGcObject*> object,
|
||||
jsid id, PropOffset* offset,
|
||||
FieldType* type) {
|
||||
switch (kind()) {
|
||||
case wasm::TypeDefKind::Struct: {
|
||||
const auto& structType = typeDef().structType();
|
||||
uint32_t index;
|
||||
if (!IdIsIndex(id, &index)) {
|
||||
return false;
|
||||
}
|
||||
if (index >= structType.fields_.length()) {
|
||||
return false;
|
||||
}
|
||||
const StructField& field = structType.fields_[index];
|
||||
offset->set(field.offset);
|
||||
*type = field.type;
|
||||
return true;
|
||||
}
|
||||
case wasm::TypeDefKind::Array: {
|
||||
const auto& arrayType = typeDef().arrayType();
|
||||
|
||||
// Special case for property 'length' that loads the length field at the
|
||||
// beginning of the data buffer
|
||||
if (id.isString() &&
|
||||
id.toString() == cx->runtime()->commonNames->length) {
|
||||
STATIC_ASSERT_WASMARRAYELEMENTS_NUMELEMENTS_IS_U32;
|
||||
*type = FieldType::I32;
|
||||
offset->set(UINT32_MAX);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Normal case of indexed properties for loading array elements
|
||||
uint32_t index;
|
||||
if (!IdIsIndex(id, &index)) {
|
||||
return false;
|
||||
}
|
||||
uint32_t numElements = object->as<WasmArrayObject>().numElements_;
|
||||
if (index >= numElements) {
|
||||
return false;
|
||||
}
|
||||
uint64_t scaledIndex =
|
||||
uint64_t(index) * uint64_t(arrayType.elementType_.size());
|
||||
if (scaledIndex >= uint64_t(UINT32_MAX)) {
|
||||
// It's unrepresentable as an WasmGcObject::PropOffset. Give up.
|
||||
return false;
|
||||
}
|
||||
offset->set(uint32_t(scaledIndex));
|
||||
*type = arrayType.elementType_;
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
MOZ_ASSERT_UNREACHABLE();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const ObjectOps WasmGcObject::objectOps_ = {
|
||||
WasmGcObject::obj_lookupProperty, // lookupProperty
|
||||
WasmGcObject::obj_defineProperty, // defineProperty
|
||||
|
@ -232,122 +176,52 @@ const ObjectOps WasmGcObject::objectOps_ = {
|
|||
bool WasmGcObject::obj_lookupProperty(JSContext* cx, HandleObject obj,
|
||||
HandleId id, MutableHandleObject objp,
|
||||
PropertyResult* propp) {
|
||||
Rooted<WasmGcObject*> typedObj(cx, &obj->as<WasmGcObject>());
|
||||
if (typedObj->hasProperty(cx, typedObj, id)) {
|
||||
propp->setWasmGcProperty();
|
||||
objp.set(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
RootedObject proto(cx, obj->staticPrototype());
|
||||
if (!proto) {
|
||||
objp.set(nullptr);
|
||||
propp->setNotFound();
|
||||
return true;
|
||||
}
|
||||
|
||||
return LookupProperty(cx, proto, id, objp, propp);
|
||||
objp.set(nullptr);
|
||||
propp->setNotFound();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WasmGcObject::obj_defineProperty(JSContext* cx, HandleObject obj,
|
||||
HandleId id,
|
||||
Handle<PropertyDescriptor> desc,
|
||||
ObjectOpResult& result) {
|
||||
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
|
||||
JSMSG_OBJECT_NOT_EXTENSIBLE, "WasmGcObject");
|
||||
return false;
|
||||
result.failReadOnly();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WasmGcObject::obj_hasProperty(JSContext* cx, HandleObject obj, HandleId id,
|
||||
bool* foundp) {
|
||||
Rooted<WasmGcObject*> typedObj(cx, &obj->as<WasmGcObject>());
|
||||
if (typedObj->hasProperty(cx, typedObj, id)) {
|
||||
*foundp = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
RootedObject proto(cx, obj->staticPrototype());
|
||||
if (!proto) {
|
||||
*foundp = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
return HasProperty(cx, proto, id, foundp);
|
||||
*foundp = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WasmGcObject::obj_getProperty(JSContext* cx, HandleObject obj,
|
||||
HandleValue receiver, HandleId id,
|
||||
MutableHandleValue vp) {
|
||||
Rooted<WasmGcObject*> typedObj(cx, &obj->as<WasmGcObject>());
|
||||
|
||||
WasmGcObject::PropOffset offset;
|
||||
FieldType type;
|
||||
if (typedObj->lookupProperty(cx, typedObj, id, &offset, &type)) {
|
||||
return typedObj->loadValue(cx, offset, type, vp);
|
||||
}
|
||||
|
||||
RootedObject proto(cx, obj->staticPrototype());
|
||||
if (!proto) {
|
||||
vp.setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
return GetProperty(cx, proto, receiver, id, vp);
|
||||
vp.setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WasmGcObject::obj_setProperty(JSContext* cx, HandleObject obj, HandleId id,
|
||||
HandleValue v, HandleValue receiver,
|
||||
ObjectOpResult& result) {
|
||||
Rooted<WasmGcObject*> typedObj(cx, &obj->as<WasmGcObject>());
|
||||
|
||||
if (typedObj->hasProperty(cx, typedObj, id)) {
|
||||
if (!receiver.isObject() || obj != &receiver.toObject()) {
|
||||
return SetPropertyByDefining(cx, id, v, receiver, result);
|
||||
}
|
||||
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
|
||||
JSMSG_TYPEDOBJECT_SETTING_IMMUTABLE);
|
||||
return false;
|
||||
}
|
||||
|
||||
return SetPropertyOnProto(cx, obj, id, v, receiver, result);
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
|
||||
JSMSG_WASM_MODIFIED_GC_OBJECT);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WasmGcObject::obj_getOwnPropertyDescriptor(
|
||||
JSContext* cx, HandleObject obj, HandleId id,
|
||||
MutableHandle<mozilla::Maybe<PropertyDescriptor>> desc) {
|
||||
Rooted<WasmGcObject*> typedObj(cx, &obj->as<WasmGcObject>());
|
||||
|
||||
WasmGcObject::PropOffset offset;
|
||||
FieldType type;
|
||||
if (typedObj->lookupProperty(cx, typedObj, id, &offset, &type)) {
|
||||
RootedValue value(cx);
|
||||
if (!typedObj->loadValue(cx, offset, type, &value)) {
|
||||
return false;
|
||||
}
|
||||
desc.set(mozilla::Some(PropertyDescriptor::Data(
|
||||
value,
|
||||
{JS::PropertyAttribute::Enumerable, JS::PropertyAttribute::Writable})));
|
||||
return true;
|
||||
}
|
||||
|
||||
desc.reset();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WasmGcObject::obj_deleteProperty(JSContext* cx, HandleObject obj,
|
||||
HandleId id, ObjectOpResult& result) {
|
||||
Rooted<WasmGcObject*> typedObj(cx, &obj->as<WasmGcObject>());
|
||||
if (typedObj->hasProperty(cx, typedObj, id)) {
|
||||
return Throw(cx, id, JSMSG_CANT_DELETE);
|
||||
}
|
||||
|
||||
RootedObject proto(cx, obj->staticPrototype());
|
||||
if (!proto) {
|
||||
return result.succeed();
|
||||
}
|
||||
|
||||
return DeleteProperty(cx, proto, id, result);
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
|
||||
JSMSG_WASM_MODIFIED_GC_OBJECT);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* static */
|
||||
|
@ -375,9 +249,61 @@ WasmGcObject* WasmGcObject::create(JSContext* cx,
|
|||
return obj;
|
||||
}
|
||||
|
||||
bool WasmGcObject::loadValue(JSContext* cx,
|
||||
const WasmGcObject::PropOffset& offset,
|
||||
FieldType type, MutableHandleValue vp) {
|
||||
bool WasmGcObject::lookUpProperty(JSContext* cx, Handle<WasmGcObject*> obj,
|
||||
jsid id, WasmGcObject::PropOffset* offset,
|
||||
FieldType* type) {
|
||||
switch (obj->kind()) {
|
||||
case wasm::TypeDefKind::Struct: {
|
||||
const auto& structType = obj->typeDef().structType();
|
||||
uint32_t index;
|
||||
if (!IdIsIndex(id, &index)) {
|
||||
return false;
|
||||
}
|
||||
if (index >= structType.fields_.length()) {
|
||||
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
|
||||
JSMSG_WASM_OUT_OF_BOUNDS);
|
||||
return false;
|
||||
}
|
||||
const StructField& field = structType.fields_[index];
|
||||
offset->set(field.offset);
|
||||
*type = field.type;
|
||||
return true;
|
||||
}
|
||||
case wasm::TypeDefKind::Array: {
|
||||
const auto& arrayType = obj->typeDef().arrayType();
|
||||
|
||||
uint32_t index;
|
||||
if (!IdIsIndex(id, &index)) {
|
||||
return false;
|
||||
}
|
||||
uint32_t numElements = obj->as<WasmArrayObject>().numElements_;
|
||||
if (index >= numElements) {
|
||||
return false;
|
||||
}
|
||||
uint64_t scaledIndex =
|
||||
uint64_t(index) * uint64_t(arrayType.elementType_.size());
|
||||
if (scaledIndex >= uint64_t(UINT32_MAX)) {
|
||||
// It's unrepresentable as an WasmGcObject::PropOffset. Give up.
|
||||
return false;
|
||||
}
|
||||
offset->set(uint32_t(scaledIndex));
|
||||
*type = arrayType.elementType_;
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
MOZ_ASSERT_UNREACHABLE();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool WasmGcObject::loadValue(JSContext* cx, Handle<WasmGcObject*> obj, jsid id,
|
||||
MutableHandleValue vp) {
|
||||
WasmGcObject::PropOffset offset;
|
||||
FieldType type;
|
||||
if (!lookUpProperty(cx, obj, id, &offset, &type)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Temporary hack, (ref T) is not exposable to JS yet but some tests would
|
||||
// like to access it so we erase (ref T) with eqref when loading. This is
|
||||
// safe as (ref T) <: eqref and we're not in the writing case where we
|
||||
|
@ -392,10 +318,10 @@ bool WasmGcObject::loadValue(JSContext* cx,
|
|||
return false;
|
||||
}
|
||||
|
||||
if (is<WasmStructObject>()) {
|
||||
if (obj->is<WasmStructObject>()) {
|
||||
// `offset` is the field offset, without regard to the in/out-line split.
|
||||
// That is handled by the call to `fieldOffsetToAddress`.
|
||||
WasmStructObject& structObj = as<WasmStructObject>();
|
||||
const WasmStructObject& structObj = obj->as<WasmStructObject>();
|
||||
// Ensure no out-of-range access possible
|
||||
MOZ_RELEASE_ASSERT(structObj.kind() == TypeDefKind::Struct);
|
||||
MOZ_RELEASE_ASSERT(offset.get() + type.size() <=
|
||||
|
@ -404,17 +330,8 @@ bool WasmGcObject::loadValue(JSContext* cx,
|
|||
type, vp);
|
||||
}
|
||||
|
||||
MOZ_ASSERT(is<WasmArrayObject>());
|
||||
WasmArrayObject& arrayObj = as<WasmArrayObject>();
|
||||
if (offset.get() == UINT32_MAX) {
|
||||
// This denotes "length"
|
||||
uint32_t numElements = arrayObj.numElements_;
|
||||
// We can't use `ToJSValue(.., ValType::I32, ..)` here since it will
|
||||
// treat the integer as signed, which it isn't. `vp.set(..)` will
|
||||
// coerce correctly to a JS::Value, though.
|
||||
vp.set(NumberValue(numElements));
|
||||
return true;
|
||||
}
|
||||
MOZ_ASSERT(obj->is<WasmArrayObject>());
|
||||
const WasmArrayObject& arrayObj = obj->as<WasmArrayObject>();
|
||||
return ToJSValue(cx, arrayObj.data_ + offset.get(), type, vp);
|
||||
}
|
||||
|
||||
|
@ -426,38 +343,6 @@ bool WasmGcObject::isRuntimeSubtypeOf(
|
|||
bool WasmGcObject::obj_newEnumerate(JSContext* cx, HandleObject obj,
|
||||
MutableHandleIdVector properties,
|
||||
bool enumerableOnly) {
|
||||
MOZ_ASSERT(obj->is<WasmGcObject>());
|
||||
Rooted<WasmGcObject*> typedObj(cx, &obj->as<WasmGcObject>());
|
||||
|
||||
size_t indexCount = 0;
|
||||
size_t otherCount = 0;
|
||||
switch (typedObj->kind()) {
|
||||
case wasm::TypeDefKind::Struct: {
|
||||
indexCount = typedObj->typeDef().structType().fields_.length();
|
||||
break;
|
||||
}
|
||||
case wasm::TypeDefKind::Array: {
|
||||
indexCount = typedObj->as<WasmArrayObject>().numElements_;
|
||||
otherCount = 1;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
MOZ_ASSERT_UNREACHABLE();
|
||||
}
|
||||
|
||||
if (!properties.reserve(indexCount + otherCount)) {
|
||||
return false;
|
||||
}
|
||||
RootedId id(cx);
|
||||
for (size_t index = 0; index < indexCount; index++) {
|
||||
id = PropertyKey::Int(int32_t(index));
|
||||
properties.infallibleAppend(id);
|
||||
}
|
||||
|
||||
if (typedObj->kind() == wasm::TypeDefKind::Array) {
|
||||
properties.infallibleAppend(NameToId(cx->runtime()->commonNames->length));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -12,9 +12,9 @@
|
|||
|
||||
#include "gc/Allocator.h"
|
||||
#include "gc/Pretenuring.h"
|
||||
#include "vm/ArrayBufferObject.h"
|
||||
#include "vm/JSObject.h"
|
||||
#include "wasm/WasmInstanceData.h"
|
||||
#include "wasm/WasmMemory.h"
|
||||
#include "wasm/WasmTypeDef.h"
|
||||
#include "wasm/WasmValType.h"
|
||||
|
||||
|
@ -86,20 +86,14 @@ class WasmGcObject : public JSObject {
|
|||
void set(uint32_t u32) { u32_ = u32; }
|
||||
};
|
||||
|
||||
[[nodiscard]] bool lookupProperty(JSContext* cx,
|
||||
js::Handle<WasmGcObject*> object, jsid id,
|
||||
PropOffset* offset, wasm::FieldType* type);
|
||||
[[nodiscard]] bool hasProperty(JSContext* cx,
|
||||
js::Handle<WasmGcObject*> object, jsid id) {
|
||||
WasmGcObject::PropOffset offset;
|
||||
wasm::FieldType type;
|
||||
return lookupProperty(cx, object, id, &offset, &type);
|
||||
}
|
||||
|
||||
bool loadValue(JSContext* cx, const WasmGcObject::PropOffset& offset,
|
||||
wasm::FieldType type, MutableHandleValue vp);
|
||||
[[nodiscard]] static bool lookUpProperty(JSContext* cx,
|
||||
Handle<WasmGcObject*> obj, jsid id,
|
||||
PropOffset* offset, FieldType* type);
|
||||
|
||||
public:
|
||||
[[nodiscard]] static bool loadValue(JSContext* cx, Handle<WasmGcObject*> obj,
|
||||
jsid id, MutableHandleValue vp);
|
||||
|
||||
const wasm::SuperTypeVector& superTypeVector() const {
|
||||
return *superTypeVector_;
|
||||
}
|
||||
|
@ -289,7 +283,7 @@ class WasmStructObject : public WasmGcObject {
|
|||
// Given the offset of a field, return its actual address. `fieldType` is
|
||||
// for assertional purposes only.
|
||||
inline uint8_t* fieldOffsetToAddress(FieldType fieldType,
|
||||
uint32_t fieldOffset);
|
||||
uint32_t fieldOffset) const;
|
||||
|
||||
// JIT accessors
|
||||
static constexpr size_t offsetOfOutlineData() {
|
||||
|
@ -351,8 +345,8 @@ inline void WasmStructObject::fieldOffsetToAreaAndOffset(FieldType fieldType,
|
|||
((fieldOffset + fieldType.size() - 1) < WasmStructObject_MaxInlineBytes));
|
||||
}
|
||||
|
||||
inline uint8_t* WasmStructObject::fieldOffsetToAddress(FieldType fieldType,
|
||||
uint32_t fieldOffset) {
|
||||
inline uint8_t* WasmStructObject::fieldOffsetToAddress(
|
||||
FieldType fieldType, uint32_t fieldOffset) const {
|
||||
bool areaIsOutline;
|
||||
uint32_t areaOffset;
|
||||
fieldOffsetToAreaAndOffset(fieldType, fieldOffset, &areaIsOutline,
|
||||
|
|
|
@ -1780,9 +1780,10 @@ bool Instance::init(JSContext* cx, const JSObjectVector& funcImports,
|
|||
}
|
||||
|
||||
// Find the shape using the class and recursion group
|
||||
const ObjectFlags objectFlags = {ObjectFlag::NotExtensible};
|
||||
typeDefData->shape =
|
||||
WasmGCShape::getShape(cx, clasp, cx->realm(), TaggedProto(),
|
||||
&typeDef.recGroup(), ObjectFlags());
|
||||
&typeDef.recGroup(), objectFlags);
|
||||
if (!typeDefData->shape) {
|
||||
return false;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче