@@ -4,29 +4,41 @@ import { asbindInstantiate, asbindInstantiateSync } from "./instantiate";
4
4
import { bindImportFunction , bindExportFunction } from "./bind-function" ;
5
5
import { isReservedExportKey } from "./reserved-export-keys" ;
6
6
7
- // Need to traverse the importObject and bind all import functions
8
- const traverseObjectAndRunCallbackForFunctions = (
9
- baseObject ,
10
- keys ,
11
- callback
12
- ) => {
13
- if ( ! baseObject ) {
14
- return ;
7
+ const SECTION_NAME = "as-bind_bindings" ;
8
+
9
+ // Basically a deep-copy, but can be limited in levels.
10
+ function copyObject ( obj , { depth = Number . POSITIVE_INFINITY } = { } ) {
11
+ if ( depth <= 0 || ! obj || typeof obj !== "object" ) {
12
+ return obj ;
15
13
}
14
+ return Object . fromEntries (
15
+ Object . entries ( obj ) . map ( ( [ key , val ] ) => [
16
+ key ,
17
+ copyObject ( val , { depth : depth - 1 } )
18
+ ] )
19
+ ) ;
20
+ }
16
21
17
- Object . keys ( baseObject ) . forEach ( baseObjectKey => {
18
- if ( typeof baseObject [ baseObjectKey ] === "function" ) {
19
- // Call the callback
20
- callback ( baseObject , keys , baseObjectKey ) ;
21
- } else if ( typeof baseObject [ baseObjectKey ] === "object" ) {
22
- traverseObjectAndRunCallbackForFunctions (
23
- baseObject [ baseObjectKey ] ,
24
- [ ...keys , baseObjectKey ] ,
25
- callback
26
- ) ;
22
+ async function compileStreaming ( source ) {
23
+ source = await Promise . resolve ( source ) ;
24
+ if ( typeof Response !== "undefined" && source instanceof Response ) {
25
+ if ( WebAssembly . compileStreaming ) {
26
+ return WebAssembly . compileStreaming ( source ) ;
27
27
}
28
- } ) ;
29
- } ;
28
+ source = await source . arrayBuffer ( ) ;
29
+ }
30
+ return WebAssembly . compile ( source ) ;
31
+ }
32
+
33
+ function extractTypeDescriptor ( module ) {
34
+ const sections = WebAssembly . Module . customSections ( module , SECTION_NAME ) ;
35
+ const str = new TextDecoder ( "utf8" ) . decode ( new Uint8Array ( sections [ 0 ] ) ) ;
36
+ try {
37
+ return JSON . parse ( str ) ;
38
+ } catch ( e ) {
39
+ throw Error ( `Couldn’t decode type descriptor: ${ e . message } ` ) ;
40
+ }
41
+ }
30
42
31
43
export default class AsbindInstance {
32
44
constructor ( ) {
@@ -35,136 +47,94 @@ export default class AsbindInstance {
35
47
this . importObject = { } ;
36
48
}
37
49
38
- async _instantiate ( source , importObject ) {
39
- // Bind our import function
40
- this . _instantiateBindImportFunctions ( importObject ) ;
41
-
42
- // Instantiate the module through the loader
43
- const unboundExports = await asbindInstantiate ( source , this . importObject ) ;
44
-
45
- // Bind our unbound exports
46
- this . _instantiateBindUnboundExports ( unboundExports ) ;
47
- }
48
-
49
- _instantiateSync ( source , importObject ) {
50
- // Bind our import function
51
- this . _instantiateBindImportFunctions ( importObject ) ;
52
-
53
- // Instantiate the module through the loader
54
- const unboundExports = asbindInstantiateSync ( source , this . importObject ) ;
55
-
56
- // Bind our unbound exports
57
- this . _instantiateBindUnboundExports ( unboundExports ) ;
50
+ getTypeId ( typeName ) {
51
+ if ( typeName in this . typeDescriptor . typeIds ) {
52
+ return this . typeDescriptor . typeIds [ typeName ] . id ;
53
+ }
54
+ throw Error ( `Unknown type ${ JSON . stringify ( typeName ) } ` ) ;
58
55
}
59
56
60
- _instantiateBindImportFunctions ( importObject ) {
61
- // Set our import object, as we will need it to store type caching
62
- this . importObject = importObject ;
63
-
64
- // Need to traverse the importObject and bind all import functions
65
- traverseObjectAndRunCallbackForFunctions (
66
- this . importObject ,
67
- [ ] ,
68
- ( baseObject , keys , baseObjectKey ) => {
69
- // Wrap the original key, but then expose a new key for the unbound import
70
- let importFunction = baseObject [ baseObjectKey ] ;
71
- baseObject [ `__asbind_unbound_${ baseObjectKey } ` ] = importFunction ;
72
- baseObject [ baseObjectKey ] = bindImportFunction (
73
- this ,
74
- this . importObject ,
75
- [ ...keys , baseObjectKey ]
76
- ) ;
77
- }
78
- ) ;
57
+ getTypeSize ( typeName ) {
58
+ if ( typeName in this . typeDescriptor . typeIds ) {
59
+ return this . typeDescriptor . typeIds [ typeName ] . byteSize ;
60
+ }
61
+ throw Error ( `Unknown type ${ JSON . stringify ( typeName ) } ` ) ;
79
62
}
80
63
81
- _instantiateBindUnboundExports ( unboundExports ) {
82
- // Set our unbound exports
83
- this . unboundExports = unboundExports ;
84
-
85
- // Ensure that the AS Wasm was built with the as-bind entryfile
64
+ _validate ( ) {
65
+ if (
66
+ ! WebAssembly . Module . exports ( this . module ) . find ( exp => exp . name === "__new" )
67
+ ) {
68
+ throw Error (
69
+ "The AssemblyScript wasm module was not built with --exportRuntime, which is required."
70
+ ) ;
71
+ }
86
72
if (
87
- ! this . unboundExports . __asbind_entryfile_flag ||
88
- this . unboundExports . __asbind_entryfile_flag === 0
73
+ WebAssembly . Module . customSections ( this . module , SECTION_NAME ) . length !== 1
89
74
) {
90
75
throw new Error (
91
- "as-bind: The instantiated AssemblyScript wasm module was not built with the as-bind entryfile. Please see the as-bind documentation (Quick Start), and rebuild your AssemblyScript wasm module ."
76
+ "The AssemblyScript wasm module was not built with the as-bind transform ."
92
77
) ;
93
78
}
94
-
95
- // Wrap appropriate the appropriate export functions
96
- this . exports = { } ;
97
- Object . keys ( this . unboundExports ) . forEach ( unboundExportKey => {
98
- if (
99
- typeof this . unboundExports [ unboundExportKey ] === "function" &&
100
- ! isReservedExportKey ( unboundExportKey )
101
- ) {
102
- // Wrap the export
103
- this . exports [ unboundExportKey ] = bindExportFunction (
104
- this ,
105
- unboundExportKey
106
- ) ;
107
- } else {
108
- this . exports [ unboundExportKey ] = this . unboundExports [ unboundExportKey ] ;
109
- }
110
- } ) ;
111
79
}
112
80
113
- enableExportFunctionTypeCaching ( ) {
114
- Object . keys ( this . exports ) . forEach ( exportKey => {
115
- if ( this . exports [ exportKey ] ) {
116
- this . exports [ exportKey ] . shouldCacheTypes = true ;
117
- }
118
- } ) ;
119
- }
81
+ async _instantiate ( source , importObject ) {
82
+ this . module = await compileStreaming ( source ) ;
120
83
121
- disableExportFunctionTypeCaching ( ) {
122
- Object . keys ( this . exports ) . forEach ( exportKey => {
123
- if ( this . exports [ exportKey ] ) {
124
- this . exports [ exportKey ] . shouldCacheTypes = false ;
125
- }
126
- } ) ;
84
+ this . _validate ( ) ;
85
+ this . typeDescriptor = extractTypeDescriptor ( this . module ) ;
86
+ this . _instantiateBindImportFunctions ( importObject ) ;
87
+ // Instantiate the module through the loader
88
+ this . loadedModule = await asbindInstantiate ( this . module , this . importObject ) ;
89
+ this . _instantiateBindUnboundExports ( ) ;
127
90
}
128
91
129
- enableExportFunctionUnsafeReturnValue ( ) {
130
- Object . keys ( this . exports ) . forEach ( exportKey => {
131
- if ( this . exports [ exportKey ] ) {
132
- this . exports [ exportKey ] . unsafeReturnValue = true ;
133
- }
134
- } ) ;
135
- }
92
+ _instantiateSync ( source , importObject ) {
93
+ this . module = new WebAssembly . Module ( source ) ;
136
94
137
- disableExportFunctionUnsafeReturnValue ( ) {
138
- Object . keys ( this . exports ) . forEach ( exportKey => {
139
- if ( this . exports [ exportKey ] ) {
140
- this . exports [ exportKey ] . unsafeReturnValue = false ;
141
- }
142
- } ) ;
95
+ this . _validate ( ) ;
96
+ this . typeDescriptor = extractTypeDescriptor ( this . module ) ;
97
+ this . _instantiateBindImportFunctions ( importObject ) ;
98
+ this . loadedModule = asbindInstantiateSync ( this . module , this . importObject ) ;
99
+ this . _instantiateBindUnboundExports ( ) ;
143
100
}
144
101
145
- enableImportFunctionTypeCaching ( ) {
146
- // Need to traverse the importObject and bind all import functions
147
- traverseObjectAndRunCallbackForFunctions (
148
- this . importObject ,
149
- [ ] ,
150
- ( baseObject , keys , baseObjectKey ) => {
151
- // Wrap the original key, but then expose a new key for the unbound import
152
- let importFunction = baseObject [ baseObjectKey ] ;
153
- importFunction . shouldCacheTypes = true ;
102
+ _instantiateBindImportFunctions ( importObject ) {
103
+ this . importObject = copyObject ( importObject , { depth : 2 } ) ;
104
+
105
+ for ( const [ moduleName , moduleDescriptor ] of Object . entries (
106
+ this . typeDescriptor . importedFunctions
107
+ ) ) {
108
+ for ( const [ importedFunctionName , descriptor ] of Object . entries (
109
+ moduleDescriptor
110
+ ) ) {
111
+ this . importObject [ moduleName ] [
112
+ `__asbind_unbound_${ importedFunctionName } `
113
+ ] = importObject [ moduleName ] [ importedFunctionName ] ;
114
+ this . importObject [ moduleName ] [
115
+ importedFunctionName
116
+ ] = bindImportFunction (
117
+ this ,
118
+ importObject [ moduleName ] [ importedFunctionName ] ,
119
+ descriptor
120
+ ) ;
154
121
}
155
- ) ;
122
+ }
156
123
}
157
124
158
- disableImportFunctionTypeCaching ( ) {
159
- // Need to traverse the importObject and bind all import functions
160
- traverseObjectAndRunCallbackForFunctions (
161
- this . importObject ,
162
- [ ] ,
163
- ( baseObject , keys , baseObjectKey ) => {
164
- // Wrap the original key, but then expose a new key for the unbound import
165
- let importFunction = baseObject [ baseObjectKey ] ;
166
- importFunction . shouldCacheTypes = false ;
167
- }
168
- ) ;
125
+ _instantiateBindUnboundExports ( ) {
126
+ // Wrap appropriate the appropriate export functions
127
+ const unboundExports = this . loadedModule . exports ;
128
+ this . exports = copyObject ( unboundExports , { depth : 1 } ) ;
129
+
130
+ for ( const [ exportedFunctionName , descriptor ] of Object . entries (
131
+ this . typeDescriptor . exportedFunctions
132
+ ) ) {
133
+ this . exports [ exportedFunctionName ] = bindExportFunction (
134
+ this ,
135
+ unboundExports [ exportedFunctionName ] ,
136
+ descriptor
137
+ ) ;
138
+ }
169
139
}
170
140
}
0 commit comments