This repository has been archived by the owner on Mar 3, 2023. It is now read-only.
/
native-compile-cache.js
130 lines (113 loc) · 3.52 KB
/
native-compile-cache.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
const Module = require('module');
const path = require('path');
const crypto = require('crypto');
const vm = require('vm');
function computeHash(contents) {
return crypto
.createHash('sha1')
.update(contents, 'utf8')
.digest('hex');
}
class NativeCompileCache {
constructor() {
this.cacheStore = null;
this.previousModuleCompile = null;
}
setCacheStore(store) {
this.cacheStore = store;
}
setV8Version(v8Version) {
this.v8Version = v8Version.toString();
}
install() {
this.savePreviousModuleCompile();
this.overrideModuleCompile();
}
uninstall() {
this.restorePreviousModuleCompile();
}
savePreviousModuleCompile() {
this.previousModuleCompile = Module.prototype._compile;
}
runInThisContext(code, filename) {
// TodoElectronIssue: produceCachedData is deprecated after Node 10.6, so we'll
// will need to update this for Electron v4 to use script.createCachedData().
const script = new vm.Script(code, { filename, produceCachedData: true });
return {
result: script.runInThisContext(),
cacheBuffer: script.cachedDataProduced ? script.cachedData : null
};
}
runInThisContextCached(code, filename, cachedData) {
const script = new vm.Script(code, { filename, cachedData });
return {
result: script.runInThisContext(),
wasRejected: script.cachedDataRejected
};
}
overrideModuleCompile() {
let self = this;
// Here we override Node's module.js
// (https://github.com/atom/node/blob/atom/lib/module.js#L378), changing
// only the bits that affect compilation in order to use the cached one.
Module.prototype._compile = function(content, filename) {
let moduleSelf = this;
// remove shebang
content = content.replace(/^#!.*/, '');
function require(path) {
return moduleSelf.require(path);
}
require.resolve = function(request) {
return Module._resolveFilename(request, moduleSelf);
};
require.main = process.mainModule;
// Enable support to add extra extension types
require.extensions = Module._extensions;
require.cache = Module._cache;
let dirname = path.dirname(filename);
// create wrapper function
let wrapper = Module.wrap(content);
let cacheKey = computeHash(wrapper + self.v8Version);
let compiledWrapper = null;
if (self.cacheStore.has(cacheKey)) {
let buffer = self.cacheStore.get(cacheKey);
let compilationResult = self.runInThisContextCached(
wrapper,
filename,
buffer
);
compiledWrapper = compilationResult.result;
if (compilationResult.wasRejected) {
self.cacheStore.delete(cacheKey);
}
} else {
let compilationResult;
try {
compilationResult = self.runInThisContext(wrapper, filename);
} catch (err) {
console.error(`Error running script ${filename}`);
throw err;
}
if (compilationResult.cacheBuffer) {
self.cacheStore.set(cacheKey, compilationResult.cacheBuffer);
}
compiledWrapper = compilationResult.result;
}
let args = [
moduleSelf.exports,
require,
moduleSelf,
filename,
dirname,
process,
global,
Buffer
];
return compiledWrapper.apply(moduleSelf.exports, args);
};
}
restorePreviousModuleCompile() {
Module.prototype._compile = this.previousModuleCompile;
}
}
module.exports = new NativeCompileCache();