@@ -270,6 +270,18 @@ module AMDLoader {
270
270
nodeMain ?:string ;
271
271
nodeModules ?:string [ ] ;
272
272
checksum ?:boolean ;
273
+ /**
274
+ * Optional data directory for reading/writing v8 cached data (http://v8project.blogspot.co.uk/2015/07/code-caching.html)
275
+ */
276
+ nodeCachedDataDir ?: string ;
277
+ /**
278
+ * Optional delay for filesystem write/delete operations
279
+ */
280
+ nodeCachedDataWriteDelay ?: number ;
281
+ /**
282
+ * Optional callback that will be invoked when errors with cached code occur
283
+ */
284
+ onNodeCachedDataError ?: ( err : any ) => void ;
273
285
}
274
286
275
287
export class ConfigurationOptionsUtil {
@@ -341,6 +353,20 @@ module AMDLoader {
341
353
if ( ! Array . isArray ( options . nodeModules ) ) {
342
354
options . nodeModules = [ ] ;
343
355
}
356
+ if ( typeof options . nodeCachedDataWriteDelay !== 'number' || options . nodeCachedDataWriteDelay < 0 ) {
357
+ options . nodeCachedDataWriteDelay = 1000 * 7 ;
358
+ }
359
+ if ( typeof options . onNodeCachedDataError !== 'function' ) {
360
+ options . onNodeCachedDataError = ( err ) => {
361
+ if ( err . errorCode === 'cachedDataRejected' ) {
362
+ console . warn ( 'Rejected cached data from file: ' + err . path ) ;
363
+
364
+ } else if ( err . errorCode === 'unlink' || err . errorCode === 'writeFile' ) {
365
+ console . error ( 'Problems writing cached data file: ' + err . path ) ;
366
+ console . error ( err . detail ) ;
367
+ }
368
+ } ;
369
+ }
344
370
345
371
return options ;
346
372
}
@@ -2192,18 +2218,40 @@ module AMDLoader {
2192
2218
}
2193
2219
}
2194
2220
2221
+ declare class Buffer {
2222
+
2223
+ }
2224
+
2195
2225
interface INodeFS {
2196
- readFile ( filename :string , options :{ encoding ?:string ; flag ?:string } , callback :( err :any , data :any ) => void ) : void ;
2226
+ readFile ( filename : string , options : { encoding ?: string ; flag ?: string } , callback : ( err : any , data : any ) => void ) : void ;
2227
+ readFile ( filename : string , callback : ( err : any , data : Buffer ) => void ) : void ;
2228
+ writeFile ( filename : string , data : Buffer , callback : ( err : any ) => void ) : void ;
2229
+ unlink ( path : string , callback : ( err : any ) => void ) : void ;
2230
+ }
2231
+
2232
+ interface INodeVMScriptOptions {
2233
+ filename : string ;
2234
+ produceCachedData ?: boolean ;
2235
+ cachedData ?: Buffer ;
2236
+ }
2237
+
2238
+ interface INodeVMScript {
2239
+ cachedData : Buffer ;
2240
+ cachedDataProduced : boolean ;
2241
+ cachedDataRejected : boolean ;
2242
+ runInThisContext ( options : INodeVMScriptOptions ) ;
2197
2243
}
2198
2244
2199
2245
interface INodeVM {
2246
+ Script : { new ( contents : string , options : INodeVMScriptOptions ) : INodeVMScript }
2200
2247
runInThisContext ( contents :string , { filename :string } ) ;
2201
2248
runInThisContext ( contents :string , filename :string ) ;
2202
2249
}
2203
2250
2204
2251
interface INodePath {
2205
2252
dirname ( filename :string ) : string ;
2206
- normalize ( filename :string ) : string ;
2253
+ normalize ( filename : string ) :string ;
2254
+ join ( ...parts : string [ ] ) : string ;
2207
2255
}
2208
2256
2209
2257
interface INodeCryptoHash {
@@ -2313,21 +2361,80 @@ module AMDLoader {
2313
2361
2314
2362
contents = nodeInstrumenter ( contents , vmScriptSrc ) ;
2315
2363
2316
- let r ;
2317
- if ( / ^ v 0 \. 1 2 / . test ( process . version ) ) {
2318
- r = this . _vm . runInThisContext ( contents , { filename :vmScriptSrc } ) ;
2364
+ if ( ! opts . nodeCachedDataDir ) {
2365
+
2366
+ const r = this . _vm . runInThisContext ( contents , { filename : vmScriptSrc } ) ;
2367
+
2368
+ r . call ( global , RequireFunc , DefineFunc , vmScriptSrc , this . _path . dirname ( scriptSrc ) ) ;
2369
+
2370
+ recorder . record ( LoaderEventType . NodeEndEvaluatingScript , scriptSrc ) ;
2371
+
2372
+ callback ( ) ;
2373
+
2319
2374
} else {
2320
- r = this . _vm . runInThisContext ( contents , vmScriptSrc ) ;
2321
- }
2322
2375
2323
- r . call ( global , RequireFunc , DefineFunc , vmScriptSrc , this . _path . dirname ( scriptSrc ) ) ;
2376
+ const cachedDataPath = this . _path . join ( opts . nodeCachedDataDir , scriptSrc . replace ( / \\ | \/ / g , '' ) + '.code' ) ;
2324
2377
2325
- recorder . record ( LoaderEventType . NodeEndEvaluatingScript , scriptSrc ) ;
2378
+ this . _fs . readFile ( cachedDataPath , ( err , data ) => {
2326
2379
2327
- callback ( ) ;
2380
+ // create script options
2381
+ const scriptOptions : INodeVMScriptOptions = {
2382
+ filename : vmScriptSrc ,
2383
+ produceCachedData : typeof data === 'undefined' ,
2384
+ cachedData : data
2385
+ } ;
2386
+
2387
+ // create script, run script
2388
+ const script = new this . _vm . Script ( contents , scriptOptions ) ;
2389
+ const r = script . runInThisContext ( scriptOptions ) ;
2390
+ r . call ( global , RequireFunc , DefineFunc , vmScriptSrc , this . _path . dirname ( scriptSrc ) ) ;
2391
+
2392
+ // signal done
2393
+ recorder . record ( LoaderEventType . NodeEndEvaluatingScript , scriptSrc ) ;
2394
+ callback ( ) ;
2395
+
2396
+ // cached code after math
2397
+ if ( script . cachedDataRejected ) {
2398
+ // data rejected => delete cache file
2399
+
2400
+ opts . onNodeCachedDataError ( {
2401
+ errorCode : 'cachedDataRejected' ,
2402
+ path : cachedDataPath
2403
+ } ) ;
2404
+
2405
+ NodeScriptLoader . _runSoon ( ( ) => this . _fs . unlink ( cachedDataPath , err => {
2406
+ if ( err ) {
2407
+ opts . onNodeCachedDataError ( {
2408
+ errorCode : 'unlink' ,
2409
+ path : cachedDataPath ,
2410
+ detail : err
2411
+ } ) ;
2412
+ }
2413
+ } ) , opts . nodeCachedDataWriteDelay ) ;
2414
+
2415
+ } else if ( script . cachedDataProduced ) {
2416
+ // data produced => write cache file
2417
+
2418
+ NodeScriptLoader . _runSoon ( ( ) => this . _fs . writeFile ( cachedDataPath , script . cachedData , err => {
2419
+ if ( err ) {
2420
+ opts . onNodeCachedDataError ( {
2421
+ errorCode : 'writeFile' ,
2422
+ path : cachedDataPath ,
2423
+ detail : err
2424
+ } ) ;
2425
+ }
2426
+ } ) , opts . nodeCachedDataWriteDelay ) ;
2427
+ }
2428
+ } ) ;
2429
+ }
2328
2430
} ) ;
2329
2431
}
2330
2432
}
2433
+
2434
+ private static _runSoon ( callback : Function , minTimeout : number ) : void {
2435
+ const timeout = minTimeout + Math . ceil ( Math . random ( ) * minTimeout ) ;
2436
+ setTimeout ( callback , timeout ) ;
2437
+ }
2331
2438
}
2332
2439
2333
2440
// ------------------------------------------------------------------------
0 commit comments