//"use strict"; // Implementation details for the 'runtime environment' we generate in // JavaScript. The Runtime object itself is used both during compilation, // and is available at runtime (dynamic compilation). The RuntimeGenerator // helps to create the Runtime object (written so that the Runtime object // itself is as optimized as possible - no unneeded runtime checks). var RuntimeGenerator = { alloc: function(size, type, sep, ignoreAlign) { sep = sep || ';'; var ret = type + 'TOP'; ret += sep + type + 'TOP = (' + type + 'TOP + ' + size + ')|0'; if ({{{ STACK_ALIGN }}} > 1 && !ignoreAlign) { ret += sep + RuntimeGenerator.alignMemory(type + 'TOP', {{{ STACK_ALIGN }}}); } return ret; }, // An allocation that lives as long as the current function call stackAlloc: function(size, sep) { sep = sep || ';'; var ret = RuntimeGenerator.alloc(size, 'STACK', sep, (isNumber(size) && parseInt(size) % {{{ STACK_ALIGN }}} == 0)); if (ASSERTIONS) { ret += sep + '(assert(' + asmCoercion('(STACKTOP|0) < (STACK_MAX|0)', 'i32') + ')|0)'; } return ret; }, stackEnter: function(initial, force) { if (initial === 0 && SKIP_STACK_IN_SMALL && !force) return ''; var ret = 'var sp=0;sp=STACKTOP'; if (initial > 0) ret += ';STACKTOP=(STACKTOP+' + initial + ')|0'; assert(initial % Runtime.STACK_ALIGN == 0); if (ASSERTIONS && Runtime.STACK_ALIGN == 4) { ret += '; (assert(' + asmCoercion('!(STACKTOP&3)', 'i32') + ')|0)'; } if (ASSERTIONS) { ret += '; (assert(' + asmCoercion('(STACKTOP|0) < (STACK_MAX|0)', 'i32') + ')|0)'; } return ret; }, stackExit: function(initial, force) { if (initial === 0 && SKIP_STACK_IN_SMALL && !force) return ''; return 'STACKTOP=sp'; }, // An allocation that cannot normally be free'd (except through sbrk, which once // called, takes control of STATICTOP) staticAlloc: function(size) { if (ASSERTIONS) size = '(assert(!staticSealed),' + size + ')'; // static area must not be sealed #if USE_PTHREADS if (typeof ENVIRONMENT_IS_PTHREAD !== 'undefined' && ENVIRONMENT_IS_PTHREAD) throw 'Runtime.staticAlloc is not available in pthreads!'; // This is because each worker has its own copy of STATICTOP, of which main thread is authoritative. #endif var ret = RuntimeGenerator.alloc(size, 'STATIC'); return ret; }, // allocation on the top of memory, adjusted dynamically by sbrk dynamicAlloc: function(size) { if (ASSERTIONS) size = '(assert(DYNAMICTOP > 0),' + size + ')'; // dynamic area must be ready #if USE_PTHREADS if (typeof ENVIRONMENT_IS_PTHREAD !== 'undefined' && ENVIRONMENT_IS_PTHREAD) throw 'Runtime.dynamicAlloc is not available in pthreads!'; // This is because each worker has its own copy of DYNAMICTOP, of which main thread is authoritative. #endif var ret = RuntimeGenerator.alloc(size, 'DYNAMIC'); if (SAFE_HEAP) ret += '; if (asm) { Runtime.setDynamicTop(DYNAMICTOP); }'; ret += '; if (DYNAMICTOP >= TOTAL_MEMORY) { var success = enlargeMemory(); if (!success) { DYNAMICTOP = ret; '; if (SAFE_HEAP) ret += 'if (asm) { Runtime.setDynamicTop(DYNAMICTOP); }'; ret += ' return 0; } }' return ret; }, forceAlign: function(target, quantum) { quantum = quantum || {{{ QUANTUM_SIZE }}}; if (quantum == 1) return target; if (isNumber(target) && isNumber(quantum)) { return Math.ceil(target/quantum)*quantum; } else if (isNumber(quantum) && isPowerOfTwo(quantum)) { return '(((' +target + ')+' + (quantum-1) + ')&' + -quantum + ')'; } return 'Math.ceil((' + target + ')/' + quantum + ')*' + quantum; }, alignMemory: function(target, quantum) { if (typeof quantum !== 'number') { quantum = '(quantum ? quantum : {{{ STACK_ALIGN }}})'; } return target + ' = ' + RuntimeGenerator.forceAlign(target, quantum); }, // Given two 32-bit unsigned parts of an emulated 64-bit number, combine them into a JS number (double). // Rounding is inevitable if the number is large. This is a particular problem for small negative numbers // (-1 will be rounded!), so handle negatives separately and carefully makeBigInt: function(low, high, unsigned) { var unsignedRet = '(' + asmCoercion(makeSignOp(low, 'i32', 'un', 1, 1), 'double') + '+(' + asmCoercion(makeSignOp(high, 'i32', 'un', 1, 1), 'double') + '*' + asmEnsureFloat(4294967296, 'double') + '))'; var signedRet = '(' + asmCoercion(makeSignOp(low, 'i32', 'un', 1, 1), 'double') + '+(' + asmCoercion(makeSignOp(high, 'i32', 're', 1, 1), 'double') + '*' + asmEnsureFloat(4294967296, 'double') + '))'; if (typeof unsigned === 'string') return '(' + unsigned + ' ? ' + unsignedRet + ' : ' + signedRet + ')'; return unsigned ? unsignedRet : signedRet; } }; function unInline(name_, params) { var src = '(function(' + params + ') { var ret = ' + RuntimeGenerator[name_].apply(null, params) + '; return ret; })'; var ret = eval(src); return ret; } var Compiletime = { isPointerType: isPointerType, isStructType: isStructType, isNumberType: function(type) { return type in Compiletime.INT_TYPES || type in Compiletime.FLOAT_TYPES; }, INT_TYPES: set('i1', 'i8', 'i16', 'i32', 'i64'), FLOAT_TYPES: set('float', 'double'), }; var Runtime = { // When a 64 bit long is returned from a compiled function the least significant // 32 bit word is passed in the return value, but the most significant 32 bit // word is placed in tempRet0. This provides an accessor for that value. setTempRet0: function(value) { tempRet0 = value; }, getTempRet0: function() { return tempRet0; }, stackSave: function() { return STACKTOP; }, stackRestore: function(stackTop) { STACKTOP = stackTop; }, //! Returns the size of a type, as C/C++ would have it (in 32-bit), in bytes. //! @param type The type, by name. getNativeTypeSize: function(type) { switch (type) { case 'i1': case 'i8': return 1; case 'i16': return 2; case 'i32': return 4; case 'i64': return 8; case 'float': return 4; case 'double': return 8; default: { if (type[type.length-1] === '*') { return Runtime.QUANTUM_SIZE; // A pointer } else if (type[0] === 'i') { var bits = parseInt(type.substr(1)); assert(bits % 8 === 0); return bits/8; } else { return 0; } } } }, //! Returns the size of a structure field, as C/C++ would have it (in 32-bit, //! for now). //! @param type The type, by name. getNativeFieldSize: function(type) { return Math.max(Runtime.getNativeTypeSize(type), Runtime.QUANTUM_SIZE); }, STACK_ALIGN: {{{ STACK_ALIGN }}}, // This must be called before reading a double or i64 vararg. It will bump the pointer properly. // It also does an assert on i32 values, so it's nice to call it before all varargs calls. prepVararg: function(ptr, type) { if (type === 'double' || type === 'i64') { // move so the load is aligned if (ptr & 7) { assert((ptr & 7) === 4); ptr += 4; } } else { assert((ptr & 3) === 0); } return ptr; }, // type can be a native type or a struct (or null, for structs we only look at size here) getAlignSize: function(type, size, vararg) { // we align i64s and doubles on 64-bit boundaries, unlike x86 if (!vararg && (type == 'i64' || type == 'double')) return 8; if (!type) return Math.min(size, 8); // align structures internally to 64 bits return Math.min(size || (type ? Runtime.getNativeFieldSize(type) : 0), Runtime.QUANTUM_SIZE); }, dynCall: function(sig, ptr, args) { if (args && args.length) { #if ASSERTIONS assert(args.length == sig.length-1); #endif if (!args.splice) args = Array.prototype.slice.call(args); args.splice(0, 0, ptr); #if ASSERTIONS assert(('dynCall_' + sig) in Module, 'bad function pointer type - no table for sig \'' + sig + '\''); #endif return Module['dynCall_' + sig].apply(null, args); } else { #if ASSERTIONS assert(sig.length == 1); #endif #if ASSERTIONS assert(('dynCall_' + sig) in Module, 'bad function pointer type - no table for sig \'' + sig + '\''); #endif return Module['dynCall_' + sig].call(null, ptr); } }, #if EMULATED_FUNCTION_POINTERS getFunctionTables: function(module) { if (!module) module = Module; var tables = {}; for (var t in module) { if (/^FUNCTION_TABLE_.*/.test(t)) { var table = module[t]; if (typeof table === 'object') tables[t.substr('FUNCTION_TABLE_'.length)] = table; } } return tables; }, alignFunctionTables: function(module) { var tables = Runtime.getFunctionTables(module); var maxx = 0; for (var sig in tables) { maxx = Math.max(maxx, tables[sig].length); } assert(maxx >= 0); for (var sig in tables) { var table = tables[sig]; while (table.length < maxx) table.push(0); } return maxx; }, registerFunctions: function(sigs, newModule) { sigs.forEach(function(sig) { if (!Module['FUNCTION_TABLE_' + sig]) { Module['FUNCTION_TABLE_' + sig] = []; } }); var oldMaxx = Runtime.alignFunctionTables(); // align the new tables we may have just added var newMaxx = Runtime.alignFunctionTables(newModule); var maxx = oldMaxx + newMaxx; sigs.forEach(function(sig) { var newTable = newModule['FUNCTION_TABLE_' + sig]; var oldTable = Module['FUNCTION_TABLE_' + sig]; assert(newTable !== oldTable); assert(oldTable.length === oldMaxx); for (var i = 0; i < newTable.length; i++) { oldTable.push(newTable[i]); } assert(oldTable.length === maxx); }); assert(maxx === Runtime.alignFunctionTables()); // align the ones we didn't touch }, #endif functionPointers: new Array(RESERVED_FUNCTION_POINTERS), addFunction: function(func) { #if EMULATED_FUNCTION_POINTERS == 0 for (var i = 0; i < Runtime.functionPointers.length; i++) { if (!Runtime.functionPointers[i]) { Runtime.functionPointers[i] = func; return {{{ FUNCTION_POINTER_ALIGNMENT }}}*(1 + i); } } throw 'Finished up all reserved function pointers. Use a higher value for RESERVED_FUNCTION_POINTERS.'; #else Runtime.alignFunctionTables(); // XXX we should rely on this being an invariant var tables = Runtime.getFunctionTables(); var ret = -1; for (var sig in tables) { var table = tables[sig]; if (ret < 0) ret = table.length; else assert(ret === table.length); table.push(func); } return ret; #endif }, removeFunction: function(index) { #if EMULATED_FUNCTION_POINTERS == 0 Runtime.functionPointers[(index-{{{ FUNCTION_POINTER_ALIGNMENT }}})/{{{ FUNCTION_POINTER_ALIGNMENT }}}] = null; #else Runtime.alignFunctionTables(); // XXX we should rely on this being an invariant var tables = Runtime.getFunctionTables(); for (var sig in tables) { tables[sig][index] = null; } #endif }, #if RELOCATABLE loadedDynamicLibraries: [], loadDynamicLibrary: function(lib) { // TODO: addRunDep etc., do asynchronously when in the browser. for now we assume we can do a sync xhr, no mem init files in libs, and we ignore the sync xhr lag var src = Module['read'](lib); var libModule = eval(src)( Runtime.alignFunctionTables(), Module ); // add symbols into global namespace TODO: weak linking etc. for (var sym in libModule) { if (!Module.hasOwnProperty(sym)) { Module[sym] = libModule[sym]; } #if ASSERTIONS == 2 else if (sym[0] === '_') { var curr = Module[sym], next = libModule[sym]; // don't warn on functions - might be odr, linkonce_odr, etc. if (!(typeof curr === 'function' && typeof next === 'function')) { Module.printErr("warning: trying to dynamically load symbol '" + sym + "' (from '" + lib + "') that already exists (duplicate symbol? or weak linking, which isn't supported yet?)"); // + [curr, ' vs ', next]); } } #endif } Runtime.loadedDynamicLibraries.push(libModule); }, #endif warnOnce: function(text) { if (!Runtime.warnOnce.shown) Runtime.warnOnce.shown = {}; if (!Runtime.warnOnce.shown[text]) { Runtime.warnOnce.shown[text] = 1; Module.printErr(text); } }, funcWrappers: {}, getFuncWrapper: function(func, sig) { assert(sig); if (!Runtime.funcWrappers[sig]) { Runtime.funcWrappers[sig] = {}; } var sigCache = Runtime.funcWrappers[sig]; if (!sigCache[func]) { sigCache[func] = function dynCall_wrapper() { return Runtime.dynCall(sig, func, arguments); }; } return sigCache[func]; }, #if RETAIN_COMPILER_SETTINGS compilerSettings: {}, #endif getCompilerSetting: function(name) { #if RETAIN_COMPILER_SETTINGS == 0 throw 'You must build with -s RETAIN_COMPILER_SETTINGS=1 for Runtime.getCompilerSetting or emscripten_get_compiler_setting to work'; #else if (!(name in Runtime.compilerSettings)) return 'invalid compiler setting: ' + name; return Runtime.compilerSettings[name]; #endif }, #if RUNTIME_DEBUG debug: true, // Switch to false at runtime to disable logging at the right times printObjectList: [], prettyPrint: function(arg) { if (typeof arg == 'undefined') return '!UNDEFINED!'; if (typeof arg == 'boolean') arg = arg + 0; if (!arg) return arg; var index = Runtime.printObjectList.indexOf(arg); if (index >= 0) return '<' + arg + '|' + index + '>'; if (arg.toString() == '[object HTMLImageElement]') { return arg + '\n\n'; } if (arg.byteLength) { return '{' + Array.prototype.slice.call(arg, 0, Math.min(arg.length, 400)) + '}'; // Useful for correct arrays, less so for compiled arrays, see the code below for that var buf = new ArrayBuffer(32); var i8buf = new Int8Array(buf); var i16buf = new Int16Array(buf); var f32buf = new Float32Array(buf); switch(arg.toString()) { case '[object Uint8Array]': i8buf.set(arg.subarray(0, 32)); break; case '[object Float32Array]': f32buf.set(arg.subarray(0, 5)); break; case '[object Uint16Array]': i16buf.set(arg.subarray(0, 16)); break; default: alert('unknown array for debugging: ' + arg); throw 'see alert'; } var ret = '{' + arg.byteLength + ':\n'; var arr = Array.prototype.slice.call(i8buf); ret += 'i8:' + arr.toString().replace(/,/g, ',') + '\n'; arr = Array.prototype.slice.call(f32buf, 0, 8); ret += 'f32:' + arr.toString().replace(/,/g, ',') + '}'; return ret; } if (typeof arg == 'object') { Runtime.printObjectList.push(arg); return '<' + arg + '|' + (Runtime.printObjectList.length-1) + '>'; } if (typeof arg == 'number') { if (arg > 0) return '0x' + arg.toString(16) + ' (' + arg + ')'; } return arg; } #endif }; Runtime.stackAlloc = unInline('stackAlloc', ['size']); Runtime.staticAlloc = unInline('staticAlloc', ['size']); Runtime.dynamicAlloc = unInline('dynamicAlloc', ['size']); Runtime.alignMemory = unInline('alignMemory', ['size', 'quantum']); Runtime.makeBigInt = unInline('makeBigInt', ['low', 'high', 'unsigned']); if (MAIN_MODULE || SIDE_MODULE) { Runtime.tempRet0 = 0; Runtime.getTempRet0 = function() { return Runtime.tempRet0; }; Runtime.setTempRet0 = function(x) { Runtime.tempRet0 = x; }; } function getRuntime() { var ret = 'var Runtime = {\n'; for (i in Runtime) { var item = Runtime[i]; ret += ' ' + i + ': '; if (typeof item === 'function') { ret += item.toString(); } else { ret += JSON.stringify(item); } ret += ',\n'; } return ret + ' __dummy__: 0\n}\n'; } // Additional runtime elements, that need preprocessing // Converts a value we have as signed, into an unsigned value. For // example, -1 in int32 would be a very large number as unsigned. function unSign(value, bits, ignore) { if (value >= 0) { return value; } return bits <= 32 ? 2*Math.abs(1 << (bits-1)) + value // Need some trickery, since if bits == 32, we are right at the limit of the bits JS uses in bitshifts : Math.pow(2, bits) + value; } // Converts a value we have as unsigned, into a signed value. For // example, 200 in a uint8 would be a negative number. function reSign(value, bits, ignore) { if (value <= 0) { return value; } var half = bits <= 32 ? Math.abs(1 << (bits-1)) // abs is needed if bits == 32 : Math.pow(2, bits-1); if (value >= half && (bits <= 32 || value > half)) { // for huge values, we can hit the precision limit and always get true here. so don't do that // but, in general there is no perfect solution here. With 64-bit ints, we get rounding and errors // TODO: In i64 mode 1, resign the two parts separately and safely value = -2*half + value; // Cannot bitshift half, as it may be at the limit of the bits JS uses in bitshifts } return value; } // The address globals begin at. Very low in memory, for code size and optimization opportunities. // Above 0 is static memory, starting with globals. // Then the stack. // Then 'dynamic' memory for sbrk. Runtime.GLOBAL_BASE = {{{ GLOBAL_BASE }}} < 0 ? 8 : {{{ GLOBAL_BASE }}}; if (RETAIN_COMPILER_SETTINGS) { var blacklist = set('STRUCT_INFO'); for (var x in this) { try { if (x[0] !== '_' && !(x in blacklist) && x == x.toUpperCase() && (typeof this[x] === 'number' || typeof this[x] === 'string' || this.isArray())) Runtime.compilerSettings[x] = this[x]; } catch(e){} } }