blob: 8703af419643138513f0749c66ca761f71c89314 [file] [log] [blame] [edit]
//"use strict";
// Convert analyzed data to javascript. Everything has already been calculated
// before this stage, which just does the final conversion to JavaScript.
// Handy sets
var STRUCT_LIST = set('struct', 'list');
var addedLibraryItems = {};
var asmLibraryFunctions = [];
var SETJMP_LABEL = -1;
var INDENTATION = ' ';
var functionStubSigs = {};
var ALWAYS_EMITTED_I64_FUNCS = set('i64Add', 'i64Subtract', 'bitshift64Shl', 'bitshift64Lshr', 'bitshift64Ashr'); // even in side modules
// JSifier
function JSify(data, functionsOnly) {
//B.start('jsifier');
var mainPass = !functionsOnly;
var itemsDict = { type: [], GlobalVariableStub: [], functionStub: [], function: [], GlobalVariable: [], GlobalVariablePostSet: [] };
if (mainPass) {
var shellFile = SHELL_FILE ? SHELL_FILE : (BUILD_AS_SHARED_LIB || SIDE_MODULE ? 'shell_sharedlib.js' : 'shell.js');
// We will start to print out the data, but must do so carefully - we are
// dealing with potentially *huge* strings. Convenient replacements and
// manipulations may create in-memory copies, and we may OOM.
//
// Final shape that will be created:
// shell
// (body)
// preamble
// runtime
// generated code
// postamble
// global_vars
//
// First, we print out everything until the generated code. Then the
// functions will print themselves out as they are parsed. Finally, we
// will call finalCombiner in the main pass, to print out everything
// else. This lets us not hold any strings in memory, we simply print
// things out as they are ready.
var shellParts = read(shellFile).split('{{BODY}}');
print(processMacros(preprocess(shellParts[0], shellFile)));
var preFile = BUILD_AS_SHARED_LIB || SIDE_MODULE ? 'preamble_sharedlib.js' : 'preamble.js';
var pre = processMacros(preprocess(read(preFile).replace('{{RUNTIME}}', getRuntime()), preFile));
print(pre);
}
if (mainPass) {
// Add additional necessary items for the main pass. We can now do this since types are parsed (types can be used through
// generateStructInfo in library.js)
//B.start('jsifier-libload');
LibraryManager.load();
//B.stop('jsifier-libload');
var libFuncsToInclude;
if (INCLUDE_FULL_LIBRARY) {
assert(!(BUILD_AS_SHARED_LIB || SIDE_MODULE), 'Cannot have both INCLUDE_FULL_LIBRARY and BUILD_AS_SHARED_LIB/SIDE_MODULE set.')
libFuncsToInclude = (MAIN_MODULE || SIDE_MODULE) ? DEFAULT_LIBRARY_FUNCS_TO_INCLUDE.slice(0) : [];
for (var key in LibraryManager.library) {
if (!key.match(/__(deps|postset|inline|asm|sig)$/)) {
libFuncsToInclude.push(key);
}
}
} else {
libFuncsToInclude = DEFAULT_LIBRARY_FUNCS_TO_INCLUDE;
}
libFuncsToInclude.forEach(function(ident) {
var finalName = '_' + ident;
if (ident[0] === '$') {
finalName = ident.substr(1);
}
data.functionStubs.push({
intertype: 'functionStub',
finalName: finalName,
ident: '_' + ident
});
});
}
function processLibraryFunction(snippet, ident, finalName) {
// It is possible that when printing the function as a string on Windows, the js interpreter we are in returns the string with Windows
// line endings \r\n. This is undesirable, since line endings are managed in the form \n in the output for binary file writes, so
// make sure the endings are uniform.
snippet = snippet.toString().replace(/\r\n/gm,"\n");
assert(snippet.indexOf('XXX missing C define') == -1,
'Trying to include a library function with missing C defines: ' + finalName + ' | ' + snippet);
// name the function; overwrite if it's already named
snippet = snippet.replace(/function(?:\s+([^(]+))?\s*\(/, 'function ' + finalName + '(');
if (LIBRARY_DEBUG && !LibraryManager.library[ident + '__asm']) {
snippet = snippet.replace('{', '{ var ret = (function() { if (Runtime.debug) Module.printErr("[library call:' + finalName + ': " + Array.prototype.slice.call(arguments).map(Runtime.prettyPrint) + "]"); ');
snippet = snippet.substr(0, snippet.length-1) + '}).apply(this, arguments); if (Runtime.debug && typeof ret !== "undefined") Module.printErr(" [ return:" + Runtime.prettyPrint(ret)); return ret; \n}';
}
return snippet;
}
// functionStub
function functionStubHandler(item) {
// special logic
if (item.ident.startsWith('___cxa_find_matching_catch_')) {
var num = +item.ident.split('_').slice(-1)[0];
LibraryManager.library[item.ident.substr(1)] = function() {
return ___cxa_find_matching_catch.apply(null, arguments);
};
}
// note the signature
if (item.returnType && item.params) {
functionStubSigs[item.ident] = Functions.getSignature(item.returnType.text, item.params.map(function(arg) { return arg.type }), false);
}
function addFromLibrary(ident) {
if (ident in addedLibraryItems) return '';
addedLibraryItems[ident] = true;
// dependencies can be JS functions, which we just run
if (typeof ident == 'function') return ident();
// $ident's are special, we do not prefix them with a '_'.
if (ident[0] === '$') {
var finalName = ident.substr(1);
} else {
var finalName = '_' + ident;
}
// Don't replace implemented functions with library ones (which can happen when we add dependencies).
// Note: We don't return the dependencies here. Be careful not to end up where this matters
if (finalName in Functions.implementedFunctions) return '';
var noExport = false;
if ((!LibraryManager.library.hasOwnProperty(ident) && !LibraryManager.library.hasOwnProperty(ident + '__inline')) || SIDE_MODULE) {
if (!(finalName in IMPLEMENTED_FUNCTIONS)) {
if (VERBOSE || ident.substr(0, 11) !== 'emscripten_') { // avoid warning on emscripten_* functions which are for internal usage anyhow
if (!LINKABLE) {
if (ERROR_ON_UNDEFINED_SYMBOLS) error('unresolved symbol: ' + ident);
else if (VERBOSE || WARN_ON_UNDEFINED_SYMBOLS) warn('unresolved symbol: ' + ident);
}
}
}
if (!(MAIN_MODULE || SIDE_MODULE)) {
// emit a stub that will fail at runtime
LibraryManager.library[shortident] = new Function("Module['printErr']('missing function: " + shortident + "'); abort(-1);");
} else {
var target = (MAIN_MODULE ? '' : 'parent') + "Module['_" + shortident + "']";
if (SIDE_MODULE && (ident in ALWAYS_EMITTED_I64_FUNCS)) return ''; // we emit i64Add etc. even in side modules (small, and should be fast)
var assertion = '';
if (ASSERTIONS) assertion = 'if (!' + target + ') abort("external function \'' + shortident + '\' is missing. perhaps a side module was not linked in? if this function was expected to arrive from a system library, try to build the MAIN_MODULE with EMCC_FORCE_STDLIBS=1 in the environment");';
LibraryManager.library[shortident] = new Function(assertion + "return " + target + ".apply(null, arguments);");
if (SIDE_MODULE) {
// no dependencies, just emit the thunk
Functions.libraryFunctions[finalName] = 1;
return processLibraryFunction(LibraryManager.library[shortident], ident, finalName);
}
noExport = true;
}
}
var snippet = LibraryManager.library[ident];
var redirectedIdent = null;
var deps = LibraryManager.library[ident + '__deps'] || [];
deps.forEach(function(dep) {
if (typeof snippet === 'string' && !(dep in LibraryManager.library)) warn('missing library dependency ' + dep + ', make sure you are compiling with the right options (see #ifdefs in src/library*.js)');
});
var isFunction = false;
if (typeof snippet === 'string') {
var target = LibraryManager.library[snippet];
if (target) {
// Redirection for aliases. We include the parent, and at runtime make ourselves equal to it.
// This avoid having duplicate functions with identical content.
redirectedIdent = snippet;
deps.push(snippet);
snippet = '_' + snippet;
}
// In asm, we need to know about library functions. If there is a target, though, then no
// need to consider this a library function - we will call directly to it anyhow
if (!redirectedIdent && (typeof target == 'function' || /Math_\w+/.exec(snippet))) {
Functions.libraryFunctions[finalName] = 1;
}
} else if (typeof snippet === 'object') {
snippet = stringifyWithFunctions(snippet);
} else if (typeof snippet === 'function') {
isFunction = true;
snippet = processLibraryFunction(snippet, ident, finalName);
Functions.libraryFunctions[finalName] = 1;
}
var postsetId = ident + '__postset';
var postset = LibraryManager.library[postsetId];
if (postset && !addedLibraryItems[postsetId] && !SIDE_MODULE) {
addedLibraryItems[postsetId] = true;
itemsDict.GlobalVariablePostSet.push({
intertype: 'GlobalVariablePostSet',
JS: postset + ';'
});
}
if (redirectedIdent) {
deps = deps.concat(LibraryManager.library[redirectedIdent + '__deps'] || []);
}
// In asm, dependencies implemented in C might be needed by JS library functions.
// We don't know yet if they are implemented in C or not. To be safe, export such
// special cases.
[LIBRARY_DEPS_TO_AUTOEXPORT].forEach(function(special) {
deps.forEach(function(dep) {
if (dep == special && !EXPORTED_FUNCTIONS[dep]) {
EXPORTED_FUNCTIONS[dep] = 1;
}
});
});
if (VERBOSE) printErr('adding ' + finalName + ' and deps ' + deps + ' : ' + (snippet + '').substr(0, 40));
var depsText = (deps ? '\n' + deps.map(addFromLibrary).filter(function(x) { return x != '' }).join('\n') : '');
var contentText;
if (isFunction) {
contentText = snippet;
} else if (typeof snippet === 'string' && snippet.indexOf(';') == 0) {
contentText = 'var ' + finalName + snippet;
if (snippet[snippet.length-1] != ';' && snippet[snippet.length-1] != '}') contentText += ';';
} else {
contentText = 'var ' + finalName + '=' + snippet + ';';
}
var sig = LibraryManager.library[ident + '__sig'];
if (isFunction && sig && LibraryManager.library[ident + '__asm']) {
// asm library function, add it as generated code alongside the generated code
Functions.implementedFunctions[finalName] = sig;
asmLibraryFunctions.push(contentText);
contentText = ' ';
EXPORTED_FUNCTIONS[finalName] = 1;
Functions.libraryFunctions[finalName] = 2;
}
if ((EXPORT_ALL || (finalName in EXPORTED_FUNCTIONS)) && !noExport) {
contentText += '\nModule["' + finalName + '"] = ' + finalName + ';';
}
return depsText + contentText;
}
itemsDict.functionStub.push(item);
var shortident = item.ident.substr(1);
if (BUILD_AS_SHARED_LIB) {
// Shared libraries reuse the runtime of their parents.
item.JS = '';
} else {
// If this is not linkable, anything not in the library is definitely missing
if (item.ident in DEAD_FUNCTIONS) {
if (LibraryManager.library[shortident + '__asm']) {
warn('cannot kill asm library function ' + item.ident);
} else {
LibraryManager.library[shortident] = new Function("Module['printErr']('dead function: " + shortident + "'); abort(-1);");
delete LibraryManager.library[shortident + '__inline'];
delete LibraryManager.library[shortident + '__deps'];
}
}
item.JS = addFromLibrary(shortident);
}
}
// Final combiner
function finalCombiner() {
var splitPostSets = splitter(itemsDict.GlobalVariablePostSet, function(x) { return x.ident && x.dependencies });
itemsDict.GlobalVariablePostSet = splitPostSets.leftIn;
var orderedPostSets = splitPostSets.splitOut;
var limit = orderedPostSets.length * orderedPostSets.length;
for (var i = 0; i < orderedPostSets.length; i++) {
for (var j = i+1; j < orderedPostSets.length; j++) {
if (orderedPostSets[j].ident in orderedPostSets[i].dependencies) {
var temp = orderedPostSets[i];
orderedPostSets[i] = orderedPostSets[j];
orderedPostSets[j] = temp;
i--;
limit--;
assert(limit > 0, 'Could not sort postsets!');
break;
}
}
}
itemsDict.GlobalVariablePostSet = itemsDict.GlobalVariablePostSet.concat(orderedPostSets);
//
if (!mainPass) {
if (!Variables.generatedGlobalBase && !BUILD_AS_SHARED_LIB) {
Variables.generatedGlobalBase = true;
// Globals are done, here is the rest of static memory
if (!SIDE_MODULE) {
print('STATIC_BASE = ' + Runtime.GLOBAL_BASE + ';\n');
print('STATICTOP = STATIC_BASE + ' + Runtime.alignMemory(Variables.nextIndexedOffset) + ';\n');
} else {
print('gb = Runtime.alignMemory(getMemory({{{ STATIC_BUMP }}}, ' + MAX_GLOBAL_ALIGN + ' || 1));\n');
print('// STATICTOP = STATIC_BASE + ' + Runtime.alignMemory(Variables.nextIndexedOffset) + ';\n'); // comment as metadata only
}
if (BINARYEN) {
print('var STATIC_BUMP = {{{ STATIC_BUMP }}};');
}
}
var generated = itemsDict.function.concat(itemsDict.type).concat(itemsDict.GlobalVariableStub).concat(itemsDict.GlobalVariable);
print(generated.map(function(item) { return item.JS; }).join('\n'));
if (memoryInitialization.length > 0) {
// apply postsets directly into the big memory initialization
itemsDict.GlobalVariablePostSet = itemsDict.GlobalVariablePostSet.filter(function(item) {
var m;
if (m = /^HEAP([\dFU]+)\[([()>\d]+)\] *= *([()|\d{}\w_' ]+);?$/.exec(item.JS)) {
var type = getTypeFromHeap(m[1]);
var bytes = Runtime.getNativeTypeSize(type);
var target = eval(m[2]) << log2(bytes);
var value = m[3];
try {
value = eval(value);
} catch(e) {
// possibly function table {{{ FT_* }}} etc.
if (value.indexOf('{{ ') < 0) return true;
}
writeInt8s(memoryInitialization, target - Runtime.GLOBAL_BASE, value, type);
return false;
}
return true;
});
// write out the singleton big memory initialization value
if (USE_PTHREADS) {
print('if (!ENVIRONMENT_IS_PTHREAD) {') // Pthreads should not initialize memory again, since it's shared with the main thread.
}
print('/* memory initializer */ ' + makePointer(memoryInitialization, null, 'ALLOC_NONE', 'i8', 'Runtime.GLOBAL_BASE' + (SIDE_MODULE ? '+H_BASE' : ''), true));
if (USE_PTHREADS) {
print('}')
}
} else {
print('/* no memory initializer */'); // test purposes
}
if (!BUILD_AS_SHARED_LIB && !SIDE_MODULE) {
if (USE_PTHREADS) {
print('var tempDoublePtr;\n');
print('if (!ENVIRONMENT_IS_PTHREAD) tempDoublePtr = Runtime.alignMemory(allocate(12, "i8", ALLOC_STATIC), 8);\n');
} else {
print('var tempDoublePtr = ' + makeStaticAlloc(8) + '\n');
}
if (ASSERTIONS) print('assert(tempDoublePtr % 8 == 0);\n');
print('function copyTempFloat(ptr) { // functions, because inlining this code increases code size too much\n');
print(' HEAP8[tempDoublePtr] = HEAP8[ptr];\n');
print(' HEAP8[tempDoublePtr+1] = HEAP8[ptr+1];\n');
print(' HEAP8[tempDoublePtr+2] = HEAP8[ptr+2];\n');
print(' HEAP8[tempDoublePtr+3] = HEAP8[ptr+3];\n');
print('}\n');
print('function copyTempDouble(ptr) {\n');
print(' HEAP8[tempDoublePtr] = HEAP8[ptr];\n');
print(' HEAP8[tempDoublePtr+1] = HEAP8[ptr+1];\n');
print(' HEAP8[tempDoublePtr+2] = HEAP8[ptr+2];\n');
print(' HEAP8[tempDoublePtr+3] = HEAP8[ptr+3];\n');
print(' HEAP8[tempDoublePtr+4] = HEAP8[ptr+4];\n');
print(' HEAP8[tempDoublePtr+5] = HEAP8[ptr+5];\n');
print(' HEAP8[tempDoublePtr+6] = HEAP8[ptr+6];\n');
print(' HEAP8[tempDoublePtr+7] = HEAP8[ptr+7];\n');
print('}\n');
}
print('// {{PRE_LIBRARY}}\n'); // safe to put stuff here that statically allocates
return;
}
// Print out global variables and postsets TODO: batching
var legalizedI64sDefault = legalizedI64s;
legalizedI64s = false;
var globalsData = {functionStubs: []}
JSify(globalsData, true);
globalsData = null;
var generated = itemsDict.functionStub.concat(itemsDict.GlobalVariablePostSet);
generated.forEach(function(item) { print(indentify(item.JS || '', 2)); });
legalizedI64s = legalizedI64sDefault;
if (!BUILD_AS_SHARED_LIB && !SIDE_MODULE) {
print('STACK_BASE = STACKTOP = Runtime.alignMemory(STATICTOP);\n');
print('staticSealed = true; // seal the static portion of memory\n');
print('STACK_MAX = STACK_BASE + TOTAL_STACK;\n');
if (ASSERTIONS) print('assert(STACK_MAX < TOTAL_MEMORY, "TOTAL_MEMORY not big enough for stack");\n');
}
if (SPLIT_MEMORY) {
print('assert(STACK_MAX < SPLIT_MEMORY, "SPLIT_MEMORY size must be big enough so the entire static memory + stack can fit in one chunk, need " + STACK_MAX);\n');
}
if (asmLibraryFunctions.length > 0) {
print('// ASM_LIBRARY FUNCTIONS');
function fix(f) { // fix indenting to not confuse js optimizer
f = f.substr(f.indexOf('f')); // remove initial spaces before 'function'
f = f.substr(0, f.lastIndexOf('\n')+1); // remove spaces and last } XXX assumes function has multiple lines
return f + '}'; // add unindented } to match function
}
print(asmLibraryFunctions.map(fix).join('\n'));
}
if (abortExecution) throw 'Aborting compilation due to previous errors';
// This is the main 'post' pass. Print out the generated code that we have here, together with the
// rest of the output that we started to print out earlier (see comment on the
// "Final shape that will be created").
if (PRECISE_I64_MATH && (Types.preciseI64MathUsed || PRECISE_I64_MATH == 2)) {
if (SIDE_MODULE) {
print('// ASM_LIBRARY FUNCTIONS'); // fastLong.js etc. code is indeed asm library code
}
if (!INCLUDE_FULL_LIBRARY) {
// first row are utilities called from generated code, second are needed from fastLong
['i64Add', 'i64Subtract', 'bitshift64Shl', 'bitshift64Lshr', 'bitshift64Ashr',
'llvm_cttz_i32'].forEach(function(ident) {
var finalName = '_' + ident;
if (!Functions.libraryFunctions[finalName] || (ident[0] === 'l' && !addedLibraryItems[ident])) { // TODO: one-by-one in fastcomp glue mode
print(processLibraryFunction(LibraryManager.library[ident], ident, finalName)); // must be first to be close to generated code
Functions.implementedFunctions[finalName] = LibraryManager.library[ident + '__sig'];
Functions.libraryFunctions[finalName] = 2; // XXX
// limited dependency handling
var deps = LibraryManager.library[ident + '__deps'];
if (deps) {
deps.forEach(function(dep) {
assert(typeof dep == 'function');
var text = dep();
assert(text.indexOf('\n') < 0);
print('/* PRE_ASM */ ' + text + '\n');
});
}
}
});
}
// these may be duplicated in side modules and the main module without issue
print(processMacros(read('fastLong.js')));
print('// EMSCRIPTEN_END_FUNCS\n');
} else {
print('// EMSCRIPTEN_END_FUNCS\n');
}
if (HEADLESS) {
print('if (!ENVIRONMENT_IS_WEB) {');
print(read('headlessCanvas.js'));
print('\n');
print(read('headless.js').replace("'%s'", "'http://emscripten.org'").replace("'?%s'", "''").replace("'?%s'", "'/'").replace('%s,', 'null,').replace('%d', '0'));
print('}');
}
if (PROXY_TO_WORKER) {
print('if (ENVIRONMENT_IS_WORKER) {\n');
print(read('webGLWorker.js'));
print(read('proxyWorker.js'));
print('}');
}
if (DETERMINISTIC) {
print(read('deterministic.js'));
}
var postFile = BUILD_AS_SHARED_LIB || SIDE_MODULE ? 'postamble_sharedlib.js' : 'postamble.js';
var postParts = processMacros(preprocess(read(postFile), postFile)).split('{{GLOBAL_VARS}}');
print(postParts[0]);
print(postParts[1]);
var shellParts = read(shellFile).split('{{BODY}}');
print(processMacros(preprocess(shellParts[1], shellFile)));
// Print out some useful metadata
if (RUNNING_JS_OPTS || PGO) {
var generatedFunctions = JSON.stringify(keys(Functions.implementedFunctions));
if (PGO) {
print('PGOMonitor.allGenerated = ' + generatedFunctions + ';\nremoveRunDependency("pgo");\n');
}
if (RUNNING_JS_OPTS) {
print('// EMSCRIPTEN_GENERATED_FUNCTIONS: ' + generatedFunctions + '\n');
}
}
PassManager.serialize();
}
// Data
if (mainPass) {
data.functionStubs.forEach(functionStubHandler);
}
//B.start('jsifier-fc');
finalCombiner();
//B.stop('jsifier-fc');
dprint('framework', 'Big picture: Finishing JSifier, main pass=' + mainPass);
//B.stop('jsifier');
}