From 957d5f1680cdef3f8c774567a6adf754e6ba9893 Mon Sep 17 00:00:00 2001 From: Asher Date: Wed, 10 Apr 2019 20:12:48 -0500 Subject: [PATCH] Don't overwrite extracted native module --- node.patch | 18 ++++++++++-------- src/api/bundler.ts | 21 ++++++++++++++------- src/patches/nbin.ts | 15 ++++++++++----- typings/nbin.d.ts | 7 ++++++- 4 files changed, 40 insertions(+), 21 deletions(-) diff --git a/node.patch b/node.patch index c5b6198..930d2e1 100644 --- a/node.patch +++ b/node.patch @@ -1,11 +1,11 @@ diff --git a/lib/internal/bootstrap/node.js b/lib/internal/bootstrap/node.js -index 885546c5d6..f1287bb191 100644 +index 885546c5d6..f21e7993f1 100644 --- a/lib/internal/bootstrap/node.js +++ b/lib/internal/bootstrap/node.js @@ -215,11 +215,16 @@ // are running from a script and running the REPL - but there are a few // others like the debugger or running --eval arguments. Here we decide - // which mode we run in. + // which mode we run in. + + if (NativeModule.exists('_third_party_main')) { + NativeModule.require('_third_party_main'); @@ -21,7 +21,7 @@ index 885546c5d6..f1287bb191 100644 // one to drop a file lib/_third_party_main.js into the build // directory which will be executed instead of Node's normal loading. diff --git a/lib/internal/modules/cjs/loader.js b/lib/internal/modules/cjs/loader.js -index fb3770b729..eb65385afe 100644 +index fb3770b729..f4bad8d6de 100644 --- a/lib/internal/modules/cjs/loader.js +++ b/lib/internal/modules/cjs/loader.js @@ -44,6 +44,9 @@ const { getOptionValue } = require('internal/options'); @@ -101,17 +101,19 @@ index fb3770b729..eb65385afe 100644 try { module.exports = JSON.parse(stripBOM(content)); } catch (err) { -@@ -715,7 +742,14 @@ Module._extensions['.json'] = function(module, filename) { +@@ -715,7 +742,16 @@ Module._extensions['.json'] = function(module, filename) { // Native extension for .node Module._extensions['.node'] = function(module, filename) { - return process.dlopen(module, path.toNamespacedPath(filename)); + let isInternal = false; + if (nbin.existsSync(filename)) { -+ const tmpFile = path.join(os.tmpdir(), `.nbin-${path.basename(filename)}`); -+ fs.writeFileSync(tmpFile, nbin.readFileSync(filename)); -+ filename = tmpFile; -+ isInternal = true; ++ const tmpFile = path.join(os.tmpdir(), `.nbin${nbin.id}-${path.basename(filename)}`); ++ if (!fs.existsSync(tmpFile)) { ++ fs.writeFileSync(tmpFile, nbin.readFileSync(filename)); ++ } ++ filename = tmpFile; ++ isInternal = true; + } + return process.dlopen(module, isInternal ? filename : path.toNamespacedPath(filename)); }; diff --git a/src/api/bundler.ts b/src/api/bundler.ts index 6e0873c..27942e4 100644 --- a/src/api/bundler.ts +++ b/src/api/bundler.ts @@ -11,8 +11,6 @@ import { WritableFilesystem } from "../common/filesystem"; import { createFooter } from "../common/footer"; import ora, { Ora } from "ora"; -declare const __non_webpack_require__: typeof require; - export class Binary implements nbin.Binary { private readonly fs: WritableFilesystem = new WritableFilesystem(); @@ -87,6 +85,15 @@ export class Binary implements nbin.Binary { public async build(): Promise { const nodeBuffer = await this.cacheBinary(); + // Create a buffer containing a (most likely) unique ID and its length. + const idLength = 6; + const possible = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + const id = Array(idLength).fill(1) + .map(() => possible[Math.floor(Math.random() * possible.length)]) + .join(""); + const idBuffer = Buffer.alloc(2 + Buffer.byteLength(id)); + writeString(idBuffer, id); + // Writing the entrypoint const mainFileBuffer = Buffer.alloc(2 + Buffer.byteLength(this.options.mainFile)); writeString(mainFileBuffer, this.options.mainFile); @@ -99,19 +106,19 @@ export class Binary implements nbin.Binary { // Footer const footerBuffer = createFooter( - fsBuffer.header.byteLength + mainFileBuffer.byteLength, // Header byte length + fsBuffer.header.byteLength + idBuffer.byteLength + mainFileBuffer.byteLength, // Header byte length nodeBuffer.byteLength, // Header byte offset fsBuffer.fileContents.byteLength, // File contents length - nodeBuffer.byteLength + fsBuffer.header.byteLength + mainFileBuffer.byteLength, // File contents offset - ); + nodeBuffer.byteLength + fsBuffer.header.byteLength + idBuffer.byteLength + mainFileBuffer.byteLength, // File contents offset + ); - return Buffer.concat([nodeBuffer, mainFileBuffer, fsBuffer.header, fsBuffer.fileContents, footerBuffer]); + return Buffer.concat([nodeBuffer, idBuffer, mainFileBuffer, fsBuffer.header, fsBuffer.fileContents, footerBuffer]); } private async cacheBinary(): Promise { let nodeBinaryPath = this.options.nodePath || path.join(__dirname, "../../lib/node/out/Release/node"); const nodeBinaryName = this.nodeBinaryName; - + const cacheDir = path.join(os.homedir(), ".nbin"); if (!fs.existsSync(nodeBinaryPath)) { if (!fs.existsSync(cacheDir)) { diff --git a/src/patches/nbin.ts b/src/patches/nbin.ts index 475841c..4f36197 100644 --- a/src/patches/nbin.ts +++ b/src/patches/nbin.ts @@ -13,12 +13,15 @@ const nbinFd = fs.openSync(execPath, "r"); // Footer is located at the end of the file const footer = readFooter(nbinFd, execPathStat.size); -// Contains the mainFile and the filesystem -const mainFileFsBuffer = Buffer.allocUnsafe(footer.headerLength); -fs.readSync(nbinFd, mainFileFsBuffer, 0, footer.headerLength, footer.headerOffset); +// Contains the ID, mainFile and the filesystem +const headerBuffer = Buffer.allocUnsafe(footer.headerLength); +fs.readSync(nbinFd, headerBuffer, 0, footer.headerLength, footer.headerOffset); + +// Reading the ID. +const id = readString(headerBuffer, 0); // Reading the mainfile -const mainFile = readString(mainFileFsBuffer, 0); +const mainFile = readString(headerBuffer, id.offset); /** * Maximize read perf by storing before any overrides @@ -26,7 +29,7 @@ const mainFile = readString(mainFileFsBuffer, 0); const originalRead = fs.read; const originalReadSync = fs.readSync; -const fsBuffer = mainFileFsBuffer.slice(mainFile.offset); +const fsBuffer = headerBuffer.slice(mainFile.offset); const readableFs = ReadableFilesystem.fromBuffer(fsBuffer, { readContents: (offset: number, length: number): Promise => { const buffer = Buffer.allocUnsafe(length); @@ -81,6 +84,8 @@ const createNotFound = (): Error => { }; const exported: typeof import("nbin") = { + id: id.value, + mainFile: mainFile.value, existsSync: (pathName: string): boolean => { diff --git a/typings/nbin.d.ts b/typings/nbin.d.ts index 2217cc1..3ebfdd6 100644 --- a/typings/nbin.d.ts +++ b/typings/nbin.d.ts @@ -39,6 +39,11 @@ declare module 'nbin' { function readFileSync(path: string, encoding?: "buffer", offset?: number, length?: number): Buffer; function readFileSync(path: string, encoding?: "utf8", offset?: number, length?: number): Buffer; + /** + * Uniquely generated ID for the packaged binary. + */ + export const id: string; + /** * Returns the entrypoint of the application. */ @@ -97,7 +102,7 @@ declare module '@coder/nbin' { * Will bundle a module based on path and name. * Allows you to do `writeModule("/test/bananas/node_modules/frog")` and * embed the `frog` module within the binary. - * + * * All modules by default will be placed in `/node_modules` */ public writeModule(modulePath: string): void;