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