Skip to content

Commit 03086c5

Browse files
authored
chore: remove source loading indirection MONGOSH-1343 (#33)
Instead of writing a module that exports the main JS source to the generated binary, just include the string as a C++ string. This avoids an extra parsing pass and reduces binary size.
1 parent 7458e5a commit 03086c5

File tree

4 files changed

+51
-20
lines changed

4 files changed

+51
-20
lines changed

resources/entry-point-trampoline.js

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,9 @@ const Module = require('module');
33
const vm = require('vm');
44
const path = require('path');
55
const {
6-
srcMod,
76
requireMappings,
87
enableBindingsPatch
98
} = REPLACE_WITH_BOXEDNODE_CONFIG;
10-
const src = require(srcMod);
119
const hydatedRequireMappings =
1210
requireMappings.map(([re, reFlags, linked]) => [new RegExp(re, reFlags), linked]);
1311

@@ -51,7 +49,7 @@ if (enableBindingsPatch) {
5149
});
5250
}
5351

54-
module.exports = (() => {
52+
module.exports = (src) => {
5553
const __filename = process.execPath;
5654
const __dirname = path.dirname(process.execPath);
5755
const innerRequire = Module.createRequire(__filename);
@@ -85,4 +83,4 @@ module.exports = (() => {
8583
filename: __filename
8684
})(__filename, __dirname, require, exports, module);
8785
return module.exports;
88-
})();
86+
};

resources/main-template.cc

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ void InitializeOncePerProcess();
3030
void TearDownOncePerProcess();
3131
}
3232
#endif
33+
namespace boxednode {
34+
Local<String> GetBoxednodeMainScriptSource(Isolate* isolate);
35+
}
3336

3437
extern "C" {
3538
typedef void (*register_boxednode_linked_module)(const void**, const void**);
@@ -122,14 +125,18 @@ static int RunNodeInstance(MultiIsolatePlatform* platform,
122125
// `module.createRequire()` is being used to create one that is able to
123126
// load files from the disk, and uses the standard CommonJS file loader
124127
// instead of the internal-only `require` function.
125-
MaybeLocal<Value> loadenv_ret = node::LoadEnvironment(
128+
Local<Value> loadenv_ret;
129+
if (!node::LoadEnvironment(
126130
env.get(),
127131
"const path = require('path');\n"
128132
"if (process.argv[2] === '--') process.argv.splice(2, 1);\n"
129-
"require(" REPLACE_WITH_ENTRY_POINT ")");
130-
131-
if (loadenv_ret.IsEmpty()) // There has been a JS exception.
132-
return 1;
133+
"return require(" REPLACE_WITH_ENTRY_POINT ")").ToLocal(&loadenv_ret)) {
134+
return 1; // There has been a JS exception.
135+
}
136+
assert(loadenv_ret->IsFunction());
137+
Local<Value> source = boxednode::GetBoxednodeMainScriptSource(isolate);
138+
if (loadenv_ret.As<Function>()->Call(context, Null(isolate), 1, &source).IsEmpty())
139+
return 1; // JS exception.
133140

134141
{
135142
// SealHandleScope protects against handle leaks from callbacks.
@@ -658,4 +665,8 @@ void TearDownOncePerProcess() {
658665

659666
} // namespace boxednode
660667

661-
#endif // USE_OWN_LEGACY_PROCESS_INITIALIZATION
668+
#endif // USE_OWN_LEGACY_PROCESS_INITIALIZATION
669+
670+
namespace boxednode {
671+
REPLACE_WITH_MAIN_SCRIPT_SOURCE_GETTER
672+
}

src/helpers.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,3 +75,29 @@ export function npm (): string[] {
7575
return ['npm'];
7676
}
7777
}
78+
79+
export function createCppJsStringDefinition (fnName: string, source: string): string {
80+
const sourceAsCharCodeArray = new Uint16Array(source.length);
81+
let isAllLatin1 = true;
82+
for (let i = 0; i < source.length; i++) {
83+
const charCode = source.charCodeAt(i);
84+
sourceAsCharCodeArray[i] = charCode;
85+
isAllLatin1 &&= charCode <= 0xFF;
86+
}
87+
88+
return `
89+
static const ${isAllLatin1 ? 'uint8_t' : 'uint16_t'} ${fnName}_source_[] = {
90+
${sourceAsCharCodeArray}
91+
};
92+
static_assert(
93+
${sourceAsCharCodeArray.length} <= v8::String::kMaxLength,
94+
"main script source exceeds max string length");
95+
Local<String> ${fnName}(Isolate* isolate) {
96+
return v8::String::NewFrom${isAllLatin1 ? 'One' : 'Two'}Byte(
97+
isolate,
98+
${fnName}_source_,
99+
v8::NewStringType::kNormal,
100+
${sourceAsCharCodeArray.length}).ToLocalChecked();
101+
}
102+
`;
103+
}

src/index.ts

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { promisify } from 'util';
1111
import { promises as fs, createReadStream, createWriteStream } from 'fs';
1212
import { AddonConfig, loadGYPConfig, storeGYPConfig, modifyAddonGyp } from './native-addons';
1313
import { ExecutableMetadata, generateRCFile } from './executable-metadata';
14-
import { spawnBuildCommand, ProcessEnv, pipeline } from './helpers';
14+
import { spawnBuildCommand, ProcessEnv, pipeline, createCppJsStringDefinition } from './helpers';
1515
import { Readable } from 'stream';
1616
import nv from '@pkgjs/nv';
1717

@@ -209,6 +209,8 @@ async function compileJSFileAsBinaryImpl (options: CompilationOptions, logger: L
209209
const extraJSSourceFiles: string[] = [];
210210
const enableBindingsPatch = options.enableBindingsPatch ?? options.addons?.length > 0;
211211

212+
const jsMainSource = await fs.readFile(options.sourceFile, 'utf8');
213+
212214
// We use the official embedder API for stability, which is available in all
213215
// supported versions of Node.js.
214216
{
@@ -250,32 +252,26 @@ async function compileJSFileAsBinaryImpl (options: CompilationOptions, logger: L
250252
registerFunctions.map((fn) => `void ${fn}(const void**,const void**);\n`).join(''));
251253
mainSource = mainSource.replace(/\bREPLACE_DEFINE_LINKED_MODULES\b/g,
252254
registerFunctions.map((fn) => `${fn},`).join(''));
255+
mainSource = mainSource.replace(/\bREPLACE_WITH_MAIN_SCRIPT_SOURCE_GETTER\b/g,
256+
createCppJsStringDefinition('GetBoxednodeMainScriptSource', jsMainSource));
253257
await fs.writeFile(path.join(nodeSourcePath, 'src', 'node_main.cc'), mainSource);
254258
logger.stepCompleted();
255259
}
256260

257261
logger.stepStarting('Inserting custom code into Node.js source');
258262
await fs.mkdir(path.join(nodeSourcePath, 'lib', namespace), { recursive: true });
259-
const source = await fs.readFile(options.sourceFile, 'utf8');
260-
await fs.writeFile(
261-
path.join(nodeSourcePath, 'lib', namespace, `${namespace}_src.js`),
262-
`module.exports = ${JSON.stringify(source)}`);
263263
let entryPointTrampolineSource = await fs.readFile(
264264
path.join(__dirname, '..', 'resources', 'entry-point-trampoline.js'), 'utf8');
265265
entryPointTrampolineSource = entryPointTrampolineSource.replace(
266266
/\bREPLACE_WITH_BOXEDNODE_CONFIG\b/g,
267267
JSON.stringify({
268-
srcMod: `${namespace}/${namespace}_src`,
269268
requireMappings: requireMappings.map(([re, linked]) => [re.source, re.flags, linked]),
270269
enableBindingsPatch
271270
}));
272271
await fs.writeFile(
273272
path.join(nodeSourcePath, 'lib', namespace, `${namespace}.js`),
274273
entryPointTrampolineSource);
275-
extraJSSourceFiles.push(
276-
`./lib/${namespace}/${namespace}.js`,
277-
`./lib/${namespace}/${namespace}_src.js`
278-
);
274+
extraJSSourceFiles.push(`./lib/${namespace}/${namespace}.js`);
279275
logger.stepCompleted();
280276

281277
logger.stepStarting('Storing executable metadata');

0 commit comments

Comments
 (0)