basic.js (11619B)
1 let testModule = `(module 2 (type $arrayMutI16 (array (mut i16))) 3 4 (func $testImp 5 (import "wasm:js-string" "test") 6 (param externref) 7 (result i32) 8 ) 9 (func $castImp 10 (import "wasm:js-string" "cast") 11 (param externref) 12 (result (ref extern)) 13 ) 14 (func $fromCharCodeArrayImp 15 (import "wasm:js-string" "fromCharCodeArray") 16 (param (ref null $arrayMutI16) i32 i32) 17 (result (ref extern)) 18 ) 19 (func $intoCharCodeArrayImp 20 (import "wasm:js-string" "intoCharCodeArray") 21 (param externref (ref null $arrayMutI16) i32) 22 (result i32) 23 ) 24 (func $fromCharCodeImp 25 (import "wasm:js-string" "fromCharCode") 26 (param i32) 27 (result (ref extern)) 28 ) 29 (func $fromCodePointImp 30 (import "wasm:js-string" "fromCodePoint") 31 (param i32) 32 (result (ref extern)) 33 ) 34 (func $charCodeAtImp 35 (import "wasm:js-string" "charCodeAt") 36 (param externref i32) 37 (result i32) 38 ) 39 (func $codePointAtImp 40 (import "wasm:js-string" "codePointAt") 41 (param externref i32) 42 (result i32) 43 ) 44 (func $lengthImp 45 (import "wasm:js-string" "length") 46 (param externref) 47 (result i32) 48 ) 49 (func $concatImp 50 (import "wasm:js-string" "concat") 51 (param externref externref) 52 (result (ref extern)) 53 ) 54 (func $substringImp 55 (import "wasm:js-string" "substring") 56 (param externref i32 i32) 57 (result (ref extern)) 58 ) 59 (func $equalsImp 60 (import "wasm:js-string" "equals") 61 (param externref externref) 62 (result i32) 63 ) 64 (func $compareImp 65 (import "wasm:js-string" "compare") 66 (param externref externref) 67 (result i32) 68 ) 69 70 (func $test (export "test") 71 (param externref) 72 (result i32) 73 local.get 0 74 call $testImp 75 ) 76 (func $cast (export "cast") 77 (param externref) 78 (result (ref extern)) 79 local.get 0 80 call $castImp 81 ) 82 (func $fromCharCodeArray (export "fromCharCodeArray") 83 (param (ref null $arrayMutI16) i32 i32) 84 (result (ref extern)) 85 local.get 0 86 local.get 1 87 local.get 2 88 call $fromCharCodeArrayImp 89 ) 90 (func $intoCharCodeArray (export "intoCharCodeArray") 91 (param externref (ref null $arrayMutI16) i32) 92 (result i32) 93 local.get 0 94 local.get 1 95 local.get 2 96 call $intoCharCodeArrayImp 97 ) 98 (func $fromCharCode (export "fromCharCode") 99 (param i32) 100 (result externref) 101 local.get 0 102 call $fromCharCodeImp 103 ) 104 (func $fromCodePoint (export "fromCodePoint") 105 (param i32) 106 (result externref) 107 local.get 0 108 call $fromCodePointImp 109 ) 110 (func $charCodeAt (export "charCodeAt") 111 (param externref i32) 112 (result i32) 113 local.get 0 114 local.get 1 115 call $charCodeAtImp 116 ) 117 (func $codePointAt (export "codePointAt") 118 (param externref i32) 119 (result i32) 120 local.get 0 121 local.get 1 122 call $codePointAtImp 123 ) 124 (func $length (export "length") 125 (param externref) 126 (result i32) 127 local.get 0 128 call $lengthImp 129 ) 130 (func $concat (export "concat") 131 (param externref externref) 132 (result externref) 133 local.get 0 134 local.get 1 135 call $concatImp 136 ) 137 (func $substring (export "substring") 138 (param externref i32 i32) 139 (result externref) 140 local.get 0 141 local.get 1 142 local.get 2 143 call $substringImp 144 ) 145 (func $equals (export "equals") 146 (param externref externref) 147 (result i32) 148 local.get 0 149 local.get 1 150 call $equalsImp 151 ) 152 (func $compare (export "compare") 153 (param externref externref) 154 (result i32) 155 local.get 0 156 local.get 1 157 call $compareImp 158 ) 159 )`; 160 161 let { 162 createArrayMutI16, 163 arrayLength, 164 arraySet, 165 arrayGet 166 } = wasmEvalText(`(module 167 (type $arrayMutI16 (array (mut i16))) 168 (func (export "createArrayMutI16") (param i32) (result anyref) 169 i32.const 0 170 local.get 0 171 array.new $arrayMutI16 172 ) 173 (func (export "arrayLength") (param arrayref) (result i32) 174 local.get 0 175 array.len 176 ) 177 (func (export "arraySet") (param (ref $arrayMutI16) i32 i32) 178 local.get 0 179 local.get 1 180 local.get 2 181 array.set $arrayMutI16 182 ) 183 (func (export "arrayGet") (param (ref $arrayMutI16) i32) (result i32) 184 local.get 0 185 local.get 1 186 array.get_u $arrayMutI16 187 ) 188 )`).exports; 189 190 function throwIfNotString(a) { 191 if (typeof a !== "string") { 192 throw new WebAssembly.RuntimeError(); 193 } 194 } 195 function throwIfNotStringOrNull(a) { 196 if (a !== null && typeof a !== "string") { 197 throw new WebAssembly.RuntimeError(); 198 } 199 } 200 let polyFillImports = { 201 test: (string) => { 202 if (string === null || 203 typeof string !== "string") { 204 return 0; 205 } 206 return 1; 207 }, 208 cast: (string) => { 209 if (string === null || 210 typeof string !== "string") { 211 throw new WebAssembly.RuntimeError(); 212 } 213 return string; 214 }, 215 fromCharCodeArray: (array, arrayStart, arrayEnd) => { 216 arrayStart >>>= 0; 217 arrayEnd >>>= 0; 218 if (array == null || 219 arrayStart > arrayEnd || 220 arrayEnd > arrayLength(array)) { 221 throw new WebAssembly.RuntimeError(); 222 } 223 let result = ''; 224 for (let i = arrayStart; i < arrayEnd; i++) { 225 result += String.fromCharCode(arrayGet(array, i)); 226 } 227 return result; 228 }, 229 intoCharCodeArray: (string, arr, arrayStart) => { 230 arrayStart >>>= 0; 231 throwIfNotString(string); 232 if (arr == null) { 233 throw new WebAssembly.RuntimeError(); 234 } 235 let arrLength = arrayLength(arr); 236 let stringLength = string.length; 237 if (BigInt(arrayStart) + BigInt(stringLength) > BigInt(arrLength)) { 238 throw new WebAssembly.RuntimeError(); 239 } 240 for (let i = 0; i < stringLength; i++) { 241 arraySet(arr, arrayStart + i, string[i].charCodeAt(0)); 242 } 243 return stringLength; 244 }, 245 fromCharCode: (charCode) => { 246 charCode >>>= 0; 247 return String.fromCharCode(charCode); 248 }, 249 fromCodePoint: (codePoint) => { 250 codePoint >>>= 0; 251 return String.fromCodePoint(codePoint); 252 }, 253 charCodeAt: (string, stringIndex) => { 254 stringIndex >>>= 0; 255 throwIfNotString(string); 256 if (stringIndex >= string.length) 257 throw new WebAssembly.RuntimeError(); 258 return string.charCodeAt(stringIndex); 259 }, 260 codePointAt: (string, stringIndex) => { 261 stringIndex >>>= 0; 262 throwIfNotString(string); 263 if (stringIndex >= string.length) 264 throw new WebAssembly.RuntimeError(); 265 return string.codePointAt(stringIndex); 266 }, 267 length: (string) => { 268 throwIfNotString(string); 269 return string.length; 270 }, 271 concat: (stringA, stringB) => { 272 throwIfNotString(stringA); 273 throwIfNotString(stringB); 274 return stringA + stringB; 275 }, 276 substring: (string, startIndex, endIndex) => { 277 startIndex >>>= 0; 278 endIndex >>>= 0; 279 throwIfNotString(string); 280 if (startIndex > string.length || 281 endIndex < startIndex) { 282 return ""; 283 } 284 if (endIndex > string.length) { 285 endIndex = string.length; 286 } 287 return string.substring(startIndex, endIndex); 288 }, 289 equals: (stringA, stringB) => { 290 throwIfNotStringOrNull(stringA); 291 throwIfNotStringOrNull(stringB); 292 return stringA === stringB; 293 }, 294 compare: (stringA, stringB) => { 295 throwIfNotString(stringA); 296 throwIfNotString(stringB); 297 if (stringA < stringB) { 298 return -1; 299 } 300 return stringA === stringB ? 0 : 1; 301 }, 302 }; 303 304 function assertSameBehavior(funcA, funcB, ...params) { 305 let resultA; 306 let errA = null; 307 try { 308 resultA = funcA(...params); 309 } catch (err) { 310 errA = err; 311 } 312 313 let resultB; 314 let errB = null; 315 try { 316 resultB = funcB(...params); 317 } catch (err) { 318 errB = err; 319 } 320 321 if (errA || errB) { 322 assertEq(errA === null, errB === null, errA ? errA.message : errB.message); 323 assertEq(Object.getPrototypeOf(errA), Object.getPrototypeOf(errB)); 324 } 325 assertEq(resultA, resultB); 326 327 if (errA) { 328 throw errA; 329 } 330 return resultA; 331 } 332 333 let builtinExports = wasmEvalText(testModule, {}, {builtins: ["js-string"]}).exports; 334 let polyfillExports = wasmEvalText(testModule, { 'wasm:js-string': polyFillImports }).exports; 335 336 let testStrings = ["", "a", "1", "ab", "hello, world", "\n", "☺", "☺smiley", String.fromCodePoint(0x10000, 0x10001)]; 337 let testStringsAndNull = [...testStrings, null]; 338 let testCharCodes = [1, 2, 3, 10, 0x7f, 0xff, 0xfffe, 0xffff]; 339 let testCodePoints = [1, 2, 3, 10, 0x7f, 0xff, 0xfffe, 0xffff, 0x10000, 0x10001]; 340 341 for (let a of WasmExternrefValues) { 342 assertSameBehavior( 343 builtinExports['test'], 344 polyfillExports['test'], 345 a 346 ); 347 try { 348 assertSameBehavior( 349 builtinExports['cast'], 350 polyfillExports['cast'], 351 a 352 ); 353 } catch (err) { 354 assertEq(err instanceof WebAssembly.RuntimeError, true); 355 } 356 } 357 358 for (let a of testCharCodes) { 359 assertSameBehavior( 360 builtinExports['fromCharCode'], 361 polyfillExports['fromCharCode'], 362 a 363 ); 364 } 365 366 for (let a of testCodePoints) { 367 assertSameBehavior( 368 builtinExports['fromCodePoint'], 369 polyfillExports['fromCodePoint'], 370 a 371 ); 372 } 373 374 for (let a of testStrings) { 375 let length = assertSameBehavior( 376 builtinExports['length'], 377 polyfillExports['length'], 378 a 379 ); 380 381 for (let i = 0; i < length; i++) { 382 let charCode = assertSameBehavior( 383 builtinExports['charCodeAt'], 384 polyfillExports['charCodeAt'], 385 a, i 386 ); 387 } 388 for (let i = 0; i < length; i++) { 389 let charCode = assertSameBehavior( 390 builtinExports['codePointAt'], 391 polyfillExports['codePointAt'], 392 a, i 393 ); 394 } 395 396 let arrayMutI16 = createArrayMutI16(length); 397 assertSameBehavior( 398 builtinExports['intoCharCodeArray'], 399 polyfillExports['intoCharCodeArray'], 400 a, arrayMutI16, 0 401 ); 402 assertSameBehavior( 403 builtinExports['fromCharCodeArray'], 404 polyfillExports['fromCharCodeArray'], 405 arrayMutI16, 0, length 406 ); 407 408 for (let i = 0; i < length; i++) { 409 // The end parameter is interpreted as unsigned and is always clamped to 410 // the string length. This means that -1, and string.length + 1 are valid 411 // end indices. 412 for (let j = -1; j <= length + 1; j++) { 413 assertSameBehavior( 414 builtinExports['substring'], 415 polyfillExports['substring'], 416 a, i, j 417 ); 418 } 419 } 420 } 421 422 for (let a of testStrings) { 423 for (let b of testStrings) { 424 assertSameBehavior( 425 builtinExports['concat'], 426 polyfillExports['concat'], 427 a, b 428 ); 429 assertSameBehavior( 430 builtinExports['compare'], 431 polyfillExports['compare'], 432 a, b 433 ); 434 } 435 } 436 437 for (let a of testStringsAndNull) { 438 for (let b of testStringsAndNull) { 439 assertSameBehavior( 440 builtinExports['equals'], 441 polyfillExports['equals'], 442 a, b 443 ); 444 } 445 } 446 447 // fromCharCodeArray endIndex is an unsigned integer 448 { 449 let arrayMutI16 = createArrayMutI16(1); 450 assertErrorMessage(() => assertSameBehavior( 451 builtinExports['fromCharCodeArray'], 452 polyfillExports['fromCharCodeArray'], 453 arrayMutI16, 1, -1 454 ), WebAssembly.RuntimeError, /./); 455 } 456 457 // fromCharCodeArray is startIndex and endIndex, not a count 458 { 459 let arrayMutI16 = createArrayMutI16(1); 460 // Ask for [1, 1) to get an empty string. If misinterpreted as a count, this 461 // will result in a trap. 462 assertEq(assertSameBehavior( 463 builtinExports['fromCharCodeArray'], 464 polyfillExports['fromCharCodeArray'], 465 arrayMutI16, 1, 1 466 ), ""); 467 } 468 469 // fromCharCodeArray array is null 470 { 471 assertErrorMessage(() => assertSameBehavior( 472 builtinExports['fromCharCodeArray'], 473 polyfillExports['fromCharCodeArray'], 474 null, 0, 0 475 ), WebAssembly.RuntimeError, /./); 476 } 477 478 // intoCharCodeArray array is null 479 { 480 assertErrorMessage(() => assertSameBehavior( 481 builtinExports['intoCharCodeArray'], 482 polyfillExports['intoCharCodeArray'], 483 "test", null, 0, 484 ), WebAssembly.RuntimeError, /./); 485 }