936b61a209
JS numbers flowing into Wasm as i31ref should be canonicalized at the boundary. In-range numbers get canonicalized to Smis, and out-of-range numbers to HeapNumbers. This way, casting to i31ref, or checking for i31ref when casting to other types, is reduced to a Smi check. Bug: v8:7748 Change-Id: Icd2bbca7870c094f32ddc9cba1d2be16207e80d1 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/4008345 Reviewed-by: Jakob Kummerow <jkummerow@chromium.org> Commit-Queue: Manos Koukoutos <manoskouk@chromium.org> Cr-Commit-Position: refs/heads/main@{#84219}
226 lines
6.9 KiB
JavaScript
226 lines
6.9 KiB
JavaScript
// 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
|
|
"use strict";
|
|
|
|
d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
|
|
|
|
let instance = (() => {
|
|
let builder = new WasmModuleBuilder();
|
|
let struct = builder.addStruct([makeField(kWasmI32, true)]);
|
|
|
|
/**
|
|
* type_producer -> create type and pass as anyref
|
|
* (implicit externalize by calling convention)
|
|
* type_externalize -> create type, externalize, pass by externref
|
|
* type_consumer -> consume type by anyref
|
|
* (implicit internalize by calling convention)
|
|
* type_internalize -> consume type by externref, internalize
|
|
*/
|
|
|
|
builder.addFunction('struct_producer', makeSig([kWasmI32], [kWasmEqRef]))
|
|
.addBody([
|
|
kExprLocalGet, 0,
|
|
kGCPrefix, kExprStructNew, struct])
|
|
.exportFunc();
|
|
|
|
builder.addFunction('struct_externalize',
|
|
makeSig([kWasmI32], [kWasmExternRef]))
|
|
.addBody([
|
|
kExprLocalGet, 0,
|
|
kGCPrefix, kExprStructNew, struct,
|
|
kGCPrefix, kExprExternExternalize,
|
|
])
|
|
.exportFunc();
|
|
|
|
builder.addFunction('struct_consumer',
|
|
makeSig([kWasmEqRef], [kWasmI32, kWasmI32]))
|
|
.addBody([
|
|
kExprLocalGet, 0,
|
|
kExprRefIsNull,
|
|
kExprBlock, kWasmVoid,
|
|
kExprLocalGet, 0,
|
|
kExprBrOnNull, 0,
|
|
kGCPrefix, kExprRefAsStruct,
|
|
kGCPrefix, kExprRefCast, struct,
|
|
kGCPrefix, kExprStructGet, struct, 0, // value
|
|
kExprI32Const, 0, // isNull
|
|
kExprReturn,
|
|
kExprEnd,
|
|
kExprDrop,
|
|
kExprI32Const, 0, // value (placeholder)
|
|
kExprI32Const, 1, // isNull
|
|
])
|
|
.exportFunc();
|
|
|
|
builder.addFunction('struct_internalize',
|
|
makeSig([kWasmExternRef], [kWasmI32, kWasmI32]))
|
|
.addBody([
|
|
kExprLocalGet, 0,
|
|
kExprRefIsNull,
|
|
kExprBlock, kWasmVoid,
|
|
kExprLocalGet, 0,
|
|
kGCPrefix, kExprExternInternalize,
|
|
kExprBrOnNull, 0,
|
|
kGCPrefix, kExprRefAsStruct,
|
|
kGCPrefix, kExprRefCast, struct,
|
|
kGCPrefix, kExprStructGet, struct, 0, // value
|
|
kExprI32Const, 0, // isNull
|
|
kExprReturn,
|
|
kExprEnd,
|
|
kExprDrop,
|
|
kExprI32Const, 0, // value (placeholder)
|
|
kExprI32Const, 1, // isNull
|
|
])
|
|
.exportFunc();
|
|
|
|
builder.addFunction('i31_producer', makeSig([kWasmI32], [kWasmEqRef]))
|
|
.addBody([
|
|
kExprLocalGet, 0,
|
|
kGCPrefix, kExprI31New])
|
|
.exportFunc();
|
|
|
|
builder.addFunction('i31_externalize',
|
|
makeSig([kWasmI32], [kWasmExternRef]))
|
|
.addBody([
|
|
kExprLocalGet, 0,
|
|
kGCPrefix, kExprI31New,
|
|
kGCPrefix, kExprExternExternalize,
|
|
])
|
|
.exportFunc();
|
|
|
|
builder.addFunction('i31_consumer',
|
|
makeSig([kWasmEqRef], [kWasmI32, kWasmI32]))
|
|
.addBody([
|
|
kExprLocalGet, 0,
|
|
kExprRefIsNull,
|
|
kExprBlock, kWasmVoid,
|
|
kExprLocalGet, 0,
|
|
kExprBrOnNull, 0,
|
|
kGCPrefix, kExprRefAsI31,
|
|
kGCPrefix, kExprI31GetS, // value
|
|
kExprI32Const, 0, // isNull
|
|
kExprReturn,
|
|
kExprEnd,
|
|
kExprDrop,
|
|
kExprI32Const, 0, // value (placeholder)
|
|
kExprI32Const, 1, // isNull
|
|
])
|
|
.exportFunc();
|
|
|
|
builder.addFunction('i31_internalize',
|
|
makeSig([kWasmExternRef], [kWasmI32, kWasmI32]))
|
|
.addBody([
|
|
kExprLocalGet, 0,
|
|
kExprRefIsNull,
|
|
kExprBlock, kWasmVoid,
|
|
kExprLocalGet, 0,
|
|
kGCPrefix, kExprExternInternalize,
|
|
kExprBrOnNull, 0,
|
|
kGCPrefix, kExprRefAsI31,
|
|
kGCPrefix, kExprI31GetS, // value
|
|
kExprI32Const, 0, // isNull
|
|
kExprReturn,
|
|
kExprEnd,
|
|
kExprDrop,
|
|
kExprI32Const, 0, // value (placeholder)
|
|
kExprI32Const, 1, // isNull
|
|
])
|
|
.exportFunc();
|
|
|
|
let array = builder.addArray(kWasmI32, true);
|
|
|
|
builder.addFunction('array_producer', makeSig([kWasmI32], [kWasmEqRef]))
|
|
.addBody([
|
|
kExprLocalGet, 0,
|
|
kGCPrefix, kExprArrayNewFixed, array, 1])
|
|
.exportFunc();
|
|
|
|
builder.addFunction('array_externalize',
|
|
makeSig([kWasmI32], [kWasmExternRef]))
|
|
.addBody([
|
|
kExprLocalGet, 0,
|
|
kGCPrefix, kExprArrayNewFixed, array, 1,
|
|
kGCPrefix, kExprExternExternalize,
|
|
])
|
|
.exportFunc();
|
|
|
|
builder.addFunction('array_consumer',
|
|
makeSig([kWasmEqRef], [kWasmI32, kWasmI32]))
|
|
.addBody([
|
|
kExprLocalGet, 0,
|
|
kExprRefIsNull,
|
|
kExprBlock, kWasmVoid,
|
|
kExprLocalGet, 0,
|
|
kExprBrOnNull, 0,
|
|
kGCPrefix, kExprRefAsArray,
|
|
kGCPrefix, kExprRefCast, array,
|
|
kExprI32Const, 0,
|
|
kGCPrefix, kExprArrayGet, array, // value
|
|
kExprI32Const, 0, // isNull
|
|
kExprReturn,
|
|
kExprEnd,
|
|
kExprDrop,
|
|
kExprI32Const, 0, // value (placeholder)
|
|
kExprI32Const, 1, // isNull
|
|
])
|
|
.exportFunc();
|
|
|
|
builder.addFunction('array_internalize',
|
|
makeSig([kWasmExternRef], [kWasmI32, kWasmI32]))
|
|
.addBody([
|
|
kExprLocalGet, 0,
|
|
kExprRefIsNull,
|
|
kExprBlock, kWasmVoid,
|
|
kExprLocalGet, 0,
|
|
kGCPrefix, kExprExternInternalize,
|
|
kExprBrOnNull, 0,
|
|
kGCPrefix, kExprRefAsArray,
|
|
kGCPrefix, kExprRefCast, array,
|
|
kExprI32Const, 0,
|
|
kGCPrefix, kExprArrayGet, array, // value
|
|
kExprI32Const, 0, // isNull
|
|
kExprReturn,
|
|
kExprEnd,
|
|
kExprDrop,
|
|
kExprI32Const, 0, // value (placeholder)
|
|
kExprI32Const, 1, // isNull
|
|
])
|
|
.exportFunc();
|
|
|
|
return builder.instantiate({});
|
|
})();
|
|
|
|
for (let type of ["struct", "i31", "array"]) {
|
|
for (let consume of ["consumer", "internalize"]) {
|
|
let fnConsume = instance.exports[`${type}_${consume}`];
|
|
// A null is converted to (ref null none).
|
|
assertEquals([0, 1], fnConsume(null));
|
|
if (consume == "internalize") {
|
|
// Passing a JavaScript object is fine on internalize but fails on
|
|
// casting it to dataref/arrayref/i31ref.
|
|
var errorType = WebAssembly.RuntimeError;
|
|
var errorMsg = "illegal cast";
|
|
} else {
|
|
// Passing a JavaScript object fails as it is not convertible to eqref.
|
|
var errorType = TypeError;
|
|
var errorMsg = "type incompatibility when transforming from/to JS";
|
|
}
|
|
assertThrows(() => fnConsume({}), errorType, errorMsg);
|
|
|
|
for (let produce of ["producer", "externalize"]) {
|
|
let fnProduce = instance.exports[`${type}_${produce}`];
|
|
// Test roundtrip of a value produced in Wasm passed back to Wasm.
|
|
let obj42 = fnProduce(42);
|
|
assertEquals([42, 0], fnConsume(obj42));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Differently to structs and arrays, the i31 value is directly accessible in
|
|
// JavaScript. Similarly, a JS smi can be internalized as an i31ref.
|
|
assertEquals(12345, instance.exports.i31_externalize(12345));
|
|
assertEquals([12345, 0], instance.exports.i31_internalize(12345));
|