Introduce NativeCompileCache

This commit is contained in:
Antonio Scandurra
2015-10-27 14:03:00 +01:00
parent 69dfdd0745
commit c684273ecf
7 changed files with 178 additions and 1 deletions

View File

@@ -18,6 +18,7 @@
"atom-keymap": "^6.1.0",
"babel-core": "^5.8.21",
"bootstrap": "^3.3.4",
"cached-run-in-this-context": "0.3.0",
"clear-cut": "^2.0.1",
"coffee-script": "1.8.0",
"color": "^0.7.3",

1
spec/fixtures/native-cache/file-1.js vendored Normal file
View File

@@ -0,0 +1 @@
module.exports = function () { return 1; }

1
spec/fixtures/native-cache/file-2.js vendored Normal file
View File

@@ -0,0 +1 @@
module.exports = function () { return 2; }

View File

@@ -0,0 +1,37 @@
describe "NativeCompileCache", ->
nativeCompileCache = require '../src/native-compile-cache'
[fakeCacheStorage, cachedFiles] = []
beforeEach ->
cachedFiles = []
fakeCacheStorage = jasmine.createSpyObj("cache storage", ["set", "get"])
nativeCompileCache.setCacheStorage(fakeCacheStorage)
nativeCompileCache.install()
it "writes and reads from the cache storage when requiring files", ->
fakeCacheStorage.get.andReturn(null)
fakeCacheStorage.set.andCallFake (filename, cacheBuffer) ->
cachedFiles.push({filename, cacheBuffer})
fn1 = require('./fixtures/native-cache/file-1')
fn2 = require('./fixtures/native-cache/file-2')
expect(cachedFiles.length).toBe(2)
expect(cachedFiles[0].filename).toBe(require.resolve('./fixtures/native-cache/file-1'))
expect(cachedFiles[0].cacheBuffer).toBeInstanceOf(Uint8Array)
expect(cachedFiles[0].cacheBuffer.length).toBeGreaterThan(0)
expect(fn1()).toBe(1)
expect(cachedFiles[1].filename).toBe(require.resolve('./fixtures/native-cache/file-2'))
expect(cachedFiles[1].cacheBuffer).toBeInstanceOf(Uint8Array)
expect(cachedFiles[1].cacheBuffer.length).toBeGreaterThan(0)
expect(fn2()).toBe(2)
fakeCacheStorage.get.andReturn(cachedFiles[0].cacheBuffer)
fakeCacheStorage.set.reset()
fn1 = require('./fixtures/native-cache/file-1')
expect(fakeCacheStorage.set).not.toHaveBeenCalled()
expect(fn1()).toBe(1)

View File

@@ -0,0 +1,33 @@
var Path = require('path');
var fs = require('fs-plus');
module.exports = FileSystemCacheStorage = (function() {
function FileSystemCacheStorage(directory) {
this.directory = directory;
this.cachedPathsByKey = {};
}
FileSystemCacheStorage.prototype.has = function(key) {
return fs.existsSync(this.pathForKey(key));
};
FileSystemCacheStorage.prototype.get = function(key) {
return fs.readFileSync(this.pathForKey(key));
};
FileSystemCacheStorage.prototype.set = function(key, value) {
fs.writeFileSync(this.pathForKey(key), value);
};
FileSystemCacheStorage.prototype.pathForKey = function(key) {
var path = this.cachedPathsByKey[key];
if (!path) {
path = key.replace(/[\/.]/g, '-');
this.cachedPathsByKey[key] = path;
}
return Path.join(this.directory, path);
};
return FileSystemCacheStorage;
})();

View File

@@ -0,0 +1,97 @@
var Module = require('module')
var fs = require('fs-plus')
var path = require('path')
var cachedVm = require('cached-run-in-this-context')
NativeCompileCache = (function() {
function NativeCompileCache() {}
NativeCompileCache.prototype.setCacheStorage = function(storage) {
this.cacheStorage = storage;
};
NativeCompileCache.prototype.getCacheStorage = function() {
return this.cacheStorage;
};
NativeCompileCache.prototype.install = function() {
this.savePreviousModuleCompile();
this.overrideModuleCompile();
};
NativeCompileCache.prototype.uninstall = function() {
this.restorePreviousModuleCompile();
};
NativeCompileCache.prototype.savePreviousModuleCompile = function() {
this.previousModuleCompile = Module.prototype._compile;
};
NativeCompileCache.prototype.restorePreviousModuleCompile = function() {
Module.prototype._compile = this.previousModuleCompile;
};
NativeCompileCache.prototype.overrideModuleCompile = function() {
var cacheStorage = this.cacheStorage;
Module.prototype._compile = function(content, filename) {
var self = this;
// remove shebang
content = content.replace(/^\#\!.*/, '');
function require(path) {
return self.require(path);
}
require.resolve = function(request) {
return Module._resolveFilename(request, self);
};
require.main = process.mainModule;
// Enable support to add extra extension types
require.extensions = Module._extensions;
require.cache = Module._cache;
var dirname = path.dirname(filename);
// create wrapper function
var wrapper = Module.wrap(content);
var compiledWrapper = null;
if (cacheStorage.has(filename)) {
var buffer = cacheStorage.get(filename);
compiledWrapper =
cachedVm.runInThisContextCached(wrapper, filename, buffer).result;
} else {
var compilationResult = cachedVm.runInThisContext(wrapper, filename);
if (compilationResult.cacheBuffer) {
cacheStorage.set(filename, compilationResult.cacheBuffer);
}
compiledWrapper = compilationResult.result;
}
if (global.v8debug) {
if (!resolvedArgv) {
// we enter the repl if we're not given a filename argument.
if (process.argv[1]) {
resolvedArgv = Module._resolveFilename(process.argv[1], null);
} else {
resolvedArgv = 'repl';
}
}
// Set breakpoint on module start
if (filename === resolvedArgv) {
// Installing this dummy debug event listener tells V8 to start
// the debugger. Without it, the setBreakPoint() fails with an
// 'illegal access' error.
global.v8debug.Debug.setListener(function() {});
global.v8debug.Debug.setBreakPoint(compiledWrapper, 0, 0);
}
}
var args = [self.exports, require, self, filename, dirname, process, global];
return compiledWrapper.apply(self.exports, args);
};
};
return NativeCompileCache;
})();
module.exports = new NativeCompileCache;

View File

@@ -1,6 +1,8 @@
(function () {
var fs = require('fs')
var fs = require('fs-plus')
var path = require('path')
var FileSystemCacheStorage = require('../src/file-system-cache-storage')
var NativeCompileCache = require("../src/native-compile-cache")
var loadSettings = null
var loadSettingsError = null
@@ -16,6 +18,11 @@
// Ensure ATOM_HOME is always set before anything else is required
setupAtomHome()
NativeCompileCache.setCacheStorage(
new FileSystemCacheStorage(path.join(process.env.ATOM_HOME, "native-compile-cache/"))
)
NativeCompileCache.install()
// Normalize to make sure drive letter case is consistent on Windows
process.resourcesPath = path.normalize(process.resourcesPath)