v8/test/mjsunit/wasm/wasm-gc-externalize-internalize.js
Manos Koukoutos 936b61a209 [wasm-gc] Canonicalize JS Numbers as i31ref at the boundary
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}
2022-11-11 15:13:10 +00:00

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));