@@ -127,6 +127,209 @@ abstract class ExportsWalker {
127
127
abstract visitNamespace ( element : Element ) : void ;
128
128
}
129
129
130
+ // TODO: Extract this into separate module, preferrable pluggable
131
+ export class NEARBindingsBuilder extends ExportsWalker {
132
+ private sb : string [ ] = [ ] ;
133
+
134
+ private typeMapping : { [ key : string ] : string } = {
135
+ "i32" : "Integer" ,
136
+ "String" : "String" ,
137
+ "Uint8Array" : "Uint8Array" ,
138
+ "bool" : "Boolean"
139
+ } ;
140
+
141
+ private nonNullableTypes = [ "i32" , "bool" ] ;
142
+
143
+ static build ( program : Program ) : string {
144
+ return new NEARBindingsBuilder ( program ) . build ( ) ;
145
+ }
146
+
147
+ visitGlobal ( element : Global ) : void {
148
+ // Do nothing
149
+ }
150
+
151
+ visitEnum ( element : Enum ) : void {
152
+ // Do nothing
153
+ }
154
+
155
+ visitFunction ( element : Function ) : void {
156
+ console . log ( "visitFunction: " + element . simpleName ) ;
157
+ let signature = element . signature ;
158
+
159
+ this . sb . push ( `export class __near_ArgsParser_${ element . simpleName } {
160
+ ` ) ;
161
+ let fields = [ ] ;
162
+ if ( signature . parameterNames ) {
163
+ for ( let i = 0 ; i < signature . parameterNames . length ; i ++ ) {
164
+ let paramName = signature . parameterNames [ i ] ;
165
+ let paramType = signature . parameterTypes [ i ] ;
166
+ fields . push ( {
167
+ simpleName : paramName ,
168
+ type : paramType
169
+ } ) ;
170
+ }
171
+ }
172
+ fields . forEach ( ( field ) => {
173
+ this . sb . push ( `__near_param_${ field . simpleName } : ${ field . type } ;` ) ;
174
+ } ) ;
175
+ this . generateBSONHandlerMethods ( "this.__near_param_" , fields ) ;
176
+ this . sb . push ( `}` ) ; // __near_ArgsParser
177
+
178
+ let returnType = signature . returnType . toString ( ) ;
179
+ this . sb . push ( `export function near_func_${ element . simpleName } (): void {
180
+ let bson = new Uint8Array(input_read_len());
181
+ input_read_into(bson.buffer.data);
182
+ let handler = new __near_ArgsParser_${ element . simpleName } ();
183
+ let decoder = new BSONDecoder<__near_ArgsParser_${ element . simpleName } >(handler);
184
+ decoder.deserialize(bson);` ) ;
185
+ if ( returnType != "void" ) {
186
+ this . sb . push ( `let result = ${ element . simpleName } (` ) ;
187
+ } else {
188
+ this . sb . push ( `${ element . simpleName } (` )
189
+ }
190
+ if ( signature . parameterNames ) {
191
+ let i = 0 ;
192
+ for ( let paramName of signature . parameterNames ) {
193
+ this . sb . push ( `handler.__near_param_${ paramName } ` ) ;
194
+ if ( i < signature . parameterNames . length ) {
195
+ this . sb . push ( "," )
196
+ }
197
+ i ++ ;
198
+ }
199
+ }
200
+ this . sb . push ( ");" ) ;
201
+
202
+ if ( returnType != "void" ) {
203
+ this . sb . push ( `
204
+ let encoder = new BSONEncoder();` ) ;
205
+ this . generateFieldEncoder ( returnType , "result" , "result" ) ;
206
+ this . sb . push ( `
207
+ return_value(near.bufferWithSize(encoder.serialize()).buffer.data);
208
+ ` ) ;
209
+ }
210
+
211
+ this . sb . push ( `}` ) ;
212
+ }
213
+
214
+ private generateBSONHandlerMethods ( valuePrefix : string , fields : any [ ] ) : void {
215
+ for ( let fieldType in this . typeMapping ) {
216
+ let setterType = this . typeMapping [ fieldType ] ;
217
+ this . sb . push ( `set${ setterType } (name: string, value: ${ fieldType } ): void {` ) ;
218
+ fields . forEach ( ( field ) => {
219
+ if ( field . type . toString ( ) == fieldType ) {
220
+ this . sb . push ( `if (name == "${ field . simpleName } ") { ${ valuePrefix } ${ field . simpleName } = value; return; }` ) ;
221
+ }
222
+ } ) ;
223
+ this . sb . push ( "}" ) ;
224
+ }
225
+ this . sb . push ( "setNull(name: string): void {" ) ;
226
+ fields . forEach ( ( field ) => {
227
+ this . sb . push ( `if (name == "${ field . simpleName } ") {
228
+ ${ valuePrefix } ${ field . simpleName } = <${ field . type . toString ( ) } >null;
229
+ }` ) ;
230
+ } ) ;
231
+ this . sb . push ( "}\n" ) ; // setNull
232
+
233
+ // TODO: Suport nested objects/arrays
234
+ // TODO: This needs some way to get current index in buffer (extract parser state into separte class?),
235
+ // TODO: so that we can call method to parse object recursively
236
+ // TODO: popObject() should also return false to exit nested parser?
237
+ this . sb . push ( `
238
+ pushObject(name: string): bool { return false; }
239
+ popObject(): void {}
240
+ pushArray(name: string): bool { return false; }
241
+ popArray(): void {}
242
+ ` ) ;
243
+ }
244
+
245
+ visitClass ( element : Class ) : void {
246
+ let className = element . simpleName ;
247
+ console . log ( "visitClass: " + className ) ;
248
+ this . sb . push ( `export function __near_encode_${ className } (
249
+ value: ${ className } ,
250
+ encoder: BSONEncoder): void {` ) ;
251
+ this . getFields ( element ) . forEach ( ( field ) => {
252
+ let fieldType = field . type . toString ( ) ;
253
+ let fieldName = field . simpleName ;
254
+ let sourceExpr = `value.${ fieldName } ` ;
255
+ this . generateFieldEncoder ( fieldType , fieldName , sourceExpr ) ;
256
+ } ) ;
257
+ this . sb . push ( "}" ) ; // __near_encode
258
+
259
+ this . sb . push ( `export class __near_BSONHandler_${ className } {
260
+ value: ${ className } = new ${ className } ();` ) ;
261
+ this . generateBSONHandlerMethods ( "this.value." , this . getFields ( element ) ) ;
262
+ this . sb . push ( "}\n" ) ; // class __near_BSONHandler_
263
+
264
+ this . sb . push ( `export function __near_decode_${ className } (
265
+ buffer: Uint8Array, offset: i32): ${ className } {
266
+ let handler = new __near_BSONHandler_${ className } ();
267
+ let decoder = new BSONDecoder<__near_BSONHandler_${ className } >(handler);
268
+ decoder.deserialize(buffer, offset);
269
+ return handler.value;
270
+ }\n` ) ;
271
+ }
272
+
273
+ private generateFieldEncoder ( fieldType : any , fieldName : any , sourceExpr : string ) {
274
+ let setterType = this . typeMapping [ fieldType ] ;
275
+ if ( ! setterType ) {
276
+ // Object
277
+ this . sb . push ( `if (${ sourceExpr } != null) {
278
+ __near_encode_${ fieldType } (${ sourceExpr } , encoder);
279
+ } else {
280
+ encoder.setNull("${ fieldName } ");
281
+ }` ) ;
282
+ }
283
+ else {
284
+ // Basic types
285
+ if ( this . nonNullableTypes . indexOf ( fieldType ) != - 1 ) {
286
+ this . sb . push ( `encoder.set${ setterType } ("${ fieldName } ", ${ sourceExpr } );` ) ;
287
+ }
288
+ else {
289
+ this . sb . push ( `if (${ sourceExpr } != null) {
290
+ encoder.set${ setterType } ("${ fieldName } ", ${ sourceExpr } );
291
+ } else {
292
+ encoder.setNull("${ fieldName } ");
293
+ }` ) ;
294
+ }
295
+ }
296
+ }
297
+
298
+ private getFields ( element : Class ) : any [ ] {
299
+ var members = element . members ;
300
+ var results = [ ] ;
301
+ if ( members ) {
302
+ for ( let member of members . values ( ) ) {
303
+ if ( ! ( member instanceof Field ) ) {
304
+ continue ;
305
+ }
306
+ results . push ( member ) ;
307
+ }
308
+ }
309
+ return results ;
310
+ }
311
+
312
+ visitInterface ( element : Interface ) : void {
313
+ // Do nothing
314
+ }
315
+
316
+ visitField ( element : Field ) : void {
317
+ throw new Error ( "Shouldn't be called" ) ;
318
+ }
319
+
320
+ visitNamespace ( element : Element ) : void {
321
+ // Do nothing
322
+ }
323
+
324
+ build ( ) : string {
325
+ let mainSource = this . program . sources
326
+ . filter ( s => s . normalizedPath . indexOf ( "~lib" ) != 0 ) [ 0 ] ;
327
+ this . sb . push ( mainSource . text ) ;
328
+ this . walk ( ) ;
329
+ return this . sb . join ( "\n" ) ;
330
+ }
331
+ }
332
+
130
333
/** A WebIDL definitions builder. */
131
334
export class IDLBuilder extends ExportsWalker {
132
335
0 commit comments