@@ -709,8 +709,11 @@ var AMDLoader;
709709 var recorder = moduleManager . getRecorder ( ) ;
710710 var cachedDataPath = that . _getCachedDataPath ( nodeCachedData , filename ) ;
711711 var options = { filename : filename } ;
712+ var hashData ;
712713 try {
713- options . cachedData = that . _fs . readFileSync ( cachedDataPath ) ;
714+ var data = that . _fs . readFileSync ( cachedDataPath ) ;
715+ hashData = data . slice ( 0 , 16 ) ;
716+ options . cachedData = data . slice ( 16 ) ;
714717 recorder . record ( 60 /* CachedDataFound */ , cachedDataPath ) ;
715718 }
716719 catch ( _e ) {
@@ -724,7 +727,8 @@ var AMDLoader;
724727 var args = [ this . exports , require , this , filename , dirname , process , _commonjsGlobal , Buffer ] ;
725728 var result = compileWrapper . apply ( this . exports , args ) ;
726729 // cached data aftermath
727- setTimeout ( function ( ) { return that . _handleCachedData ( script , cachedDataPath , ! options . cachedData , moduleManager ) ; } , Math . ceil ( moduleManager . getConfig ( ) . getOptionsLiteral ( ) . nodeCachedData . writeDelay * Math . random ( ) ) ) ;
730+ that . _handleCachedData ( script , scriptSource , cachedDataPath , ! options . cachedData , moduleManager ) ;
731+ that . _verifyCachedData ( script , scriptSource , cachedDataPath , hashData ) ;
728732 return result ;
729733 } ;
730734 } ;
@@ -755,7 +759,7 @@ var AMDLoader;
755759 var vmScriptPathOrUri_1 = this . _getElectronRendererScriptPathOrUri ( normalizedScriptSrc_1 ) ;
756760 var wantsCachedData_1 = Boolean ( opts . nodeCachedData ) ;
757761 var cachedDataPath_1 = wantsCachedData_1 ? this . _getCachedDataPath ( opts . nodeCachedData , scriptSrc ) : undefined ;
758- this . _readSourceAndCachedData ( normalizedScriptSrc_1 , cachedDataPath_1 , recorder , function ( err , data , cachedData ) {
762+ this . _readSourceAndCachedData ( normalizedScriptSrc_1 , cachedDataPath_1 , recorder , function ( err , data , cachedData , hashData ) {
759763 if ( err ) {
760764 errorback ( err ) ;
761765 return ;
@@ -770,7 +774,8 @@ var AMDLoader;
770774 scriptSource = nodeInstrumenter ( scriptSource , normalizedScriptSrc_1 ) ;
771775 var scriptOpts = { filename : vmScriptPathOrUri_1 , cachedData : cachedData } ;
772776 var script = _this . _createAndEvalScript ( moduleManager , scriptSource , scriptOpts , callback , errorback ) ;
773- _this . _handleCachedData ( script , cachedDataPath_1 , wantsCachedData_1 && ! cachedData , moduleManager ) ;
777+ _this . _handleCachedData ( script , scriptSource , cachedDataPath_1 , wantsCachedData_1 && ! cachedData , moduleManager ) ;
778+ _this . _verifyCachedData ( script , scriptSource , cachedDataPath_1 , hashData ) ;
774779 } ) ;
775780 }
776781 } ;
@@ -815,36 +820,43 @@ var AMDLoader;
815820 var basename = this . _path . basename ( filename ) . replace ( / \. j s $ / , '' ) ;
816821 return this . _path . join ( config . path , basename + "-" + hash + ".code" ) ;
817822 } ;
818- NodeScriptLoader . prototype . _handleCachedData = function ( script , cachedDataPath , createCachedData , moduleManager ) {
823+ NodeScriptLoader . prototype . _handleCachedData = function ( script , scriptSource , cachedDataPath , createCachedData , moduleManager ) {
819824 var _this = this ;
820825 if ( script . cachedDataRejected ) {
821826 // cached data got rejected -> delete and re-create
822827 this . _fs . unlink ( cachedDataPath , function ( err ) {
823828 moduleManager . getRecorder ( ) . record ( 62 /* CachedDataRejected */ , cachedDataPath ) ;
824- _this . _createAndWriteCachedData ( script , cachedDataPath , moduleManager ) ;
829+ _this . _createAndWriteCachedData ( script , scriptSource , cachedDataPath , moduleManager ) ;
825830 if ( err ) {
826831 moduleManager . getConfig ( ) . onError ( err ) ;
827832 }
828833 } ) ;
829834 }
830835 else if ( createCachedData ) {
831836 // no cached data, but wanted
832- this . _createAndWriteCachedData ( script , cachedDataPath , moduleManager ) ;
837+ this . _createAndWriteCachedData ( script , scriptSource , cachedDataPath , moduleManager ) ;
833838 }
834839 } ;
835- NodeScriptLoader . prototype . _createAndWriteCachedData = function ( script , cachedDataPath , moduleManager ) {
840+ // Cached data format: | SOURCE_HASH | V8_CACHED_DATA |
841+ // -SOURCE_HASH is the md5 hash of the JS source (always 16 bytes)
842+ // -V8_CACHED_DATA is what v8 produces
843+ NodeScriptLoader . prototype . _createAndWriteCachedData = function ( script , scriptSource , cachedDataPath , moduleManager ) {
836844 var _this = this ;
837845 var timeout = Math . ceil ( moduleManager . getConfig ( ) . getOptionsLiteral ( ) . nodeCachedData . writeDelay * ( 1 + Math . random ( ) ) ) ;
838846 var lastSize = - 1 ;
839847 var iteration = 0 ;
848+ var hashData = undefined ;
840849 var createLoop = function ( ) {
841850 setTimeout ( function ( ) {
851+ if ( ! hashData ) {
852+ hashData = _this . _crypto . createHash ( 'md5' ) . update ( scriptSource , 'utf8' ) . digest ( ) ;
853+ }
842854 var cachedData = script . createCachedData ( ) ;
843855 if ( cachedData . length === 0 || cachedData . length === lastSize || iteration >= 5 ) {
844856 return ;
845857 }
846858 lastSize = cachedData . length ;
847- _this . _fs . writeFile ( cachedDataPath , cachedData , function ( err ) {
859+ _this . _fs . writeFile ( cachedDataPath , Buffer . concat ( [ hashData , cachedData ] ) , function ( err ) {
848860 if ( err ) {
849861 moduleManager . getConfig ( ) . onError ( err ) ;
850862 }
@@ -865,28 +877,56 @@ var AMDLoader;
865877 }
866878 else {
867879 // cached data case: read both files in parallel
868- var source_1 ;
869- var cachedData_1 ;
880+ var source_1 = undefined ;
881+ var cachedData_1 = undefined ;
882+ var hashData_1 = undefined ;
870883 var steps_1 = 2 ;
871884 var step_1 = function ( err ) {
872885 if ( err ) {
873886 callback ( err ) ;
874887 }
875888 else if ( -- steps_1 === 0 ) {
876- callback ( undefined , source_1 , cachedData_1 ) ;
889+ callback ( undefined , source_1 , cachedData_1 , hashData_1 ) ;
877890 }
878891 } ;
879892 this . _fs . readFile ( sourcePath , { encoding : 'utf8' } , function ( err , data ) {
880893 source_1 = data ;
881894 step_1 ( err ) ;
882895 } ) ;
883896 this . _fs . readFile ( cachedDataPath , function ( err , data ) {
884- cachedData_1 = data && data . length > 0 ? data : undefined ;
897+ if ( ! err && data && data . length > 0 ) {
898+ hashData_1 = data . slice ( 0 , 16 ) ;
899+ cachedData_1 = data . slice ( 16 ) ;
900+ recorder . record ( 60 /* CachedDataFound */ , cachedDataPath ) ;
901+ }
902+ else {
903+ recorder . record ( 61 /* CachedDataMissed */ , cachedDataPath ) ;
904+ }
885905 step_1 ( ) ; // ignored: cached data is optional
886- recorder . record ( err ? 61 /* CachedDataMissed */ : 60 /* CachedDataFound */ , cachedDataPath ) ;
887906 } ) ;
888907 }
889908 } ;
909+ NodeScriptLoader . prototype . _verifyCachedData = function ( script , scriptSource , cachedDataPath , hashData ) {
910+ var _this = this ;
911+ if ( ! hashData ) {
912+ // nothing to do
913+ return ;
914+ }
915+ if ( script . cachedDataRejected ) {
916+ // invalid anyways
917+ return ;
918+ }
919+ setTimeout ( function ( ) {
920+ // check source hash - the contract is that file paths change when file content
921+ // change (e.g use the commit or version id as cache path). this check is
922+ // for violations of this contract.
923+ var hashDataNow = _this . _crypto . createHash ( 'md5' ) . update ( scriptSource , 'utf8' ) . digest ( ) ;
924+ if ( ! hashData . equals ( hashDataNow ) ) {
925+ console . warn ( "FAILED TO VERIFY CACHED DATA. Deleting '" + cachedDataPath + "' now, but a RESTART IS REQUIRED" ) ;
926+ _this . _fs . unlink ( cachedDataPath , function ( err ) { return console . error ( "FAILED to unlink: '" + cachedDataPath + "'" , err ) ; } ) ;
927+ }
928+ } , Math . ceil ( 5000 * ( 1 + Math . random ( ) ) ) ) ;
929+ } ;
890930 NodeScriptLoader . _BOM = 0xFEFF ;
891931 NodeScriptLoader . _PREFIX = '(function (require, define, __filename, __dirname) { ' ;
892932 NodeScriptLoader . _SUFFIX = '\n});' ;
0 commit comments