basic.any.js (9709B)
1 // META: global=window,dedicatedworker,jsshell 2 // META: script=/wasm/jsapi/assertions.js 3 // META: script=/wasm/jsapi/wasm-module-builder.js 4 // META: script=/wasm/jsapi/js-string/polyfill.js 5 6 // Generate two sets of exports, one from a polyfill implementation and another 7 // from the builtins provided by the host. 8 let polyfillExports; 9 let builtinExports; 10 setup(() => { 11 // Compile a module that exports a function for each builtin that will call 12 // it. We could just generate a module that re-exports the builtins, but that 13 // would not catch any special codegen that could happen when direct calling 14 // a known builtin function from wasm. 15 const builder = new WasmModuleBuilder(); 16 const arrayIndex = builder.addArray(kWasmI16, true, kNoSuperType, true); 17 const builtins = [ 18 { 19 name: "test", 20 params: [kWasmExternRef], 21 results: [kWasmI32], 22 }, 23 { 24 name: "cast", 25 params: [kWasmExternRef], 26 results: [wasmRefType(kWasmExternRef)], 27 }, 28 { 29 name: "fromCharCodeArray", 30 params: [wasmRefNullType(arrayIndex), kWasmI32, kWasmI32], 31 results: [wasmRefType(kWasmExternRef)], 32 }, 33 { 34 name: "intoCharCodeArray", 35 params: [kWasmExternRef, wasmRefNullType(arrayIndex), kWasmI32], 36 results: [kWasmI32], 37 }, 38 { 39 name: "fromCharCode", 40 params: [kWasmI32], 41 results: [wasmRefType(kWasmExternRef)], 42 }, 43 { 44 name: "fromCodePoint", 45 params: [kWasmI32], 46 results: [wasmRefType(kWasmExternRef)], 47 }, 48 { 49 name: "charCodeAt", 50 params: [kWasmExternRef, kWasmI32], 51 results: [kWasmI32], 52 }, 53 { 54 name: "codePointAt", 55 params: [kWasmExternRef, kWasmI32], 56 results: [kWasmI32], 57 }, 58 { 59 name: "length", 60 params: [kWasmExternRef], 61 results: [kWasmI32], 62 }, 63 { 64 name: "concat", 65 params: [kWasmExternRef, kWasmExternRef], 66 results: [wasmRefType(kWasmExternRef)], 67 }, 68 { 69 name: "substring", 70 params: [kWasmExternRef, kWasmI32, kWasmI32], 71 results: [wasmRefType(kWasmExternRef)], 72 }, 73 { 74 name: "equals", 75 params: [kWasmExternRef, kWasmExternRef], 76 results: [kWasmI32], 77 }, 78 { 79 name: "compare", 80 params: [kWasmExternRef, kWasmExternRef], 81 results: [kWasmI32], 82 }, 83 ]; 84 85 // Add a function type for each builtin 86 for (let builtin of builtins) { 87 builtin.type = builder.addType({ 88 params: builtin.params, 89 results: builtin.results 90 }); 91 } 92 93 // Add an import for each builtin 94 for (let builtin of builtins) { 95 builtin.importFuncIndex = builder.addImport( 96 "wasm:js-string", 97 builtin.name, 98 builtin.type); 99 } 100 101 // Generate an exported function to call the builtin 102 for (let builtin of builtins) { 103 let func = builder.addFunction(builtin.name + "Imp", builtin.type); 104 func.addLocals(builtin.params.length); 105 let body = []; 106 for (let i = 0; i < builtin.params.length; i++) { 107 body.push(kExprLocalGet); 108 body.push(...wasmSignedLeb(i)); 109 } 110 body.push(kExprCallFunction); 111 body.push(...wasmSignedLeb(builtin.importFuncIndex)); 112 func.addBody(body); 113 func.exportAs(builtin.name); 114 } 115 116 const buffer = builder.toBuffer(); 117 118 // Instantiate this module using the builtins from the host 119 const builtinModule = new WebAssembly.Module(buffer, { 120 builtins: ["js-string"] 121 }); 122 const builtinInstance = new WebAssembly.Instance(builtinModule, {}); 123 builtinExports = builtinInstance.exports; 124 125 // Instantiate this module using the polyfill module 126 const polyfillModule = new WebAssembly.Module(buffer); 127 const polyfillInstance = new WebAssembly.Instance(polyfillModule, { 128 "wasm:js-string": polyfillImports 129 }); 130 polyfillExports = polyfillInstance.exports; 131 }); 132 133 // A helper function to assert that the behavior of two functions are the 134 // same. 135 function assert_same_behavior(funcA, funcB, ...params) { 136 let resultA; 137 let errA = null; 138 try { 139 resultA = funcA(...params); 140 } catch (err) { 141 errA = err; 142 } 143 144 let resultB; 145 let errB = null; 146 try { 147 resultB = funcB(...params); 148 } catch (err) { 149 errB = err; 150 } 151 152 if (errA || errB) { 153 assert_equals(errA === null, errB === null, errA ? errA.message : errB.message); 154 assert_equals(Object.getPrototypeOf(errA), Object.getPrototypeOf(errB)); 155 } 156 assert_equals(resultA, resultB); 157 158 if (errA) { 159 throw errA; 160 } 161 return resultA; 162 } 163 164 function assert_throws_if(func, shouldThrow, constructor) { 165 let error = null; 166 try { 167 func(); 168 } catch (e) { 169 error = e; 170 } 171 assert_equals(error !== null, shouldThrow, "shouldThrow mismatch"); 172 if (shouldThrow && error !== null) { 173 assert_true(error instanceof constructor); 174 } 175 } 176 177 // Constant values used in the tests below 178 const testStrings = [ 179 "", 180 "a", 181 "1", 182 "ab", 183 "hello, world", 184 "\n", 185 "☺", 186 "☺☺", 187 String.fromCodePoint(0x10000, 0x10001) 188 ]; 189 const testCharCodes = [1, 2, 3, 10, 0x7f, 0xff, 0xfffe, 0xffff]; 190 const testCodePoints = [1, 2, 3, 10, 0x7f, 0xff, 0xfffe, 0xffff, 0x10000, 0x10001]; 191 const testExternRefValues = [ 192 null, 193 undefined, 194 true, 195 false, 196 {x:1337}, 197 ["abracadabra"], 198 13.37, 199 -0, 200 0x7fffffff + 0.1, 201 -0x7fffffff - 0.1, 202 0x80000000 + 0.1, 203 -0x80000000 - 0.1, 204 0xffffffff + 0.1, 205 -0xffffffff - 0.1, 206 Number.EPSILON, 207 Number.MAX_SAFE_INTEGER, 208 Number.MIN_SAFE_INTEGER, 209 Number.MIN_VALUE, 210 Number.MAX_VALUE, 211 Number.NaN, 212 "hi", 213 37n, 214 new Number(42), 215 new Boolean(true), 216 Symbol("status"), 217 () => 1337, 218 ]; 219 220 // Test that `test` and `cast` work on various JS values. Run all the 221 // other builtins and assert that they also perform equivalent type 222 // checks. 223 test(() => { 224 for (let a of testExternRefValues) { 225 let isString = assert_same_behavior( 226 builtinExports['test'], 227 polyfillExports['test'], 228 a 229 ); 230 231 assert_throws_if(() => assert_same_behavior( 232 builtinExports['cast'], 233 polyfillExports['cast'], 234 a 235 ), !isString, WebAssembly.RuntimeError); 236 237 let arrayMutI16 = helperExports.createArrayMutI16(10); 238 assert_throws_if(() => assert_same_behavior( 239 builtinExports['intoCharCodeArray'], 240 polyfillExports['intoCharCodeArray'], 241 a, arrayMutI16, 0 242 ), !isString, WebAssembly.RuntimeError); 243 244 assert_throws_if(() => assert_same_behavior( 245 builtinExports['charCodeAt'], 246 polyfillExports['charCodeAt'], 247 a, 0 248 ), !isString, WebAssembly.RuntimeError); 249 250 assert_throws_if(() => assert_same_behavior( 251 builtinExports['codePointAt'], 252 polyfillExports['codePointAt'], 253 a, 0 254 ), !isString, WebAssembly.RuntimeError); 255 256 assert_throws_if(() => assert_same_behavior( 257 builtinExports['length'], 258 polyfillExports['length'], 259 a 260 ), !isString, WebAssembly.RuntimeError); 261 262 assert_throws_if(() => assert_same_behavior( 263 builtinExports['concat'], 264 polyfillExports['concat'], 265 a, a 266 ), !isString, WebAssembly.RuntimeError); 267 268 assert_throws_if(() => assert_same_behavior( 269 builtinExports['substring'], 270 polyfillExports['substring'], 271 a, 0, 0 272 ), !isString, WebAssembly.RuntimeError); 273 274 assert_throws_if(() => assert_same_behavior( 275 builtinExports['equals'], 276 polyfillExports['equals'], 277 a, a 278 ), a !== null && !isString, WebAssembly.RuntimeError); 279 280 assert_throws_if(() => assert_same_behavior( 281 builtinExports['compare'], 282 polyfillExports['compare'], 283 a, a 284 ), !isString, WebAssembly.RuntimeError); 285 } 286 }); 287 288 // Test that `fromCharCode` works on various char codes 289 test(() => { 290 for (let a of testCharCodes) { 291 assert_same_behavior( 292 builtinExports['fromCharCode'], 293 polyfillExports['fromCharCode'], 294 a 295 ); 296 } 297 }); 298 299 // Test that `fromCodePoint` works on various code points 300 test(() => { 301 for (let a of testCodePoints) { 302 assert_same_behavior( 303 builtinExports['fromCodePoint'], 304 polyfillExports['fromCodePoint'], 305 a 306 ); 307 } 308 }); 309 310 // Perform tests on various strings 311 test(() => { 312 for (let a of testStrings) { 313 let length = assert_same_behavior( 314 builtinExports['length'], 315 polyfillExports['length'], 316 a 317 ); 318 319 for (let i = 0; i < length; i++) { 320 let charCode = assert_same_behavior( 321 builtinExports['charCodeAt'], 322 polyfillExports['charCodeAt'], 323 a, i 324 ); 325 } 326 327 for (let i = 0; i < length; i++) { 328 let charCode = assert_same_behavior( 329 builtinExports['codePointAt'], 330 polyfillExports['codePointAt'], 331 a, i 332 ); 333 } 334 335 let arrayMutI16 = helperExports.createArrayMutI16(length); 336 assert_same_behavior( 337 builtinExports['intoCharCodeArray'], 338 polyfillExports['intoCharCodeArray'], 339 a, arrayMutI16, 0 340 ); 341 342 assert_same_behavior( 343 builtinExports['fromCharCodeArray'], 344 polyfillExports['fromCharCodeArray'], 345 arrayMutI16, 0, length 346 ); 347 348 for (let i = 0; i < length; i++) { 349 for (let j = 0; j < length; j++) { 350 assert_same_behavior( 351 builtinExports['substring'], 352 polyfillExports['substring'], 353 a, i, j 354 ); 355 } 356 } 357 } 358 }); 359 360 // Test various binary operations 361 test(() => { 362 for (let a of testStrings) { 363 for (let b of testStrings) { 364 assert_same_behavior( 365 builtinExports['concat'], 366 polyfillExports['concat'], 367 a, b 368 ); 369 370 assert_same_behavior( 371 builtinExports['equals'], 372 polyfillExports['equals'], 373 a, b 374 ); 375 376 assert_same_behavior( 377 builtinExports['compare'], 378 polyfillExports['compare'], 379 a, b 380 ); 381 } 382 } 383 });