forked from atom/atom
-
Notifications
You must be signed in to change notification settings - Fork 31
/
Copy pathnative-compile-cache.js
129 lines (112 loc) · 3.38 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
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) {
const script = new vm.Script(code, filename);
const cachedData = script.createCachedData();
return {
result: script.runInThisContext(),
cacheBuffer: typeof cachedData !== 'undefined' ? 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();