Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
emscripten-core
GitHub Repository: emscripten-core/emscripten
Path: blob/main/src/shell.js
4128 views
1
/**
2
* @license
3
* Copyright 2010 The Emscripten Authors
4
* SPDX-License-Identifier: MIT
5
*/
6
#if STRICT_JS
7
"use strict";
8
9
#endif
10
// The Module object: Our interface to the outside world. We import
11
// and export values on it. There are various ways Module can be used:
12
// 1. Not defined. We create it here
13
// 2. A function parameter, function(moduleArg) => Promise<Module>
14
// 3. pre-run appended it, var Module = {}; ..generated code..
15
// 4. External script tag defines var Module.
16
// We need to check if Module already exists (e.g. case 3 above).
17
// Substitution will be replaced with actual code on later stage of the build,
18
// this way Closure Compiler will not mangle it (e.g. case 4. above).
19
// Note that if you want to run closure, and also to use Module
20
// after the generated code, you will need to define var Module = {};
21
// before the code. Then that object will be used in the code, and you
22
// can continue to use Module afterwards as well.
23
#if MODULARIZE
24
#if MODULARIZE == 'instance'
25
var Module = {};
26
#else
27
var Module = moduleArg;
28
#endif
29
#elif USE_CLOSURE_COMPILER
30
/** @type{Object} */
31
var Module;
32
// if (!Module)` is crucial for Closure Compiler here as it will otherwise replace every `Module` occurrence with a string
33
if (!Module) /** @suppress{checkTypes}*/Module = {"__EMSCRIPTEN_PRIVATE_MODULE_EXPORT_NAME_SUBSTITUTION__":1};
34
#elif AUDIO_WORKLET
35
var Module = globalThis.Module || (typeof {{{ EXPORT_NAME }}} != 'undefined' ? {{{ EXPORT_NAME }}} : {});
36
#else
37
var Module = typeof {{{ EXPORT_NAME }}} != 'undefined' ? {{{ EXPORT_NAME }}} : {};
38
#endif // USE_CLOSURE_COMPILER
39
40
#if POLYFILL
41
#if WASM_BIGINT && MIN_SAFARI_VERSION < 150000
42
// See https://caniuse.com/mdn-javascript_builtins_bigint64array
43
#include "polyfill/bigint64array.js"
44
#endif
45
#endif // POLYFILL
46
47
#if WASM_WORKERS
48
// The way we signal to a worker that it is hosting a pthread is to construct
49
// it with a specific name.
50
var ENVIRONMENT_IS_WASM_WORKER = globalThis.name == 'em-ww';
51
#endif
52
53
#if AUDIO_WORKLET
54
var ENVIRONMENT_IS_AUDIO_WORKLET = typeof AudioWorkletGlobalScope !== 'undefined';
55
// Audio worklets behave as wasm workers.
56
if (ENVIRONMENT_IS_AUDIO_WORKLET) ENVIRONMENT_IS_WASM_WORKER = true;
57
#endif
58
59
// Determine the runtime environment we are in. You can customize this by
60
// setting the ENVIRONMENT setting at compile time (see settings.js).
61
62
#if ENVIRONMENT.length == 1
63
var ENVIRONMENT_IS_WEB = {{{ ENVIRONMENT[0] === 'web' }}};
64
#if PTHREADS && ENVIRONMENT_MAY_BE_NODE
65
// node+pthreads always supports workers; detect which we are at runtime
66
var ENVIRONMENT_IS_WORKER = typeof WorkerGlobalScope != 'undefined';
67
#else
68
var ENVIRONMENT_IS_WORKER = {{{ ENVIRONMENT[0] === 'worker' }}};
69
#endif
70
var ENVIRONMENT_IS_NODE = {{{ ENVIRONMENT[0] === 'node' }}};
71
var ENVIRONMENT_IS_SHELL = {{{ ENVIRONMENT[0] === 'shell' }}};
72
#else // ENVIRONMENT
73
// Attempt to auto-detect the environment
74
var ENVIRONMENT_IS_WEB = typeof window == 'object';
75
var ENVIRONMENT_IS_WORKER = typeof WorkerGlobalScope != 'undefined';
76
// N.b. Electron.js environment is simultaneously a NODE-environment, but
77
// also a web environment.
78
var ENVIRONMENT_IS_NODE = {{{ nodeDetectionCode() }}};
79
#if AUDIO_WORKLET
80
var ENVIRONMENT_IS_SHELL = !ENVIRONMENT_IS_WEB && !ENVIRONMENT_IS_NODE && !ENVIRONMENT_IS_WORKER && !ENVIRONMENT_IS_AUDIO_WORKLET;
81
#else
82
var ENVIRONMENT_IS_SHELL = !ENVIRONMENT_IS_WEB && !ENVIRONMENT_IS_NODE && !ENVIRONMENT_IS_WORKER;
83
#endif
84
#endif // ENVIRONMENT
85
86
#if PTHREADS
87
// Three configurations we can be running in:
88
// 1) We could be the application main() thread running in the main JS UI thread. (ENVIRONMENT_IS_WORKER == false and ENVIRONMENT_IS_PTHREAD == false)
89
// 2) We could be the application main() thread proxied to worker. (with Emscripten -sPROXY_TO_WORKER) (ENVIRONMENT_IS_WORKER == true, ENVIRONMENT_IS_PTHREAD == false)
90
// 3) We could be an application pthread running in a worker. (ENVIRONMENT_IS_WORKER == true and ENVIRONMENT_IS_PTHREAD == true)
91
92
// The way we signal to a worker that it is hosting a pthread is to construct
93
// it with a specific name.
94
var ENVIRONMENT_IS_PTHREAD = ENVIRONMENT_IS_WORKER && self.name?.startsWith('em-pthread');
95
96
#if MODULARIZE && ASSERTIONS
97
if (ENVIRONMENT_IS_PTHREAD) {
98
assert(!globalThis.moduleLoaded, 'module should only be loaded once on each pthread worker');
99
globalThis.moduleLoaded = true;
100
}
101
#endif
102
#endif
103
104
#if ENVIRONMENT_MAY_BE_NODE && (EXPORT_ES6 || PTHREADS || WASM_WORKERS)
105
if (ENVIRONMENT_IS_NODE) {
106
#if EXPORT_ES6
107
// When building an ES module `require` is not normally available.
108
// We need to use `createRequire()` to construct the require()` function.
109
const { createRequire } = await import('module');
110
/** @suppress{duplicate} */
111
var require = createRequire(import.meta.url);
112
#endif
113
114
#if PTHREADS || WASM_WORKERS
115
var worker_threads = require('worker_threads');
116
global.Worker = worker_threads.Worker;
117
ENVIRONMENT_IS_WORKER = !worker_threads.isMainThread;
118
#if PTHREADS
119
// Under node we set `workerData` to `em-pthread` to signal that the worker
120
// is hosting a pthread.
121
ENVIRONMENT_IS_PTHREAD = ENVIRONMENT_IS_WORKER && worker_threads['workerData'] == 'em-pthread'
122
#endif // PTHREADS
123
#if WASM_WORKERS
124
ENVIRONMENT_IS_WASM_WORKER = ENVIRONMENT_IS_WORKER && worker_threads['workerData'] == 'em-ww'
125
#endif
126
#endif // PTHREADS || WASM_WORKERS
127
}
128
#endif // ENVIRONMENT_MAY_BE_NODE
129
130
// --pre-jses are emitted after the Module integration code, so that they can
131
// refer to Module (if they choose; they can also define Module)
132
{{{ preJS() }}}
133
134
var arguments_ = [];
135
var thisProgram = './this.program';
136
var quit_ = (status, toThrow) => {
137
throw toThrow;
138
};
139
140
#if EXPORT_ES6
141
var _scriptName = import.meta.url;
142
#else
143
#if ENVIRONMENT_MAY_BE_WEB
144
#if !MODULARIZE
145
// In MODULARIZE mode _scriptName needs to be captured already at the very top of the page immediately when the page is parsed, so it is generated there
146
// before the page load. In non-MODULARIZE modes generate it here.
147
#if SINGLE_FILE && OUTPUT_FORMAT == 'HTML'
148
var _scriptName = typeof document != 'undefined' ? URL.createObjectURL(new Blob([document.getElementById('mainScript').textContent], { "type" : "text/javascript" })) : undefined;
149
#else
150
var _scriptName = typeof document != 'undefined' ? document.currentScript?.src : undefined;
151
#endif
152
#endif // !MODULARIZE
153
#elif ENVIRONMENT_MAY_BE_NODE || ENVIRONMENT_MAY_BE_WORKER
154
var _scriptName;
155
#endif // ENVIRONMENT_MAY_BE_WEB
156
157
#if ENVIRONMENT_MAY_BE_NODE
158
if (typeof __filename != 'undefined') { // Node
159
_scriptName = __filename;
160
} else
161
#endif // ENVIRONMENT_MAY_BE_NODE
162
#if ENVIRONMENT_MAY_BE_WORKER
163
if (ENVIRONMENT_IS_WORKER) {
164
_scriptName = self.location.href;
165
}
166
#elif ENVIRONMENT_MAY_BE_NODE
167
/*no-op*/{}
168
#endif // ENVIRONMENT_MAY_BE_WORKER
169
#endif // EXPORT_ES6
170
171
// `/` should be present at the end if `scriptDirectory` is not empty
172
var scriptDirectory = '';
173
function locateFile(path) {
174
#if RUNTIME_DEBUG
175
dbg('locateFile:', path, 'scriptDirectory:', scriptDirectory);
176
#endif
177
#if expectToReceiveOnModule('locateFile')
178
if (Module['locateFile']) {
179
return Module['locateFile'](path, scriptDirectory);
180
}
181
#endif
182
return scriptDirectory + path;
183
}
184
185
// Hooks that are implemented differently in different runtime environments.
186
var readAsync, readBinary;
187
188
#if ENVIRONMENT_MAY_BE_NODE
189
if (ENVIRONMENT_IS_NODE) {
190
#if ENVIRONMENT.length && ASSERTIONS
191
const isNode = {{{ nodeDetectionCode() }}};
192
if (!isNode) throw new Error('not compiled for this environment (did you build to HTML and try to run it not on the web, or set ENVIRONMENT to something - like node - and run it someplace else - like on the web?)');
193
#endif
194
195
#if ASSERTIONS
196
var nodeVersion = process.versions.node;
197
var numericVersion = nodeVersion.split('.').slice(0, 3);
198
numericVersion = (numericVersion[0] * 10000) + (numericVersion[1] * 100) + (numericVersion[2].split('-')[0] * 1);
199
if (numericVersion < {{{ MIN_NODE_VERSION }}}) {
200
throw new Error('This emscripten-generated code requires node {{{ formattedMinNodeVersion() }}} (detected v' + nodeVersion + ')');
201
}
202
#endif
203
204
// These modules will usually be used on Node.js. Load them eagerly to avoid
205
// the complexity of lazy-loading.
206
var fs = require('fs');
207
208
#if EXPORT_ES6
209
if (_scriptName.startsWith('file:')) {
210
scriptDirectory = require('path').dirname(require('url').fileURLToPath(_scriptName)) + '/';
211
}
212
#else
213
scriptDirectory = __dirname + '/';
214
#endif
215
216
#include "node_shell_read.js"
217
218
if (process.argv.length > 1) {
219
thisProgram = process.argv[1].replace(/\\/g, '/');
220
}
221
222
arguments_ = process.argv.slice(2);
223
224
#if !MODULARIZE
225
// MODULARIZE will export the module in the proper place outside, we don't need to export here
226
if (typeof module != 'undefined') {
227
module['exports'] = Module;
228
}
229
#endif
230
231
#if NODEJS_CATCH_EXIT
232
process.on('uncaughtException', (ex) => {
233
// suppress ExitStatus exceptions from showing an error
234
#if RUNTIME_DEBUG
235
dbg(`node: uncaughtException: ${ex}`)
236
#endif
237
if (ex !== 'unwind' && !(ex instanceof ExitStatus) && !(ex.context instanceof ExitStatus)) {
238
throw ex;
239
}
240
});
241
#endif
242
243
#if NODEJS_CATCH_REJECTION
244
// Without this older versions of node (< v15) will log unhandled rejections
245
// but return 0, which is not normally the desired behaviour. This is
246
// not be needed with node v15 and about because it is now the default
247
// behaviour:
248
// See https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode
249
var nodeMajor = process.versions.node.split(".")[0];
250
if (nodeMajor < 15) {
251
process.on('unhandledRejection', (reason) => { throw reason; });
252
}
253
#endif
254
255
quit_ = (status, toThrow) => {
256
process.exitCode = status;
257
throw toThrow;
258
};
259
260
#if WASM == 2
261
// If target shell does not support Wasm, load the JS version of the code.
262
if (typeof WebAssembly == 'undefined') {
263
eval(fs.readFileSync(locateFile('{{{ TARGET_BASENAME }}}.wasm.js'))+'');
264
}
265
#endif
266
267
} else
268
#endif // ENVIRONMENT_MAY_BE_NODE
269
#if ENVIRONMENT_MAY_BE_SHELL || ASSERTIONS
270
if (ENVIRONMENT_IS_SHELL) {
271
272
#if ENVIRONMENT.length && ASSERTIONS
273
const isNode = {{{ nodeDetectionCode() }}};
274
if (isNode || typeof window == 'object' || typeof WorkerGlobalScope != 'undefined') throw new Error('not compiled for this environment (did you build to HTML and try to run it not on the web, or set ENVIRONMENT to something - like node - and run it someplace else - like on the web?)');
275
#endif
276
277
#if ENVIRONMENT_MAY_BE_SHELL
278
readBinary = (f) => {
279
if (typeof readbuffer == 'function') {
280
return new Uint8Array(readbuffer(f));
281
}
282
let data = read(f, 'binary');
283
assert(typeof data == 'object');
284
return data;
285
};
286
287
readAsync = async (f) => readBinary(f);
288
289
globalThis.clearTimeout ??= (id) => {};
290
291
// spidermonkey lacks setTimeout but we use it above in readAsync.
292
globalThis.setTimeout ??= (f) => f();
293
294
// v8 uses `arguments_` whereas spidermonkey uses `scriptArgs`
295
arguments_ = globalThis.arguments || globalThis.scriptArgs;
296
297
if (typeof quit == 'function') {
298
quit_ = (status, toThrow) => {
299
// Unlike node which has process.exitCode, d8 has no such mechanism. So we
300
// have no way to set the exit code and then let the program exit with
301
// that code when it naturally stops running (say, when all setTimeouts
302
// have completed). For that reason, we must call `quit` - the only way to
303
// set the exit code - but quit also halts immediately. To increase
304
// consistency with node (and the web) we schedule the actual quit call
305
// using a setTimeout to give the current stack and any exception handlers
306
// a chance to run. This enables features such as addOnPostRun (which
307
// expected to be able to run code after main returns).
308
setTimeout(() => {
309
if (!(toThrow instanceof ExitStatus)) {
310
let toLog = toThrow;
311
if (toThrow && typeof toThrow == 'object' && toThrow.stack) {
312
toLog = [toThrow, toThrow.stack];
313
}
314
err(`exiting due to exception: ${toLog}`);
315
}
316
quit(status);
317
});
318
throw toThrow;
319
};
320
}
321
322
if (typeof print != 'undefined') {
323
// Prefer to use print/printErr where they exist, as they usually work better.
324
globalThis.console ??= /** @type{!Console} */({});
325
console.log = /** @type{!function(this:Console, ...*): undefined} */ (print);
326
console.warn = console.error = /** @type{!function(this:Console, ...*): undefined} */ (globalThis.printErr ?? print);
327
}
328
329
#if WASM == 2
330
// If target shell does not support Wasm, load the JS version of the code.
331
if (typeof WebAssembly == 'undefined') {
332
eval(read(locateFile('{{{ TARGET_BASENAME }}}.wasm.js'))+'');
333
}
334
#endif
335
#endif // ENVIRONMENT_MAY_BE_SHELL
336
337
} else
338
#endif // ENVIRONMENT_MAY_BE_SHELL || ASSERTIONS
339
340
// Note that this includes Node.js workers when relevant (pthreads is enabled).
341
// Node.js workers are detected as a combination of ENVIRONMENT_IS_WORKER and
342
// ENVIRONMENT_IS_NODE.
343
#if ENVIRONMENT_MAY_BE_WEB || ENVIRONMENT_MAY_BE_WORKER
344
if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) {
345
try {
346
scriptDirectory = new URL('.', _scriptName).href; // includes trailing slash
347
} catch {
348
// Must be a `blob:` or `data:` URL (e.g. `blob:http://site.com/etc/etc`), we cannot
349
// infer anything from them.
350
}
351
352
#if ENVIRONMENT.length && ASSERTIONS
353
if (!(typeof window == 'object' || typeof WorkerGlobalScope != 'undefined')) throw new Error('not compiled for this environment (did you build to HTML and try to run it not on the web, or set ENVIRONMENT to something - like node - and run it someplace else - like on the web?)');
354
#endif
355
356
#if PTHREADS && ENVIRONMENT_MAY_BE_NODE
357
// Differentiate the Web Worker from the Node Worker case, as reading must
358
// be done differently.
359
if (!ENVIRONMENT_IS_NODE)
360
#endif
361
{
362
#include "web_or_worker_shell_read.js"
363
}
364
} else
365
#endif // ENVIRONMENT_MAY_BE_WEB || ENVIRONMENT_MAY_BE_WORKER
366
#if AUDIO_WORKLET && ASSERTIONS
367
if (!ENVIRONMENT_IS_AUDIO_WORKLET)
368
#endif
369
{
370
#if ASSERTIONS
371
throw new Error('environment detection error');
372
#endif // ASSERTIONS
373
}
374
375
#if ENVIRONMENT_MAY_BE_NODE && (PTHREADS || WASM_WORKERS)
376
// Set up the out() and err() hooks, which are how we can print to stdout or
377
// stderr, respectively.
378
// Normally just binding console.log/console.error here works fine, but
379
// under node (with workers) we see missing/out-of-order messages so route
380
// directly to stdout and stderr.
381
// See https://github.com/emscripten-core/emscripten/issues/14804
382
var defaultPrint = console.log.bind(console);
383
var defaultPrintErr = console.error.bind(console);
384
if (ENVIRONMENT_IS_NODE) {
385
var utils = require('util');
386
var stringify = (a) => typeof a == 'object' ? utils.inspect(a) : a;
387
defaultPrint = (...args) => fs.writeSync(1, args.map(stringify).join(' ') + '\n');
388
defaultPrintErr = (...args) => fs.writeSync(2, args.map(stringify).join(' ') + '\n');
389
}
390
{{{ makeModuleReceiveWithVar('out', 'print', 'defaultPrint') }}}
391
{{{ makeModuleReceiveWithVar('err', 'printErr', 'defaultPrintErr') }}}
392
#else
393
{{{ makeModuleReceiveWithVar('out', 'print', 'console.log.bind(console)') }}}
394
{{{ makeModuleReceiveWithVar('err', 'printErr', 'console.error.bind(console)') }}}
395
#endif
396
397
#if ASSERTIONS
398
399
{{{ makeRemovedFSAssert('IDBFS') }}}
400
{{{ makeRemovedFSAssert('PROXYFS') }}}
401
{{{ makeRemovedFSAssert('WORKERFS') }}}
402
{{{ makeRemovedFSAssert('FETCHFS') }}}
403
{{{ makeRemovedFSAssert('ICASEFS') }}}
404
{{{ makeRemovedFSAssert('JSFILEFS') }}}
405
{{{ makeRemovedFSAssert('OPFS') }}}
406
407
#if !NODERAWFS
408
{{{ makeRemovedFSAssert('NODEFS') }}}
409
#endif
410
411
// perform assertions in shell.js after we set up out() and err(), as otherwise
412
// if an assertion fails it cannot print the message
413
#if PTHREADS
414
assert(
415
#if AUDIO_WORKLET
416
ENVIRONMENT_IS_AUDIO_WORKLET ||
417
#endif
418
ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER || ENVIRONMENT_IS_NODE, 'Pthreads do not work in this environment yet (need Web Workers, or an alternative to them)');
419
#else
420
#endif // PTHREADS
421
422
#if !ENVIRONMENT_MAY_BE_WEB
423
assert(!ENVIRONMENT_IS_WEB, 'web environment detected but not enabled at build time. Add `web` to `-sENVIRONMENT` to enable.');
424
#endif
425
426
#if !ENVIRONMENT_MAY_BE_WORKER
427
assert(!ENVIRONMENT_IS_WORKER, 'worker environment detected but not enabled at build time. Add `worker` to `-sENVIRONMENT` to enable.');
428
#endif
429
430
#if !ENVIRONMENT_MAY_BE_NODE
431
assert(!ENVIRONMENT_IS_NODE, 'node environment detected but not enabled at build time. Add `node` to `-sENVIRONMENT` to enable.');
432
#endif
433
434
#if !ENVIRONMENT_MAY_BE_SHELL
435
assert(!ENVIRONMENT_IS_SHELL, 'shell environment detected but not enabled at build time. Add `shell` to `-sENVIRONMENT` to enable.');
436
#endif
437
438
#endif // ASSERTIONS
439
440