commit 14e953f61591b39ab4221df8526d7b8d66820bfb parent 8e1c3e46748144055a8924a53c95533864870919 Author: Asumu Takikawa <asumu@igalia.com> Date: Mon, 8 Dec 2025 16:02:54 +0000 Bug 1977854 - Wasm custom page sizes part 6: add spec tests from proposal r=rhunt,bvisness Differential Revision: https://phabricator.services.mozilla.com/D267334 Diffstat:
9 files changed, 1079 insertions(+), 0 deletions(-)
diff --git a/js/src/jit-test/etc/wasm/generate-spectests/config-lock.toml b/js/src/jit-test/etc/wasm/generate-spectests/config-lock.toml @@ -5,3 +5,7 @@ commit = 'd7aada502' [[repos]] name = 'threads' commit = '3635ca51a' + +[[repos]] +name = 'custom-page-sizes' +commit = 'cad80dc2c' diff --git a/js/src/jit-test/etc/wasm/generate-spectests/config.toml b/js/src/jit-test/etc/wasm/generate-spectests/config.toml @@ -67,6 +67,60 @@ excluded_tests = [ ] directive = "; skip-if: !wasmThreadsEnabled() || helperThreadCount() === 0;" +[[repos]] +name = "custom-page-sizes" +url = "https://github.com/WebAssembly/custom-page-sizes" +branch = "main" +parent = "spec" +excluded_tests = [ + # tests that are unrelated and out of date + "^address.wast.js", + "^align.wast.js", + "^binary.wast.js", + "^binary-leb128.wast.js", + "^br_if.wast.js", + "^br_table.wast.js", + "^call_indirect.wast.js", + "^comments.wast.js", + "^custom.wast.js", + "^data.wast.js", + "^elem.wast.js", + "^exports.wast.js", + "^f32.wast.js", + "^f64.wast.js", + "^float_exprs.wast.js", + "^float_literals.wast.js", + "^float_memory.wast.js", + "^float_misc.wast.js", + "^func.wast.js", + "^global.wast.js", + "^if.wast.js", + "^imports.wast.js", + "^linking.wast.js", + "^load.wast.js", + "^local_get.wast.js", + "^local_tee.wast.js", + "^memory_copy.wast.js", + "^memory_fill.wast.js", + "^memory_grow.wast.js", + "^memory_init.wast.js", + "^memory_size.wast.js", + "^memory.wast.js", + "^ref_func.wast.js", + "^ref_is_null.wast.js", + "^ref_null.wast.js", + "^select.wast.js", + "^store.wast.js", + "^simd_.*.wast.js", + "^table.*.wast.js", + "^token.wast.js", + "^tokens.wast.js", + "^unreachable.wast.js", + "^unreached-.*.wast.js", + "^utf8-.*.wast.js", +] +directive = "; skip-if: !wasmCustomPageSizesEnabled(); test-also=-P wasm_custom_page_sizes; test-also=--wasm-compiler=baseline -P wasm_custom_page_sizes; test-also=--wasm-compiler=optimizing -P wasm_custom_page_sizes; test-also=--disable-wasm-huge-memory -P wasm_custom_page_sizes" + # Example proposal config: # # [[repos]] diff --git a/js/src/jit-test/tests/wasm/spec/custom-page-sizes/custom-page-sizes-invalid.wast.js b/js/src/jit-test/tests/wasm/spec/custom-page-sizes/custom-page-sizes-invalid.wast.js @@ -0,0 +1,164 @@ +/* Copyright 2021 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// ./test/core/custom-page-sizes/custom-page-sizes-invalid.wast + +// ./test/core/custom-page-sizes/custom-page-sizes-invalid.wast:2 +assert_malformed( + () => instantiate(`(memory 0 (pagesize 3)) `), + `invalid custom page size`, +); + +// ./test/core/custom-page-sizes/custom-page-sizes-invalid.wast:7 +assert_malformed( + () => instantiate(`(memory 0 (pagesize 0)) `), + `invalid custom page size`, +); + +// ./test/core/custom-page-sizes/custom-page-sizes-invalid.wast:13 +assert_invalid( + () => instantiate(`(module (memory 0 (pagesize 2)))`), + `invalid custom page size`, +); + +// ./test/core/custom-page-sizes/custom-page-sizes-invalid.wast:17 +assert_invalid( + () => instantiate(`(module (memory 0 (pagesize 4)))`), + `invalid custom page size`, +); + +// ./test/core/custom-page-sizes/custom-page-sizes-invalid.wast:21 +assert_invalid( + () => instantiate(`(module (memory 0 (pagesize 8)))`), + `invalid custom page size`, +); + +// ./test/core/custom-page-sizes/custom-page-sizes-invalid.wast:25 +assert_invalid( + () => instantiate(`(module (memory 0 (pagesize 16)))`), + `invalid custom page size`, +); + +// ./test/core/custom-page-sizes/custom-page-sizes-invalid.wast:29 +assert_invalid( + () => instantiate(`(module (memory 0 (pagesize 32)))`), + `invalid custom page size`, +); + +// ./test/core/custom-page-sizes/custom-page-sizes-invalid.wast:33 +assert_invalid( + () => instantiate(`(module (memory 0 (pagesize 64)))`), + `invalid custom page size`, +); + +// ./test/core/custom-page-sizes/custom-page-sizes-invalid.wast:37 +assert_invalid( + () => instantiate(`(module (memory 0 (pagesize 128)))`), + `invalid custom page size`, +); + +// ./test/core/custom-page-sizes/custom-page-sizes-invalid.wast:41 +assert_invalid( + () => instantiate(`(module (memory 0 (pagesize 256)))`), + `invalid custom page size`, +); + +// ./test/core/custom-page-sizes/custom-page-sizes-invalid.wast:45 +assert_invalid( + () => instantiate(`(module (memory 0 (pagesize 512)))`), + `invalid custom page size`, +); + +// ./test/core/custom-page-sizes/custom-page-sizes-invalid.wast:49 +assert_invalid( + () => instantiate(`(module (memory 0 (pagesize 1024)))`), + `invalid custom page size`, +); + +// ./test/core/custom-page-sizes/custom-page-sizes-invalid.wast:53 +assert_invalid( + () => instantiate(`(module (memory 0 (pagesize 2048)))`), + `invalid custom page size`, +); + +// ./test/core/custom-page-sizes/custom-page-sizes-invalid.wast:57 +assert_invalid( + () => instantiate(`(module (memory 0 (pagesize 4096)))`), + `invalid custom page size`, +); + +// ./test/core/custom-page-sizes/custom-page-sizes-invalid.wast:61 +assert_invalid( + () => instantiate(`(module (memory 0 (pagesize 8192)))`), + `invalid custom page size`, +); + +// ./test/core/custom-page-sizes/custom-page-sizes-invalid.wast:65 +assert_invalid( + () => instantiate(`(module (memory 0 (pagesize 16384)))`), + `invalid custom page size`, +); + +// ./test/core/custom-page-sizes/custom-page-sizes-invalid.wast:69 +assert_invalid( + () => instantiate(`(module (memory 0 (pagesize 32768)))`), + `invalid custom page size`, +); + +// ./test/core/custom-page-sizes/custom-page-sizes-invalid.wast:75 +assert_invalid( + () => instantiate(`(module (memory 0 (pagesize 0x20000)))`), + `invalid custom page size`, +); + +// ./test/core/custom-page-sizes/custom-page-sizes-invalid.wast:82 +assert_malformed( + () => instantiate(`(module binary + "\\00asm" "\\01\\00\\00\\00" + "\\05\\04\\01" ;; Memory section + + ;; memory 0 + "\\08" ;; flags w/ custom page size + "\\00" ;; minimum = 0 + "\\41" ;; pagesize = 2**65 + )`), + `invalid custom page size`, +); + +// ./test/core/custom-page-sizes/custom-page-sizes-invalid.wast:97 +let $0 = instantiate(`(module \$m + (memory (export "small-pages-memory") 0 (pagesize 1)) + (memory (export "large-pages-memory") 0 (pagesize 65536)) +)`); +let $m = $0; + +// ./test/core/custom-page-sizes/custom-page-sizes-invalid.wast:101 +register($m, `m`); + +// ./test/core/custom-page-sizes/custom-page-sizes-invalid.wast:103 +assert_unlinkable( + () => instantiate(`(module + (memory (import "m" "small-pages-memory") 0 (pagesize 65536)) + )`), + `memory types incompatible`, +); + +// ./test/core/custom-page-sizes/custom-page-sizes-invalid.wast:110 +assert_unlinkable( + () => instantiate(`(module + (memory (import "m" "large-pages-memory") 0 (pagesize 1)) + )`), + `memory types incompatible`, +); diff --git a/js/src/jit-test/tests/wasm/spec/custom-page-sizes/custom-page-sizes.wast.js b/js/src/jit-test/tests/wasm/spec/custom-page-sizes/custom-page-sizes.wast.js @@ -0,0 +1,216 @@ +/* Copyright 2021 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// ./test/core/custom-page-sizes/custom-page-sizes.wast + +// ./test/core/custom-page-sizes/custom-page-sizes.wast:2 +let $0 = instantiate(`(module (memory 1 (pagesize 1)))`); + +// ./test/core/custom-page-sizes/custom-page-sizes.wast:3 +let $1 = instantiate(`(module (memory 1 (pagesize 65536)))`); + +// ./test/core/custom-page-sizes/custom-page-sizes.wast:6 +let $2 = instantiate(`(module (memory 1 2 (pagesize 1)))`); + +// ./test/core/custom-page-sizes/custom-page-sizes.wast:7 +let $3 = instantiate(`(module (memory 1 2 (pagesize 65536)))`); + +// ./test/core/custom-page-sizes/custom-page-sizes.wast:10 +let $4 = instantiate(`(module + (memory 0 (pagesize 1)) + (func (export "size") (result i32) + memory.size + ) + (func (export "grow") (param i32) (result i32) + (memory.grow (local.get 0)) + ) + (func (export "load") (param i32) (result i32) + (i32.load8_u (local.get 0)) + ) + (func (export "store") (param i32 i32) + (i32.store8 (local.get 0) (local.get 1)) + ) +)`); + +// ./test/core/custom-page-sizes/custom-page-sizes.wast:26 +assert_return(() => invoke($4, `size`, []), [value("i32", 0)]); + +// ./test/core/custom-page-sizes/custom-page-sizes.wast:27 +assert_trap(() => invoke($4, `load`, [0]), `out of bounds memory access`); + +// ./test/core/custom-page-sizes/custom-page-sizes.wast:29 +assert_return(() => invoke($4, `grow`, [65536]), [value("i32", 0)]); + +// ./test/core/custom-page-sizes/custom-page-sizes.wast:30 +assert_return(() => invoke($4, `size`, []), [value("i32", 65536)]); + +// ./test/core/custom-page-sizes/custom-page-sizes.wast:31 +assert_return(() => invoke($4, `load`, [65535]), [value("i32", 0)]); + +// ./test/core/custom-page-sizes/custom-page-sizes.wast:32 +assert_return(() => invoke($4, `store`, [65535, 1]), []); + +// ./test/core/custom-page-sizes/custom-page-sizes.wast:33 +assert_return(() => invoke($4, `load`, [65535]), [value("i32", 1)]); + +// ./test/core/custom-page-sizes/custom-page-sizes.wast:34 +assert_trap(() => invoke($4, `load`, [65536]), `out of bounds memory access`); + +// ./test/core/custom-page-sizes/custom-page-sizes.wast:36 +assert_return(() => invoke($4, `grow`, [65536]), [value("i32", 65536)]); + +// ./test/core/custom-page-sizes/custom-page-sizes.wast:37 +assert_return(() => invoke($4, `size`, []), [value("i32", 131072)]); + +// ./test/core/custom-page-sizes/custom-page-sizes.wast:38 +assert_return(() => invoke($4, `load`, [131071]), [value("i32", 0)]); + +// ./test/core/custom-page-sizes/custom-page-sizes.wast:39 +assert_return(() => invoke($4, `store`, [131071, 1]), []); + +// ./test/core/custom-page-sizes/custom-page-sizes.wast:40 +assert_return(() => invoke($4, `load`, [131071]), [value("i32", 1)]); + +// ./test/core/custom-page-sizes/custom-page-sizes.wast:41 +assert_trap(() => invoke($4, `load`, [131072]), `out of bounds memory access`); + +// ./test/core/custom-page-sizes/custom-page-sizes.wast:46 +let $5 = instantiate(`(module + (memory 0 (pagesize 65536)) + (func (export "size") (result i32) + memory.size + ) + (func (export "grow") (param i32) (result i32) + (memory.grow (local.get 0)) + ) +)`); + +// ./test/core/custom-page-sizes/custom-page-sizes.wast:55 +assert_return(() => invoke($5, `size`, []), [value("i32", 0)]); + +// ./test/core/custom-page-sizes/custom-page-sizes.wast:56 +assert_return(() => invoke($5, `grow`, [65537]), [value("i32", -1)]); + +// ./test/core/custom-page-sizes/custom-page-sizes.wast:57 +assert_return(() => invoke($5, `size`, []), [value("i32", 0)]); + +// ./test/core/custom-page-sizes/custom-page-sizes.wast:60 +let $6 = instantiate(`(module + (memory \$small 10 (pagesize 1)) + (memory \$large 1 (pagesize 65536)) + + (data (memory \$small) (i32.const 0) "\\11\\22\\33\\44") + (data (memory \$large) (i32.const 0) "\\55\\66\\77\\88") + + (func (export "copy-small-to-large") (param i32 i32 i32) + (memory.copy \$large \$small (local.get 0) (local.get 1) (local.get 2)) + ) + + (func (export "copy-large-to-small") (param i32 i32 i32) + (memory.copy \$small \$large (local.get 0) (local.get 1) (local.get 2)) + ) + + (func (export "load8-small") (param i32) (result i32) + (i32.load8_u \$small (local.get 0)) + ) + + (func (export "load8-large") (param i32) (result i32) + (i32.load8_u \$large (local.get 0)) + ) +)`); + +// ./test/core/custom-page-sizes/custom-page-sizes.wast:84 +assert_return(() => invoke($6, `copy-small-to-large`, [6, 0, 2]), []); + +// ./test/core/custom-page-sizes/custom-page-sizes.wast:85 +assert_return(() => invoke($6, `load8-large`, [6]), [value("i32", 17)]); + +// ./test/core/custom-page-sizes/custom-page-sizes.wast:86 +assert_return(() => invoke($6, `load8-large`, [7]), [value("i32", 34)]); + +// ./test/core/custom-page-sizes/custom-page-sizes.wast:88 +assert_return(() => invoke($6, `copy-large-to-small`, [4, 1, 3]), []); + +// ./test/core/custom-page-sizes/custom-page-sizes.wast:89 +assert_return(() => invoke($6, `load8-small`, [4]), [value("i32", 102)]); + +// ./test/core/custom-page-sizes/custom-page-sizes.wast:90 +assert_return(() => invoke($6, `load8-small`, [5]), [value("i32", 119)]); + +// ./test/core/custom-page-sizes/custom-page-sizes.wast:91 +assert_return(() => invoke($6, `load8-small`, [6]), [value("i32", 136)]); + +// ./test/core/custom-page-sizes/custom-page-sizes.wast:96 +let $7 = instantiate(`(module \$m + (memory (export "small-pages-memory") 0 (pagesize 1)) + (memory (export "large-pages-memory") 0 (pagesize 65536)) +)`); +let $m = $7; + +// ./test/core/custom-page-sizes/custom-page-sizes.wast:100 +register($m, `m`); + +// ./test/core/custom-page-sizes/custom-page-sizes.wast:102 +let $8 = instantiate(`(module + (memory (import "m" "small-pages-memory") 0 (pagesize 1)) +)`); + +// ./test/core/custom-page-sizes/custom-page-sizes.wast:106 +let $9 = instantiate(`(module + (memory (import "m" "large-pages-memory") 0 (pagesize 65536)) +)`); + +// ./test/core/custom-page-sizes/custom-page-sizes.wast:113 +assert_malformed( + () => instantiate(`(memory (pagesize 0) (data)) `), + `invalid custom page size`, +); + +// ./test/core/custom-page-sizes/custom-page-sizes.wast:116 +let $10 = instantiate(`(module + (memory (pagesize 1) (data "xyz")) + (func (export "size") (result i32) + memory.size) + (func (export "grow") (param i32) (result i32) + (memory.grow (local.get 0))) + (func (export "load") (param i32) (result i32) + (i32.load8_u (local.get 0))))`); + +// ./test/core/custom-page-sizes/custom-page-sizes.wast:125 +assert_return(() => invoke($10, `size`, []), [value("i32", 3)]); + +// ./test/core/custom-page-sizes/custom-page-sizes.wast:126 +assert_return(() => invoke($10, `load`, [0]), [value("i32", 120)]); + +// ./test/core/custom-page-sizes/custom-page-sizes.wast:127 +assert_return(() => invoke($10, `load`, [1]), [value("i32", 121)]); + +// ./test/core/custom-page-sizes/custom-page-sizes.wast:128 +assert_return(() => invoke($10, `load`, [2]), [value("i32", 122)]); + +// ./test/core/custom-page-sizes/custom-page-sizes.wast:129 +assert_trap(() => invoke($10, `load`, [3]), `out of bounds memory access`); + +// ./test/core/custom-page-sizes/custom-page-sizes.wast:130 +assert_return(() => invoke($10, `grow`, [1]), [value("i32", -1)]); + +// ./test/core/custom-page-sizes/custom-page-sizes.wast:133 +let $11 = instantiate(`(module + (memory (pagesize 65536) (data "xyz")) + (func (export "size") (result i32) + memory.size))`); + +// ./test/core/custom-page-sizes/custom-page-sizes.wast:138 +assert_return(() => invoke($11, `size`, []), [value("i32", 1)]); diff --git a/js/src/jit-test/tests/wasm/spec/custom-page-sizes/directives.txt b/js/src/jit-test/tests/wasm/spec/custom-page-sizes/directives.txt @@ -0,0 +1 @@ +|jit-test| test-also=--wasm-compiler=optimizing; test-also=--wasm-compiler=baseline; test-also=--setpref=wasm_test_serialization=true; test-also=--test-wasm-await-tier2; test-also=--disable-wasm-huge-memory; skip-variant-if: --disable-wasm-huge-memory, !wasmHugeMemorySupported(); local-include:harness/harness.js; skip-if: !wasmCustomPageSizesEnabled(); test-also=-P wasm_custom_page_sizes; test-also=--wasm-compiler=baseline -P wasm_custom_page_sizes; test-also=--wasm-compiler=optimizing -P wasm_custom_page_sizes; test-also=--disable-wasm-huge-memory -P wasm_custom_page_sizes +\ No newline at end of file diff --git a/js/src/jit-test/tests/wasm/spec/custom-page-sizes/harness/directives.txt b/js/src/jit-test/tests/wasm/spec/custom-page-sizes/harness/directives.txt @@ -0,0 +1 @@ +|jit-test| skip-if: true +\ No newline at end of file diff --git a/js/src/jit-test/tests/wasm/spec/custom-page-sizes/harness/harness.js b/js/src/jit-test/tests/wasm/spec/custom-page-sizes/harness/harness.js @@ -0,0 +1,532 @@ +"use strict"; + +/* Copyright 2021 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +if (!wasmIsSupported()) { + quit(); +} + +function partialOobWriteMayWritePartialData() { + let arm_native = getBuildConfiguration("arm") && !getBuildConfiguration("arm-simulator"); + let arm64_native = getBuildConfiguration("arm64") && !getBuildConfiguration("arm64-simulator"); + let riscv64_native = getBuildConfiguration("riscv64") && !getBuildConfiguration("riscv64-simulator"); + return arm_native || arm64_native || riscv64_native; +} + +function bytes(type, bytes) { + var typedBuffer = new Uint8Array(bytes); + return wasmGlobalFromArrayBuffer(type, typedBuffer.buffer); +} +function value(type, value) { + return new WebAssembly.Global({ + value: type, + mutable: false, + }, value); +} + +function i8x16(elements) { + let typedBuffer = new Uint8Array(elements); + return wasmGlobalFromArrayBuffer("v128", typedBuffer.buffer); +} +function i16x8(elements) { + let typedBuffer = new Uint16Array(elements); + return wasmGlobalFromArrayBuffer("v128", typedBuffer.buffer); +} +function i32x4(elements) { + let typedBuffer = new Uint32Array(elements); + return wasmGlobalFromArrayBuffer("v128", typedBuffer.buffer); +} +function i64x2(elements) { + let typedBuffer = new BigUint64Array(elements); + return wasmGlobalFromArrayBuffer("v128", typedBuffer.buffer); +} +function f32x4(elements) { + let typedBuffer = new Float32Array(elements); + return wasmGlobalFromArrayBuffer("v128", typedBuffer.buffer); +} +function f64x2(elements) { + let typedBuffer = new Float64Array(elements); + return wasmGlobalFromArrayBuffer("v128", typedBuffer.buffer); +} + +function either(...arr) { + return new EitherVariants(arr); +} + +class F32x4Pattern { + constructor(x, y, z, w) { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } +} + +class F64x2Pattern { + constructor(x, y) { + this.x = x; + this.y = y; + } +} + +class RefWithType { + constructor(type) { + this.type = type; + } + + formatExpected() { + return `RefWithType(${this.type})`; + } + + test(refGlobal) { + try { + new WebAssembly.Global({value: this.type}, refGlobal.value); + return true; + } catch (err) { + assertEq(err instanceof TypeError, true, `wrong type of error when creating global: ${err}`); + assertEq(!!err.message.match(/can only pass/), true, `wrong type of error when creating global: ${err}`); + return false; + } + } +} + +// ref.extern values created by spec tests will be JS objects of the form +// { [externsym]: <number> }. Other externref values are possible to observe +// if extern.convert_any is used. +let externsym = Symbol("externref"); +function externref(s) { + return { [externsym]: s }; +} + +class ExternRefResult { + constructor(n) { + this.n = n; + } + + formatExpected() { + return `ref.extern ${this.n}`; + } + + test(global) { + // the global's value can either be an externref or just a plain old JS number + let result = global.value; + if (typeof global.value === "object" && externsym in global.value) { + result = global.value[externsym]; + } + return result === this.n; + } +} + +// ref.host values created by spectests will be whatever the JS API does to +// convert the given value to anyref. It should implicitly be like any.convert_extern. +function hostref(v) { + const { internalizeNum } = new WebAssembly.Instance( + new WebAssembly.Module(wasmTextToBinary(`(module + (func (import "test" "coerce") (param i32) (result anyref)) + (func (export "internalizeNum") (param i32) (result anyref) + (call 0 (local.get 0)) + ) + )`)), + { "test": { "coerce": x => x } }, + ).exports; + return internalizeNum(v); +} + +class HostRefResult { + constructor(n) { + this.n = n; + } + + formatExpected() { + return `ref.host ${this.n}`; + } + + test(externrefGlobal) { + assertEq(externsym in externrefGlobal.value, true, `HostRefResult only works with externref inputs`); + return externrefGlobal.value[externsym] === this.n; + } +} + +// https://github.com/WebAssembly/spec/blob/main/interpreter/README.md#spectest-host-module +let linkage = { + "spectest": { + global_i32: 666, + global_i64: 666n, + global_f32: 666.6, + global_f64: 666.6, + + table: new WebAssembly.Table({ + initial: 10, + maximum: 20, + element: "anyfunc", + }), + table64: new WebAssembly.Table({ + address: "i64", + initial: 10n, + maximum: 20n, + element: "anyfunc", + }), + + memory: new WebAssembly.Memory({ initial: 1, maximum: 2 }), + + print: console.log.bind(console), + print_i32: console.log.bind(console), + print_i64: console.log.bind(console), + print_f32: console.log.bind(console), + print_f64: console.log.bind(console), + print_i32_f32: console.log.bind(console), + print_f64_f64: console.log.bind(console), + }, +}; + +function module(source) { + let bytecode = wasmTextToBinary(source); + let module = new WebAssembly.Module(bytecode); + return module; +} + +function instantiate(source) { + let bytecode = wasmTextToBinary(source); + let module = new WebAssembly.Module(bytecode); + let instance = new WebAssembly.Instance(module, linkage); + return instance.exports; +} + +function instantiateFromModule(module) { + let instance = new WebAssembly.Instance(module, linkage); + return instance.exports; +} + +function register(instance, name) { + linkage[name] = instance; +} + +function invoke(instance, field, params) { + let func = instance[field]; + assertEq(func instanceof Function, true, "expected a function"); + return wasmLosslessInvoke(func, ...params); +} + +function get(instance, field) { + let global = instance[field]; + assertEq( + global instanceof WebAssembly.Global, + true, + "expected a WebAssembly.Global", + ); + return global; +} + +function assert_trap(thunk, message) { + try { + thunk(); + throw new Error(`got no error`); + } catch (err) { + if (err instanceof WebAssembly.RuntimeError) { + return; + } + err.message = `expected trap (${message}): ${err.message}`; + throw err; + } +} + +let StackOverflow; +try { + (function f() { + 1 + f(); + })(); +} catch (e) { + StackOverflow = e.constructor; +} +function assert_exhaustion(thunk, message) { + try { + thunk(); + throw new Error(`got no error`); + } catch (err) { + if (err instanceof StackOverflow) { + return; + } + err.message = `expected exhaustion (${message}): ${err.message}`; + throw err; + } +} + +function assert_invalid(thunk, message) { + try { + thunk(); + throw new Error(`got no error`); + } catch (err) { + if (err instanceof WebAssembly.LinkError || err instanceof WebAssembly.CompileError) { + return; + } + err.message = `expected invalid module (${message}): ${err.message}`; + throw err; + } +} + +function assert_unlinkable(thunk, message) { + try { + thunk(); + throw new Error(`got no error`); + } catch (err) { + if (err instanceof WebAssembly.LinkError || err instanceof WebAssembly.CompileError) { + return; + } + err.message = `expected an unlinkable module (${message}): ${err.message}`; + throw err; + } +} + +function assert_malformed(thunk, message) { + try { + thunk(); + throw new Error(`got no error`); + } catch (err) { + if ( + err instanceof TypeError || + err instanceof SyntaxError || + err instanceof WebAssembly.CompileError || + err instanceof WebAssembly.LinkError + ) { + return; + } + err.message = `expected a malformed module (${message}): ${err.message}`; + throw err; + } +} + +function assert_exception(thunk) { + let thrown = false; + try { + thunk(); + } catch (err) { + thrown = true; + } + assertEq(thrown, true, "expected an exception to be thrown"); +} + +function assert_return(thunk, expected) { + let results = thunk(); + + if (results === undefined) { + results = []; + } else if (!Array.isArray(results)) { + results = [results]; + } + if (!Array.isArray(expected)) { + expected = [expected]; + } + + if (!compareResults(results, expected)) { + let got = results.map((x) => formatResult(x)).join(", "); + let wanted = expected.map((x) => formatExpected(x)).join(", "); + assertEq( + `[${got}]`, + `[${wanted}]`, + ); + assertEq(true, false, `${got} !== ${wanted}`); + } +} + +function formatResult(result) { + if (typeof (result) === "object") { + return wasmGlobalToString(result); + } else { + return `${result}`; + } +} + +function formatExpected(expected) { + if ( + expected === `f32_canonical_nan` || + expected === `f32_arithmetic_nan` || + expected === `f64_canonical_nan` || + expected === `f64_arithmetic_nan` + ) { + return expected; + } else if (expected instanceof F32x4Pattern) { + return `f32x4(${formatExpected(expected.x)}, ${ + formatExpected(expected.y) + }, ${formatExpected(expected.z)}, ${formatExpected(expected.w)})`; + } else if (expected instanceof F64x2Pattern) { + return `f64x2(${formatExpected(expected.x)}, ${ + formatExpected(expected.y) + })`; + } else if (expected instanceof EitherVariants) { + return expected.formatExpected(); + } else if (expected instanceof RefWithType) { + return expected.formatExpected(); + } else if (expected instanceof ExternRefResult) { + return expected.formatExpected(); + } else if (expected instanceof HostRefResult) { + return expected.formatExpected(); + } else if (typeof (expected) === "object") { + return wasmGlobalToString(expected); + } else { + throw new Error("unknown expected result"); + } +} + +class EitherVariants { + constructor(arr) { + this.arr = arr; + } + matches(v) { + return this.arr.some((e) => compareResult(v, e)); + } + formatExpected() { + return `either(${this.arr.map(formatExpected).join(", ")})`; + } +} + +function compareResults(results, expected) { + if (results.length !== expected.length) { + return false; + } + for (let i in results) { + if (expected[i] instanceof EitherVariants) { + return expected[i].matches(results[i]); + } + if (!compareResult(results[i], expected[i])) { + return false; + } + } + return true; +} + +function compareResult(result, expected) { + if ( + expected === `canonical_nan` || + expected === `arithmetic_nan` + ) { + return wasmGlobalIsNaN(result, expected); + } else if (expected === null) { + return result.value === null; + } else if (expected instanceof F32x4Pattern) { + return compareResult( + wasmGlobalExtractLane(result, "f32x4", 0), + expected.x, + ) && + compareResult(wasmGlobalExtractLane(result, "f32x4", 1), expected.y) && + compareResult(wasmGlobalExtractLane(result, "f32x4", 2), expected.z) && + compareResult(wasmGlobalExtractLane(result, "f32x4", 3), expected.w); + } else if (expected instanceof F64x2Pattern) { + return compareResult( + wasmGlobalExtractLane(result, "f64x2", 0), + expected.x, + ) && + compareResult(wasmGlobalExtractLane(result, "f64x2", 1), expected.y); + } else if (expected instanceof RefWithType) { + return expected.test(result); + } else if (expected instanceof ExternRefResult) { + return expected.test(result); + } else if (expected instanceof HostRefResult) { + return expected.test(result); + } else if (typeof (expected) === "object") { + return wasmGlobalsEqual(result, expected); + } else { + throw new Error("unknown expected result"); + } +} + +class Thread { + LOC_STATE = 0; + LOC_DID_ERROR = 1; + + STATE_WORKER_READY = 0x60; // "GO" + STATE_SENDING_VALUE = 0xF00D; // feed me values + STATE_GOT_VALUE = 0x600DF00D; // mm delicious values + STATE_RUN_CODE = 0xC0DE; + STATE_DONE = 0xDEAD; + + constructor(sharedModule, sharedModuleName, code) { + this._coord = new Int32Array(new SharedArrayBuffer(4*2)); + + setSharedObject(this._coord.buffer); + evalInWorker(` + const _coord = new Int32Array(getSharedObject()); + + ${readRelativeToScript("harness.js")} + + function setState(state) { + Atomics.store(_coord, ${this.LOC_STATE}, state); + } + function waitForState(expected) { + while (Atomics.load(_coord, ${this.LOC_STATE}) !== expected) {} + } + function receive() { + waitForState(${this.STATE_SENDING_VALUE}); + const x = getSharedObject(); + setState(${this.STATE_GOT_VALUE}); + return x; + } + + // Tell main thread we are ready + setState(${this.STATE_WORKER_READY}); + + // Get shared module's exports from main thread. (We do this one at a + // time for reasons explained below.) + const ${sharedModuleName} = {}; + ${Object.keys(sharedModule).map(name => + `${sharedModuleName}["${name}"] = receive();` + )} + waitForState(${this.STATE_RUN_CODE}); + + try { + ${code} + } catch (e) { + Atomics.store(_coord, ${this.LOC_DID_ERROR}, 1); + throw e; + } finally { + setState(${this.STATE_DONE}); + } + `); + + // Wait for worker to spawn + this.waitForState(this.STATE_WORKER_READY); + + // Send shared module exports to worker. We send values one at a time + // because setGlobalObject can only take very specific objects, like wasm + // memories, not generic objects like the whole exports object. + for (const exportedValue of Object.values(sharedModule)) { + this.send(exportedValue); + } + + // Give the worker the all-clear to execute its workload + this.setState(this.STATE_RUN_CODE); + } + + setState(state) { + Atomics.store(this._coord, this.LOC_STATE, state); + } + + waitForState(expected) { + while (Atomics.load(this._coord, this.LOC_STATE) !== expected) {} + } + + send(val) { + setSharedObject(val); + this.setState(this.STATE_SENDING_VALUE); + this.waitForState(this.STATE_GOT_VALUE); + } + + wait() { + this.waitForState(this.STATE_DONE); + if (Atomics.load(this._coord, this.LOC_DID_ERROR)) { + throw new Error("Error in worker code. Note that line numbers will not be helpful because of how the harness is loaded."); + } + } +} diff --git a/js/src/jit-test/tests/wasm/spec/custom-page-sizes/memory_max.wast.js b/js/src/jit-test/tests/wasm/spec/custom-page-sizes/memory_max.wast.js @@ -0,0 +1,53 @@ +/* Copyright 2021 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// ./test/core/custom-page-sizes/memory_max.wast + +// ./test/core/custom-page-sizes/memory_max.wast:12 +let $0 = instantiate(`(module + (memory (export "unknown") 0))`); + +// ./test/core/custom-page-sizes/memory_max.wast:15 +register($0, `test`); + +// ./test/core/custom-page-sizes/memory_max.wast:18 +assert_unlinkable( + () => instantiate(`(module + (import "test" "unknown" (func)) + (memory 0xFFFF_FFFF (pagesize 1)))`), + `unknown import`, +); + +// ./test/core/custom-page-sizes/memory_max.wast:25 +assert_unlinkable( + () => instantiate(`(module + (import "test" "unknown" (func)) + (memory 65536 (pagesize 65536)))`), + `unknown import`, +); + +// ./test/core/custom-page-sizes/memory_max.wast:34 +assert_invalid( + () => instantiate(`(module + (memory 0x1_0000_0000 (pagesize 1)))`), + `memory size must be at most`, +); + +// ./test/core/custom-page-sizes/memory_max.wast:40 +assert_invalid( + () => instantiate(`(module + (memory 65537 (pagesize 65536)))`), + `memory size must be at most`, +); diff --git a/js/src/jit-test/tests/wasm/spec/custom-page-sizes/memory_max_i64.wast.js b/js/src/jit-test/tests/wasm/spec/custom-page-sizes/memory_max_i64.wast.js @@ -0,0 +1,52 @@ +/* Copyright 2021 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// ./test/core/custom-page-sizes/memory_max_i64.wast + +// ./test/core/custom-page-sizes/memory_max_i64.wast:12 +let $0 = instantiate(`(module + (memory (export "unknown") 0))`); + +// ./test/core/custom-page-sizes/memory_max_i64.wast:15 +register($0, `test`); + +// ./test/core/custom-page-sizes/memory_max_i64.wast:18 +assert_unlinkable( + () => instantiate(`(module + (import "test" "import" (func)) + (memory i64 0xFFFF_FFFF_FFFF_FFFF (pagesize 1)))`), + `unknown import`, +); + +// ./test/core/custom-page-sizes/memory_max_i64.wast:25 +assert_unlinkable( + () => instantiate(`(module + (import "test" "unknown" (func)) + (memory i64 0x1_0000_0000_0000 (pagesize 65536)))`), + `unknown import`, +); + +// ./test/core/custom-page-sizes/memory_max_i64.wast:37 +assert_malformed( + () => instantiate(`(memory i64 0x1_0000_0000_0000_0000 (pagesize 1)) `), + `constant out of range`, +); + +// ./test/core/custom-page-sizes/memory_max_i64.wast:42 +assert_invalid( + () => instantiate(`(module + (memory i64 0x1_0000_0000_0001 (pagesize 65536)))`), + `memory size must be at most`, +);