diff --git a/spec/file-system-blob-store-spec.coffee b/spec/file-system-blob-store-spec.coffee new file mode 100644 index 000000000..c947259f6 --- /dev/null +++ b/spec/file-system-blob-store-spec.coffee @@ -0,0 +1,69 @@ +temp = require 'temp' +FileSystemBlobStore = require '../src/file-system-blob-store' + +describe "FileSystemBlobStore", -> + [storageDirectory, blobStore] = [] + + beforeEach -> + storageDirectory = temp.path() + blobStore = FileSystemBlobStore.load(storageDirectory) + + it "is empty when the file doesn't exist", -> + expect(blobStore.get("foo")).toBeUndefined() + expect(blobStore.get("bar")).toBeUndefined() + + it "allows to read and write buffers from/to memory without persisting them", -> + blobStore.set("foo", new Buffer("foo")) + blobStore.set("bar", new Buffer("bar")) + + expect(blobStore.get("foo")).toEqual(new Buffer("foo")) + expect(blobStore.get("bar")).toEqual(new Buffer("bar")) + + it "persists buffers when saved and retrieves them on load, giving priority to in-memory ones", -> + blobStore.set("foo", new Buffer("foo")) + blobStore.set("bar", new Buffer("bar")) + blobStore.save() + + blobStore = FileSystemBlobStore.load(storageDirectory) + + expect(blobStore.get("foo")).toEqual(new Buffer("foo")) + expect(blobStore.get("bar")).toEqual(new Buffer("bar")) + + blobStore.set("foo", new Buffer("changed")) + + expect(blobStore.get("foo")).toEqual(new Buffer("changed")) + + it "persists both in-memory and previously stored buffers when saved", -> + blobStore.set("foo", new Buffer("foo")) + blobStore.set("bar", new Buffer("bar")) + blobStore.save() + + blobStore = FileSystemBlobStore.load(storageDirectory) + blobStore.set("bar", new Buffer("changed")) + blobStore.set("qux", new Buffer("qux")) + blobStore.save() + + blobStore = FileSystemBlobStore.load(storageDirectory) + + expect(blobStore.get("foo")).toEqual(new Buffer("foo")) + expect(blobStore.get("bar")).toEqual(new Buffer("changed")) + expect(blobStore.get("qux")).toEqual(new Buffer("qux")) + + it "allows to delete keys from both memory and stored buffers", -> + blobStore.set("a", new Buffer("a")) + blobStore.set("b", new Buffer("b")) + blobStore.save() + + blobStore = FileSystemBlobStore.load(storageDirectory) + + blobStore.set("b", new Buffer("b")) + blobStore.set("c", new Buffer("c")) + blobStore.delete("b") + blobStore.delete("c") + blobStore.save() + + blobStore = FileSystemBlobStore.load(storageDirectory) + + expect(blobStore.get("a")).toEqual(new Buffer("a")) + expect(blobStore.get("b")).toBeUndefined() + expect(blobStore.get("c")).toBeUndefined() diff --git a/spec/file-system-cache-blob-storage-spec.coffee b/spec/file-system-cache-blob-storage-spec.coffee deleted file mode 100644 index 353a083cb..000000000 --- a/spec/file-system-cache-blob-storage-spec.coffee +++ /dev/null @@ -1,69 +0,0 @@ -temp = require 'temp' -FileSystemCacheBlobStorage = require '../src/file-system-cache-blob-storage' - -describe "FileSystemCacheBlobStorage", -> - [storageDirectory, cacheBlobStorage] = [] - - beforeEach -> - storageDirectory = temp.path() - cacheBlobStorage = FileSystemCacheBlobStorage.load(storageDirectory) - - it "is empty when the file doesn't exist", -> - expect(cacheBlobStorage.get("foo")).toBeUndefined() - expect(cacheBlobStorage.get("bar")).toBeUndefined() - - it "allows to read and write buffers from/to memory without persisting them", -> - cacheBlobStorage.set("foo", new Buffer("foo")) - cacheBlobStorage.set("bar", new Buffer("bar")) - - expect(cacheBlobStorage.get("foo")).toEqual(new Buffer("foo")) - expect(cacheBlobStorage.get("bar")).toEqual(new Buffer("bar")) - - it "persists buffers when saved and retrieves them on load, giving priority to in-memory ones", -> - cacheBlobStorage.set("foo", new Buffer("foo")) - cacheBlobStorage.set("bar", new Buffer("bar")) - cacheBlobStorage.save() - - cacheBlobStorage = FileSystemCacheBlobStorage.load(storageDirectory) - - expect(cacheBlobStorage.get("foo")).toEqual(new Buffer("foo")) - expect(cacheBlobStorage.get("bar")).toEqual(new Buffer("bar")) - - cacheBlobStorage.set("foo", new Buffer("changed")) - - expect(cacheBlobStorage.get("foo")).toEqual(new Buffer("changed")) - - it "persists both in-memory and previously stored buffers when saved", -> - cacheBlobStorage.set("foo", new Buffer("foo")) - cacheBlobStorage.set("bar", new Buffer("bar")) - cacheBlobStorage.save() - - cacheBlobStorage = FileSystemCacheBlobStorage.load(storageDirectory) - cacheBlobStorage.set("bar", new Buffer("changed")) - cacheBlobStorage.set("qux", new Buffer("qux")) - cacheBlobStorage.save() - - cacheBlobStorage = FileSystemCacheBlobStorage.load(storageDirectory) - - expect(cacheBlobStorage.get("foo")).toEqual(new Buffer("foo")) - expect(cacheBlobStorage.get("bar")).toEqual(new Buffer("changed")) - expect(cacheBlobStorage.get("qux")).toEqual(new Buffer("qux")) - - it "allows to delete keys from both memory and stored buffers", -> - cacheBlobStorage.set("a", new Buffer("a")) - cacheBlobStorage.set("b", new Buffer("b")) - cacheBlobStorage.save() - - cacheBlobStorage = FileSystemCacheBlobStorage.load(storageDirectory) - - cacheBlobStorage.set("b", new Buffer("b")) - cacheBlobStorage.set("c", new Buffer("c")) - cacheBlobStorage.delete("b") - cacheBlobStorage.delete("c") - cacheBlobStorage.save() - - cacheBlobStorage = FileSystemCacheBlobStorage.load(storageDirectory) - - expect(cacheBlobStorage.get("a")).toEqual(new Buffer("a")) - expect(cacheBlobStorage.get("b")).toBeUndefined() - expect(cacheBlobStorage.get("c")).toBeUndefined() diff --git a/spec/native-compile-cache-spec.coffee b/spec/native-compile-cache-spec.coffee index 28306d80e..dd720e84d 100644 --- a/spec/native-compile-cache-spec.coffee +++ b/spec/native-compile-cache-spec.coffee @@ -1,16 +1,16 @@ describe "NativeCompileCache", -> nativeCompileCache = require '../src/native-compile-cache' - [fakeCacheStorage, cachedFiles] = [] + [fakeCacheStore, cachedFiles] = [] beforeEach -> cachedFiles = [] - fakeCacheStorage = jasmine.createSpyObj("cache storage", ["set", "get", "has", "delete"]) - nativeCompileCache.setCacheStorage(fakeCacheStorage) + fakeCacheStore = jasmine.createSpyObj("cache store", ["set", "get", "has", "delete"]) + nativeCompileCache.setCacheStore(fakeCacheStore) nativeCompileCache.install() it "writes and reads from the cache storage when requiring files", -> - fakeCacheStorage.has.andReturn(false) - fakeCacheStorage.set.andCallFake (filename, cacheBuffer) -> + fakeCacheStore.has.andReturn(false) + fakeCacheStore.set.andCallFake (filename, cacheBuffer) -> cachedFiles.push({filename, cacheBuffer}) fn1 = require('./fixtures/native-cache/file-1') @@ -28,20 +28,20 @@ describe "NativeCompileCache", -> expect(cachedFiles[1].cacheBuffer.length).toBeGreaterThan(0) expect(fn2()).toBe(2) - fakeCacheStorage.has.andReturn(true) - fakeCacheStorage.get.andReturn(cachedFiles[0].cacheBuffer) - fakeCacheStorage.set.reset() + fakeCacheStore.has.andReturn(true) + fakeCacheStore.get.andReturn(cachedFiles[0].cacheBuffer) + fakeCacheStore.set.reset() fn1 = require('./fixtures/native-cache/file-1') - expect(fakeCacheStorage.set).not.toHaveBeenCalled() + expect(fakeCacheStore.set).not.toHaveBeenCalled() expect(fn1()).toBe(1) it "deletes previously cached code when the cache is not valid", -> - fakeCacheStorage.has.andReturn(true) - fakeCacheStorage.get.andCallFake -> new Buffer("an invalid cache") + fakeCacheStore.has.andReturn(true) + fakeCacheStore.get.andCallFake -> new Buffer("an invalid cache") fn3 = require('./fixtures/native-cache/file-3') - expect(fakeCacheStorage.delete).toHaveBeenCalledWith(require.resolve('./fixtures/native-cache/file-3')) + expect(fakeCacheStore.delete).toHaveBeenCalledWith(require.resolve('./fixtures/native-cache/file-3')) expect(fn3()).toBe(3) diff --git a/src/file-system-blob-store.js b/src/file-system-blob-store.js new file mode 100644 index 000000000..f2d2f7014 --- /dev/null +++ b/src/file-system-blob-store.js @@ -0,0 +1,94 @@ +'use strict' + +const fs = require('fs-plus') +const path = require('path') + +module.exports = +class FileSystemBlobStore { + static load (directory) { + let instance = new FileSystemBlobStore(directory) + instance.load() + return instance + } + + constructor (directory) { + this.inMemoryBlobs = new Map() + this.blobFilename = path.join(directory, 'BLOB') + this.mapFilename = path.join(directory, 'MAP') + this.storedBlob = new Buffer(0) + this.storedMap = {} + } + + load () { + if (!fs.existsSync(this.mapFilename)) { + return + } + if (!fs.existsSync(this.blobFilename)) { + return + } + this.storedBlob = fs.readFileSync(this.blobFilename) + this.storedMap = JSON.parse(fs.readFileSync(this.mapFilename)) + } + + save () { + let dump = this.getDump() + let cacheBlob = Buffer.concat(dump[0]) + let cacheMap = JSON.stringify(dump[1]) + fs.writeFileSync(this.blobFilename, cacheBlob) + fs.writeFileSync(this.mapFilename, cacheMap) + } + + has (key) { + return this.inMemoryBlobs.hasOwnProperty(key) || this.storedMap.hasOwnProperty(key) + } + + get (key) { + return this.getFromMemory(key) || this.getFromStorage(key) + } + + set (key, buffer) { + return this.inMemoryBlobs.set(key, buffer) + } + + delete (key) { + this.inMemoryBlobs.delete(key) + delete this.storedMap[key] + } + + getFromMemory (key) { + return this.inMemoryBlobs.get(key) + } + + getFromStorage (key) { + if (this.storedMap[key] == null) { + return + } + + return this.storedBlob.slice.apply(this.storedBlob, this.storedMap[key]) + } + + getDump () { + let buffers = [] + let cacheMap = {} + let currentBufferStart = 0 + + function dump (key, getBufferByKey) { + let buffer = getBufferByKey(key) + buffers.push(buffer) + cacheMap[key] = [currentBufferStart, currentBufferStart + buffer.length] + currentBufferStart += buffer.length + } + + for (let key of this.inMemoryBlobs.keys()) { + dump(key, this.getFromMemory.bind(this)) + } + + for (let key of Object.keys(this.storedMap)) { + if (!cacheMap[key]) { + dump(key, this.getFromStorage.bind(this)) + } + } + + return [buffers, cacheMap] + } +} diff --git a/src/file-system-cache-blob-storage.js b/src/file-system-cache-blob-storage.js deleted file mode 100644 index a2a016722..000000000 --- a/src/file-system-cache-blob-storage.js +++ /dev/null @@ -1,94 +0,0 @@ -'use strict' - -const fs = require('fs-plus') -const path = require('path') - -module.exports = -class FileSystemCacheBlobStorage { - static load (directory) { - let instance = new FileSystemCacheBlobStorage(directory) - instance.load() - return instance - } - - constructor (directory) { - this.inMemoryCache = new Map() - this.cacheBlobFilename = path.join(directory, 'v8-compile-cache.blob') - this.cacheMapFilename = path.join(directory, 'v8-compile-cache.map') - this.storedCacheBlob = new Buffer(0) - this.storedCacheMap = {} - } - - load () { - if (!fs.existsSync(this.cacheMapFilename)) { - return - } - if (!fs.existsSync(this.cacheBlobFilename)) { - return - } - this.storedCacheBlob = fs.readFileSync(this.cacheBlobFilename) - this.storedCacheMap = JSON.parse(fs.readFileSync(this.cacheMapFilename)) - } - - save () { - let dump = this.getDump() - let cacheBlob = Buffer.concat(dump[0]) - let cacheMap = JSON.stringify(dump[1]) - fs.writeFileSync(this.cacheBlobFilename, cacheBlob) - fs.writeFileSync(this.cacheMapFilename, cacheMap) - } - - has (key) { - return this.inMemoryCache.hasOwnProperty(key) || this.storedCacheMap.hasOwnProperty(key) - } - - get (key) { - return this.getFromMemory(key) || this.getFromStorage(key) - } - - set (key, buffer) { - return this.inMemoryCache.set(key, buffer) - } - - delete (key) { - this.inMemoryCache.delete(key) - delete this.storedCacheMap[key] - } - - getFromMemory (key) { - return this.inMemoryCache.get(key) - } - - getFromStorage (key) { - if (this.storedCacheMap[key] == null) { - return - } - - return this.storedCacheBlob.slice.apply(this.storedCacheBlob, this.storedCacheMap[key]) - } - - getDump () { - let buffers = [] - let cacheMap = {} - let currentBufferStart = 0 - - function dump (key, getBufferByKey) { - let buffer = getBufferByKey(key) - buffers.push(buffer) - cacheMap[key] = [currentBufferStart, currentBufferStart + buffer.length] - currentBufferStart += buffer.length - } - - for (let key of this.inMemoryCache.keys()) { - dump(key, this.getFromMemory.bind(this)) - } - - for (let key of Object.keys(this.storedCacheMap)) { - if (!cacheMap[key]) { - dump(key, this.getFromStorage.bind(this)) - } - } - - return [buffers, cacheMap] - } -} diff --git a/src/native-compile-cache.js b/src/native-compile-cache.js index 37500518e..1245d1f34 100644 --- a/src/native-compile-cache.js +++ b/src/native-compile-cache.js @@ -6,12 +6,12 @@ const cachedVm = require('cached-run-in-this-context') class NativeCompileCache { constructor () { - this.cacheStorage = null + this.cacheStore = null this.previousModuleCompile = null } - setCacheStorage (storage) { - this.cacheStorage = storage + setCacheStore (store) { + this.cacheStore = store } install () { @@ -28,7 +28,7 @@ class NativeCompileCache { } overrideModuleCompile () { - let cacheStorage = this.cacheStorage + let cacheStore = this.cacheStore let resolvedArgv = null Module.prototype._compile = function (content, filename) { let self = this @@ -52,17 +52,17 @@ class NativeCompileCache { let wrapper = Module.wrap(content) let compiledWrapper = null - if (cacheStorage.has(filename)) { - let buffer = cacheStorage.get(filename) + if (cacheStore.has(filename)) { + let buffer = cacheStore.get(filename) let compilationResult = cachedVm.runInThisContextCached(wrapper, filename, buffer) compiledWrapper = compilationResult.result if (compilationResult.wasRejected) { - cacheStorage.delete(filename) + cacheStore.delete(filename) } } else { let compilationResult = cachedVm.runInThisContext(wrapper, filename) if (compilationResult.cacheBuffer) { - cacheStorage.set(filename, compilationResult.cacheBuffer) + cacheStore.set(filename, compilationResult.cacheBuffer) } compiledWrapper = compilationResult.result } diff --git a/static/index.js b/static/index.js index 2e2b0980f..e60a8e282 100644 --- a/static/index.js +++ b/static/index.js @@ -2,7 +2,7 @@ var app = require('remote').require('app') var fs = require('fs-plus') var path = require('path') - var FileSystemCacheBlobStorage = require('../src/file-system-cache-blob-storage') + var FileSystemBlobStore = require('../src/file-system-blob-store') var NativeCompileCache = require('../src/native-compile-cache') var loadSettings = null @@ -26,10 +26,10 @@ // Ensure ATOM_HOME is always set before anything else is required setupAtomHome() - cacheStorage = FileSystemCacheBlobStorage.load( - path.join(process.env.ATOM_HOME, 'native-compile-cache/') + blobStore = FileSystemBlobStore.load( + path.join(process.env.ATOM_HOME, 'blob-store/') ) - NativeCompileCache.setCacheStorage(cacheStorage) + NativeCompileCache.setCacheStore(blobStore) NativeCompileCache.install() // Normalize to make sure drive letter case is consistent on Windows