blob: dc834884c0869f04489e54ebe77d0453978e5d9d [file] [log] [blame]
/**
* @license
* Copyright 2011 The Emscripten Authors
* SPDX-License-Identifier: MIT
*/
//"use strict";
// Various namespace-like modules
var STACK_ALIGN = 16;
var Variables = {
globals: {},
indexedGlobals: {}, // for indexed globals, ident ==> index
// Used in calculation of indexed globals
nextIndexedOffset: 0,
resolveAliasToIdent: function(ident) {
while (1) {
var varData = Variables.globals[ident];
if (!(varData && varData.targetIdent)) break;
ident = varData.targetIdent; // might need to eval to turn (6) into 6
}
return ident;
},
};
var Types = {
types: {},
};
// Constructs an array ['a0', 'a1', 'a2', ..., 'a(n-1)']
function genArgSequence(n) {
var args = [];
for(var i = 0; i < n; ++i) {
args.push('a'+i);
}
return args;
}
var Functions = {
// All functions that will be implemented in this file. Maps id to signature
implementedFunctions: {},
// functions added from the library. value 2 means asmLibraryFunction
libraryFunctions: {},
};
var LibraryManager = {
library: null,
structs: {},
loaded: false,
libraries: [],
has: function(name) {
return this.libraries.indexOf(name) >= 0;
},
load: function() {
if (this.library) return;
// Core system libraries (always linked against)
var libraries = [
'library.js',
'library_stack.js',
'library_formatString.js',
'library_math.js',
'library_path.js',
'library_signals.js',
'library_syscall.js',
'library_html5.js',
'library_stack_trace.js',
'library_wasi.js',
'library_int53.js'
];
if (!EXCEPTION_HANDLING) {
if (!DISABLE_EXCEPTION_THROWING) {
libraries.push('library_exceptions.js');
} else {
libraries.push('library_exceptions_stub.js');
}
}
if (MINIMAL_RUNTIME) {
// Classic runtime brings in string-related functions in the runtime preamble, by including
// runtime_strings_extra.js that contain the same contents as library_strings.js does. In
// MINIMAL_RUNTIME those string functions are available as JS library functions instead from
// library_strings.js, to avoid unconditionally bringing in extra code to the build.
libraries.push('library_strings.js');
} else {
libraries.push('library_browser.js');
}
if (USE_PTHREADS) { // TODO: Currently WebGL proxying makes pthreads library depend on WebGL.
libraries.push('library_webgl.js');
}
if (FILESYSTEM) {
// Core filesystem libraries (always linked against, unless -s FILESYSTEM=0 is specified)
libraries = libraries.concat([
'library_fs.js',
'library_memfs.js',
'library_tty.js',
'library_pipefs.js', // ok to include it by default since it's only used if the syscall is used
'library_sockfs.js', // ok to include it by default since it's only used if the syscall is used
]);
if (RAW_OS) {
// Enable NODERAWFS, which also requires NODEFS
if (SYSTEM_JS_LIBRARIES.indexOf('library_nodefs.js') < 0) {
libraries.push('library_nodefs.js');
}
libraries.push('library_noderawfs.js');
}
}
// Additional JS libraries (without AUTO_JS_LIBRARIES, link to these explicitly via -lxxx.js)
if (AUTO_JS_LIBRARIES) {
libraries = libraries.concat([
'library_webgl.js',
'library_openal.js',
'library_sdl.js',
'library_glut.js',
'library_xlib.js',
'library_egl.js',
'library_glfw.js',
'library_uuid.js',
'library_glew.js',
'library_idbstore.js',
'library_async.js'
]);
} else {
if (ASYNCIFY) {
libraries.push('library_async.js');
}
if (USE_SDL == 1) {
libraries.push('library_sdl.js');
}
if (USE_SDL == 2) {
libraries.push('library_egl.js', 'library_webgl.js');
}
}
// Add any explicitly specified system JS libraries to link to, add those to link.
libraries = libraries.concat(SYSTEM_JS_LIBRARIES)
if (LZ4) {
libraries.push('library_lz4.js');
}
if (MAX_WEBGL_VERSION >= 2) {
libraries.push('library_webgl2.js');
}
if (LEGACY_GL_EMULATION) {
libraries.push('library_glemu.js');
}
if (USE_WEBGPU) {
libraries.push('library_webgpu.js');
libraries.push('library_html5_webgpu.js');
}
if (BOOTSTRAPPING_STRUCT_INFO) {
libraries = [
'library_bootstrap.js',
'library_stack.js',
'library_formatString.js',
'library_stack_trace.js'
];
}
// Deduplicate libraries to avoid processing any library file multiple times
libraries = libraries.filter(function(item, pos) {
return libraries.indexOf(item) == pos;
});
// Save the list for has() queries later.
this.libraries = libraries;
for (var i = 0; i < libraries.length; i++) {
var filename = libraries[i];
var src = read(filename);
var processed = undefined;
try {
processed = processMacros(preprocess(src, filename));
eval(processed);
} catch(e) {
var details = [e, e.lineNumber ? 'line number: ' + e.lineNumber : ''];
if (VERBOSE) {
details.push((e.stack || "").toString().replace('Object.<anonymous>', filename));
}
if (processed) {
error('failure to execute js library "' + filename + '": ' + details);
if (VERBOSE) {
error('preprocessed source (you can run a js engine on this to get a clearer error message sometimes):\n=============\n' + processed + '\n=============');
} else {
error('use -s VERBOSE to see more details')
}
} else {
error('failure to process js library "' + filename + '": ' + details);
if (VERBOSE) {
error('original source:\n=============\n' + src + '\n=============');
} else {
error('use -s VERBOSE to see more details')
}
}
throw e;
}
}
// apply synonyms. these are typically not speed-sensitive, and doing it this way makes it possible to not include hacks in the compiler
// (and makes it simpler to switch between SDL versions, fastcomp and non-fastcomp, etc.).
var lib = LibraryManager.library;
libloop: for (var x in lib) {
if (x.lastIndexOf('__') > 0) continue; // ignore __deps, __*
if (lib[x + '__asm']) continue; // ignore asm library functions, those need to be fully optimized
if (typeof lib[x] === 'string') {
var target = x;
while (typeof lib[target] === 'string') {
// ignore code and variable assignments, aliases are just simple names
if (lib[target].search(/[=({; ]/) >= 0) continue libloop;
// ignore trivial pass-throughs to Math.*
if (lib[target].indexOf('Math_') == 0) continue libloop;
target = lib[target];
}
if (!isNaN(target)) continue; // This is a number, and so cannot be an alias target.
if (typeof lib[target] === 'undefined' || typeof lib[target] === 'function') {
// When functions are aliased, the alias target must provide a signature for the function so that an efficient form of forwarding can be implemented.
// Primarily read the signature on the alias, and secondarily on the target.
function testStringType(sig) {
if (typeof lib[sig] !== 'undefined' && typeof typeof lib[sig] !== 'string') {
error(sig + ' should be a string! (was ' + typeof lib[sig]);
}
}
testStringType(x + '__sig');
testStringType(target + '__sig');
if (typeof lib[x + '__sig'] === 'string' && typeof lib[target + '__sig'] === 'string' && lib[x + '__sig'] != lib[target + '__sig']) {
error(x + '__sig (' + lib[x + '__sig'] + ') differs from ' + target + '__sig (' + lib[target + '__sig'] + ')');
}
var sig = lib[x + '__sig'] || lib[target + '__sig'];
if (typeof sig !== 'string') {
error('Function ' + x + ' aliases to target function ' + target + ', but neither the alias or the target provide a signature. Please add a ' + target + "__sig: 'vifj...' annotation or a " + x + "__sig: 'vifj...' annotation to describe the type of function forwarding that is needed!");
}
var argCount = sig.length - 1;
var ret = sig == 'v' ? '' : 'return ';
var args = genArgSequence(argCount).join(',');
lib[x] = new Function(args, ret + '_' + target + '(' + args + ');');
if (!lib[x + '__deps']) lib[x + '__deps'] = [];
lib[x + '__deps'].push(target);
}
}
}
if (WASM_BACKEND) {
// all asm.js methods should just be run in JS. We should optimize them eventually into wasm. TODO
for (var x in lib) {
if (lib[x + '__asm']) {
lib[x + '__asm'] = undefined;
}
}
}
/*
// export code for CallHandlers.h
printErr('============================');
for (var x in this.library) {
var y = this.library[x];
if (typeof y === 'string' && x.indexOf('__sig') < 0 && x.indexOf('__postset') < 0 && y.indexOf(' ') < 0) {
printErr('DEF_REDIRECT_HANDLER(' + x + ', ' + y + ');');
}
}
printErr('============================');
for (var x in this.library) {
var y = this.library[x];
if (typeof y === 'string' && x.indexOf('__sig') < 0 && x.indexOf('__postset') < 0 && y.indexOf(' ') < 0) {
printErr(' SETUP_CALL_HANDLER(' + x + ');');
}
}
printErr('============================');
// end export code for CallHandlers.h
*/
this.loaded = true;
},
// Given an ident, see if it is an alias for something, and so forth, returning
// the earliest ancestor (the root)
getRootIdent: function(ident) {
if (!this.library) return null;
var ret = LibraryManager.library[ident];
if (!ret) return null;
var last = ident;
while (typeof ret === 'string') {
last = ret;
ret = LibraryManager.library[ret];
}
return last;
},
isStubFunction: function(ident) {
if (SIDE_MODULE == 1) return false; // cannot eliminate these, as may be implement in the main module and imported by us
var libCall = LibraryManager.library[ident.substr(1)];
return typeof libCall === 'function' && libCall.toString().replace(/\s/g, '') === 'function(){}'
&& !(ident in Functions.implementedFunctions);
}
};
if (!BOOTSTRAPPING_STRUCT_INFO) {
// Load struct and define information.
var temp = JSON.parse(read(STRUCT_INFO));
C_STRUCTS = temp.structs;
C_DEFINES = temp.defines;
} else {
C_STRUCTS = {};
C_DEFINES = {};
}
// Safe way to access a C define. We check that we don't add library functions with missing defines.
function cDefine(key) {
if (key in C_DEFINES) return C_DEFINES[key];
throw 'Missing C define ' + key + '! If you just added it to struct_info.json, you need to ./emcc --clear-cache';
}
var EXPORTED_RUNTIME_METHODS_SET = set(EXPORTED_RUNTIME_METHODS.concat(EXTRA_EXPORTED_RUNTIME_METHODS));
EXPORTED_RUNTIME_METHODS = unset(EXPORTED_RUNTIME_METHODS_SET);
EXTRA_EXPORTED_RUNTIME_METHODS = [];
function isFSPrefixed(name) {
return name.length > 3 && name[0] === 'F' && name[1] === 'S' && name[2] === '_';
}
// forcing the filesystem exports a few things by default
function isExportedByForceFilesystem(name) {
return name === 'FS_createFolder' ||
name === 'FS_createPath' ||
name === 'FS_createDataFile' ||
name === 'FS_createPreloadedFile' ||
name === 'FS_createLazyFile' ||
name === 'FS_createLink' ||
name === 'FS_createDevice' ||
name === 'FS_unlink' ||
name === 'getMemory' ||
name === 'addRunDependency' ||
name === 'removeRunDependency';
}
// export parts of the JS runtime that the user asked for
function exportRuntime() {
// optionally export something.
// in ASSERTIONS mode we show a useful error if it is used without
// being exported. how we show the message depends on whether it's
// a function (almost all of them) or a number.
function maybeExport(name, isNumber) {
// if requested to be exported, export it
if (name in EXPORTED_RUNTIME_METHODS_SET) {
var exported = name;
// the exported name may differ from the internal name
if (isFSPrefixed(exported)) {
// this is a filesystem value, FS.x exported as FS_x
exported = 'FS.' + exported.substr(3);
} else if (exported === 'print') {
exported = 'out';
} else if (exported === 'printErr') {
exported = 'err';
}
return 'Module["' + name + '"] = ' + exported + ';';
}
// do not export it. but if ASSERTIONS, emit a
// stub with an error, so the user gets a message
// if it is used, that they should export it
if (ASSERTIONS) {
// check if it already exists, to support EXPORT_ALL and other cases
// (we could optimize this, but in ASSERTIONS mode code size doesn't
// matter anyhow)
var extra = '';
if (isExportedByForceFilesystem(name)) {
extra = '. Alternatively, forcing filesystem support (-s FORCE_FILESYSTEM=1) can export this for you';
}
if (!isNumber) {
return 'if (!Object.getOwnPropertyDescriptor(Module, "' + name + '")) Module["' + name + '"] = function() { abort("\'' + name + '\' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)' + extra + '") };';
} else {
return 'if (!Object.getOwnPropertyDescriptor(Module, "' + name + '")) Object.defineProperty(Module, "' + name + '", { configurable: true, get: function() { abort("\'' + name + '\' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)' + extra + '") } });';
}
}
return '';
}
function maybeExportNumber(name) {
return maybeExport(name, true);
}
// All possible runtime elements to export
var runtimeElements = [
'intArrayFromString',
'intArrayToString',
'ccall',
'cwrap',
'setValue',
'getValue',
'allocate',
'getMemory',
'UTF8ArrayToString',
'UTF8ToString',
'stringToUTF8Array',
'stringToUTF8',
'lengthBytesUTF8',
'stackTrace',
'addOnPreRun',
'addOnInit',
'addOnPreMain',
'addOnExit',
'addOnPostRun',
'writeStringToMemory',
'writeArrayToMemory',
'writeAsciiToMemory',
'addRunDependency',
'removeRunDependency',
'FS_createFolder',
'FS_createPath',
'FS_createDataFile',
'FS_createPreloadedFile',
'FS_createLazyFile',
'FS_createLink',
'FS_createDevice',
'FS_unlink',
'dynamicAlloc',
'loadDynamicLibrary',
'loadWebAssemblyModule',
'getLEB',
'getFunctionTables',
'alignFunctionTables',
'registerFunctions',
'addFunction',
'removeFunction',
'getFuncWrapper',
'prettyPrint',
'makeBigInt',
'dynCall',
'getCompilerSetting',
'print',
'printErr',
'getTempRet0',
'setTempRet0',
'callMain',
'abort',
];
function isJsLibraryConfigIdentifier(ident) {
return ident.endsWith('__sig') || ident.endsWith('__proxy') || ident.endsWith('__asm') || ident.endsWith('__inline')
|| ident.endsWith('__deps') || ident.endsWith('__postset') || ident.endsWith('__docs') || ident.endsWith('__import');
}
// Add JS library elements such as FS, GL, ENV, etc. These are prefixed with
// '$ which indicates they are JS methods.
for (var ident in LibraryManager.library) {
if (ident[0] === '$' && !isJsLibraryConfigIdentifier(ident)) {
runtimeElements.push(ident.substr(1));
}
}
if (!MINIMAL_RUNTIME) {
// MINIMAL_RUNTIME has moved these functions to library_strings.js
runtimeElements = runtimeElements.concat([
'warnOnce',
'stackSave',
'stackRestore',
'stackAlloc',
'AsciiToString',
'stringToAscii',
'UTF16ToString',
'stringToUTF16',
'lengthBytesUTF16',
'UTF32ToString',
'stringToUTF32',
'lengthBytesUTF32',
'allocateUTF8',
'allocateUTF8OnStack'
]);
}
if (STACK_OVERFLOW_CHECK) {
runtimeElements.push('writeStackCookie');
runtimeElements.push('checkStackCookie');
}
if (USE_PTHREADS) {
// In pthreads mode, the following functions always need to be exported to
// Module for closure compiler, and also for MODULARIZE (so worker.js can
// access them).
var threadExports = ['PThread', '_pthread_self'];
if (WASM) {
threadExports.push('wasmMemory');
}
if (!MINIMAL_RUNTIME) {
threadExports.push('ExitStatus');
}
threadExports.forEach(function(x) {
EXPORTED_RUNTIME_METHODS_SET[x] = 1;
runtimeElements.push(x);
});
}
if (SUPPORT_BASE64_EMBEDDING) {
runtimeElements.push('intArrayFromBase64');
runtimeElements.push('tryParseAsDataURI');
}
// dynCall_* methods are not hardcoded here, as they
// depend on the file being compiled. check for them
// and add them.
for (var name in EXPORTED_RUNTIME_METHODS_SET) {
if (/^dynCall_/.test(name)) {
// a specific dynCall; add to the list
runtimeElements.push(name);
}
}
var runtimeNumbers = [
'ALLOC_NORMAL',
'ALLOC_STACK',
'ALLOC_DYNAMIC',
'ALLOC_NONE',
];
if (ASSERTIONS) {
// check all exported things exist, warn about typos
for (var name in EXPORTED_RUNTIME_METHODS_SET) {
if (runtimeElements.indexOf(name) < 0 &&
runtimeNumbers.indexOf(name) < 0) {
printErr('warning: invalid item (maybe a typo?) in EXPORTED_RUNTIME_METHODS: ' + name);
}
}
}
return runtimeElements.map(function(name) {
return maybeExport(name);
}).join('\n') + runtimeNumbers.map(function(name) {
return maybeExportNumber(name);
}).join('\n');
}
var PassManager = {
serialize: function() {
print('\n//FORWARDED_DATA:' + JSON.stringify({
Functions: Functions,
EXPORTED_FUNCTIONS: EXPORTED_FUNCTIONS,
STATIC_BUMP: STATIC_BUMP, // updated with info from JS
DYNAMICTOP_PTR: DYNAMICTOP_PTR,
ATINITS: ATINITS.join('\n'),
ATMAINS: ATMAINS.join('\n'),
ATEXITS: ATEXITS.join('\n'),
}));
},
load: function(json) {
var data = JSON.parse(json);
for (var i in data.Types) {
Types[i] = data.Types[i];
}
for (var i in data.Variables) {
Variables[i] = data.Variables[i];
}
for (var i in data.Functions) {
Functions[i] = data.Functions[i];
}
EXPORTED_FUNCTIONS = data.EXPORTED_FUNCTIONS;
/*
print('\n//LOADED_DATA:' + JSON.stringify({
Types: Types,
Variables: Variables,
Functions: Functions
}));
*/
}
};