22
33const {
44 ArrayPrototypePush,
5+ BigInt,
56 Boolean,
67 FunctionPrototypeCall,
78 JSONParse,
@@ -12,13 +13,17 @@ const {
1213 SafeMap,
1314 SafeSet,
1415 SafeWeakMap,
16+ StringFromCharCode,
17+ StringFromCodePoint,
1518 StringPrototypeIncludes,
1619 StringPrototypeReplaceAll,
1720 StringPrototypeSlice,
1821 StringPrototypeStartsWith,
1922 globalThis : { WebAssembly } ,
2023} = primordials ;
2124
25+ const { Buffer : { from : BufferFrom } } = require ( 'buffer' ) ;
26+
2227const {
2328 compileFunctionForCJSLoader,
2429} = internalBinding ( 'contextify' ) ;
@@ -455,6 +460,17 @@ translators.set('wasm', async function(url, source) {
455460 if ( impt . kind === 'global' ) {
456461 ArrayPrototypePush ( wasmGlobalImports , impt ) ;
457462 }
463+ // Prefix reservations per https://webassembly.github.io/esm-integration/js-api/index.html#parse-a-webassembly-module.
464+ if ( impt . module . startsWith ( 'wasm-js:' ) ) {
465+ throw new WebAssembly . LinkError ( `Invalid Wasm import "${ impt . module } " in ${ url } ` ) ;
466+ }
467+ // wasm:js-string polyfill is being applied
468+ if ( impt . module === 'wasm:js-string' ) {
469+ continue ;
470+ }
471+ if ( impt . name . startsWith ( 'wasm:' ) || impt . name . startsWith ( 'wasm-js:' ) ) {
472+ throw new WebAssembly . LinkError ( `Invalid Wasm import name "${ impt . module } " in ${ url } ` ) ;
473+ }
458474 importsList . add ( impt . module ) ;
459475 }
460476
@@ -464,6 +480,9 @@ translators.set('wasm', async function(url, source) {
464480 if ( expt . kind === 'global' ) {
465481 wasmGlobalExports . add ( expt . name ) ;
466482 }
483+ if ( expt . name . startsWith ( 'wasm:' ) || expt . name . startsWith ( 'wasm-js:' ) ) {
484+ throw new WebAssembly . LinkError ( `Invalid Wasm export name "${ expt . name } " in ${ url } ` ) ;
485+ }
467486 exportsList . add ( expt . name ) ;
468487 }
469488
@@ -486,9 +505,14 @@ translators.set('wasm', async function(url, source) {
486505 reflect . imports [ impt ] = wrappedModule ;
487506 }
488507 }
508+
489509 // In cycles importing unexecuted Wasm, wasmInstance will be undefined, which will fail during
490510 // instantiation, since all bindings will be in the Temporal Deadzone (TDZ).
491- const { exports } = new WebAssembly . Instance ( compiled , reflect . imports ) ;
511+ const { exports } = new WebAssembly . Instance ( compiled , {
512+ ...reflect . imports ,
513+ // Provide a polyfill for js string builtins
514+ 'wasm:js-string' : wasmJSStringBuiltinsPolyfill ,
515+ } ) ;
492516 wasmInstances . set ( module . getNamespace ( ) , exports ) ;
493517 for ( const expt of exportsList ) {
494518 let val = exports [ expt ] ;
@@ -524,3 +548,147 @@ translators.set('module-typescript', function(url, source) {
524548 debug ( `Translating TypeScript ${ url } ` ) ;
525549 return FunctionPrototypeCall ( translators . get ( 'module' ) , this , url , code , false ) ;
526550} ) ;
551+
552+ // Helper binary:
553+ // (module
554+ // (type $array_i16 (array (mut i16)))
555+ // (func $createArrayMutI16 (param $size i32) (result anyref)
556+ // (local.get $size)
557+ // (array.new_default $array_i16)
558+ // )
559+ // (func $arrayLength (param $arr arrayref) (result i32)
560+ // (local.get $arr)
561+ // (array.len)
562+ // )
563+ // (func $arraySet (param $arr (ref null $array_i16)) (param $index i32) (param $value i32)
564+ // (local.get $arr)
565+ // (local.get $index)
566+ // (local.get $value)
567+ // (array.set $array_i16)
568+ // )
569+ // (func $arrayGet (param $arr (ref null $array_i16)) (param $index i32) (result i32)
570+ // (local.get $arr)
571+ // (local.get $index)
572+ // (array.get_u $array_i16)
573+ // )
574+ // (export "createArrayMutI16" (func $createArrayMutI16))
575+ // (export "arrayLength" (func $arrayLength))
576+ // (export "arraySet" (func $arraySet))
577+ // (export "arrayGet" (func $arrayGet))
578+ // )
579+ let helperExports ;
580+ function loadHelperBinary ( ) {
581+ if ( ! helperExports ) {
582+ const module = new WebAssembly . Module ( BufferFrom ( 'AGFzbQEAAAABHAVedwFgAX8BbmABagF/YANjAH9/AGACYwB/AX8DBQQBAgMEBz' +
583+ 'kEEWNyZWF0ZUFycmF5TXV0STE2AAALYXJyYXlMZW5ndGgAAQhhcnJheVNldAACCGFycmF5R2V0AAMKJgQHACAA+wcACwYAIAD7DwsLACAAIAE' +
584+ 'gAvsOAAsJACAAIAH7DQALAH8EbmFtZQE1BAARY3JlYXRlQXJyYXlNdXRJMTYBC2FycmF5TGVuZ3RoAghhcnJheVNldAMIYXJyYXlHZXQCMwQA' +
585+ 'AQAEc2l6ZQEBAANhcnICAwADYXJyAQVpbmRleAIFdmFsdWUDAgADYXJyAQVpbmRleAQMAQAJYXJyYXlfaTE2' , 'base64' ) ) ;
586+ ( { exports : helperExports } = new WebAssembly . Instance ( module ) ) ;
587+ }
588+ }
589+
590+ function throwIfNotString ( a ) {
591+ if ( typeof a !== 'string' ) {
592+ throw new WebAssembly . RuntimeError ( ) ;
593+ }
594+ }
595+
596+ const wasmJSStringBuiltinsPolyfill = {
597+ test : ( string ) => {
598+ if ( string === null || typeof string !== 'string' ) {
599+ return 0 ;
600+ }
601+ return 1 ;
602+ } ,
603+ cast : ( string ) => {
604+ throwIfNotString ( string ) ;
605+ return string ;
606+ } ,
607+ fromCharCodeArray : ( array , arrayStart , arrayCount ) => {
608+ loadHelperBinary ( ) ;
609+ arrayStart >>>= 0 ;
610+ arrayCount >>>= 0 ;
611+ const length = helperExports . arrayLength ( array ) ;
612+ if ( BigInt ( arrayStart ) + BigInt ( arrayCount ) > BigInt ( length ) ) {
613+ throw new WebAssembly . RuntimeError ( ) ;
614+ }
615+ let result = '' ;
616+ for ( let i = arrayStart ; i < arrayStart + arrayCount ; i ++ ) {
617+ result += StringFromCharCode ( helperExports . arrayGet ( array , i ) ) ;
618+ }
619+ return result ;
620+ } ,
621+ intoCharCodeArray : ( string , arr , arrayStart ) => {
622+ loadHelperBinary ( ) ;
623+ arrayStart >>>= 0 ;
624+ throwIfNotString ( string ) ;
625+ const arrLength = helperExports . arrayLength ( arr ) ;
626+ const stringLength = string . length ;
627+ if ( BigInt ( arrayStart ) + BigInt ( stringLength ) > BigInt ( arrLength ) ) {
628+ throw new WebAssembly . RuntimeError ( ) ;
629+ }
630+ for ( let i = 0 ; i < stringLength ; i ++ ) {
631+ helperExports . arraySet ( arr , arrayStart + i , string [ i ] . charCodeAt ( 0 ) ) ;
632+ }
633+ return stringLength ;
634+ } ,
635+ fromCharCode : ( charCode ) => {
636+ charCode >>>= 0 ;
637+ return StringFromCharCode ( charCode ) ;
638+ } ,
639+ fromCodePoint : ( codePoint ) => {
640+ codePoint >>>= 0 ;
641+ return StringFromCodePoint ( codePoint ) ;
642+ } ,
643+ charCodeAt : ( string , stringIndex ) => {
644+ stringIndex >>>= 0 ;
645+ throwIfNotString ( string ) ;
646+ if ( stringIndex >= string . length ) {
647+ throw new WebAssembly . RuntimeError ( ) ;
648+ }
649+ return string . charCodeAt ( stringIndex ) ;
650+ } ,
651+ codePointAt : ( string , stringIndex ) => {
652+ stringIndex >>>= 0 ;
653+ throwIfNotString ( string ) ;
654+ if ( stringIndex >= string . length ) {
655+ throw new WebAssembly . RuntimeError ( ) ;
656+ }
657+ return string . codePointAt ( stringIndex ) ;
658+ } ,
659+ length : ( string ) => {
660+ throwIfNotString ( string ) ;
661+ return string . length ;
662+ } ,
663+ concat : ( stringA , stringB ) => {
664+ throwIfNotString ( stringA ) ;
665+ throwIfNotString ( stringB ) ;
666+ return stringA + stringB ;
667+ } ,
668+ substring : ( string , startIndex , endIndex ) => {
669+ startIndex >>>= 0 ;
670+ endIndex >>>= 0 ;
671+ throwIfNotString ( string ) ;
672+ if ( startIndex > string . length || endIndex > string . length || endIndex < startIndex ) {
673+ return '' ;
674+ }
675+ return string . substring ( startIndex , endIndex ) ;
676+ } ,
677+ equals : ( stringA , stringB ) => {
678+ if ( stringA !== null ) {
679+ throwIfNotString ( stringA ) ;
680+ }
681+ if ( stringB !== null ) {
682+ throwIfNotString ( stringB ) ;
683+ }
684+ return stringA === stringB ;
685+ } ,
686+ compare : ( stringA , stringB ) => {
687+ throwIfNotString ( stringA ) ;
688+ throwIfNotString ( stringB ) ;
689+ if ( stringA < stringB ) {
690+ return - 1 ;
691+ }
692+ return stringA === stringB ? 0 : 1 ;
693+ } ,
694+ } ;
0 commit comments