diff options
50 files changed, 2142 insertions, 930 deletions
@@ -471,6 +471,9 @@ Options that are modified or new in %s include: to hide these warnings and acknowledge that the explicit use of absolute paths is intentional. + --proxy-to-worker Generates both html and js files. The main + program is in js, and the html proxies to/from it. + The target file, if specified (-o <target>), defines what will be generated: @@ -740,6 +743,7 @@ try: save_bc = False memory_init_file = False use_preload_cache = False + proxy_to_worker = False if use_cxx: default_cxx_std = '-std=c++03' # Enforce a consistent C++ standard when compiling .cpp files, if user does not specify one on the cmdline. @@ -903,6 +907,9 @@ try: memory_init_file = int(newargs[i+1]) newargs[i] = '' newargs[i+1] = '' + elif newargs[i] == '--proxy-to-worker': + proxy_to_worker = True + newargs[i] = '' elif newargs[i].startswith(('-I', '-L')): path_name = newargs[i][2:] if not absolute_warning_shown and os.path.isabs(path_name): @@ -1125,6 +1132,12 @@ try: else: logging.debug('using response file for EXPORTED_FUNCTIONS, make sure it includes _malloc and _free') + if shared.Settings.ASM_JS and shared.Settings.DLOPEN_SUPPORT: + assert shared.Settings.DISABLE_EXCEPTION_CATCHING, 'no exceptions support with dlopen in asm yet' + + if proxy_to_worker: + shared.Settings.PROXY_TO_WORKER = 1 + ## Compile source code to bitcode logging.debug('compiling to bitcode') @@ -1706,7 +1719,11 @@ try: logging.debug('generating HTML') shell = open(shell_path).read() html = open(target, 'w') - if not Compression.on: + if proxy_to_worker: + html.write(shell.replace('{{{ SCRIPT_CODE }}}', open(shared.path_from_root('src', 'proxyClient.js')).read().replace('{{{ filename }}}', target_basename))) + js_target = unsuffixed(target) + '.js' + shutil.copyfile(final, js_target) + elif not Compression.on: if debug_level >= 4: match = re.match('.*?<script[^>]*>{{{ SCRIPT_CODE }}}</script>', shell, re.DOTALL) @@ -1780,13 +1797,12 @@ try: html.close() else: if split_js_file: - from tools.split import split_javascript_file - split_javascript_file(final, unsuffixed(target), split_js_file) + from tools.split import split_javascript_file + split_javascript_file(final, unsuffixed(target), split_js_file) else: - if debug_level >= 4: generate_source_map(target) - - # copy final JS to output - shutil.move(final, target) + if debug_level >= 4: generate_source_map(target) + # copy final JS to output + shutil.move(final, target) if DEBUG: logging.debug('total time: %.2f seconds' % (time.time() - start_time)) diff --git a/emlibtool b/emlibtool deleted file mode 100755 index 1eb18edc..00000000 --- a/emlibtool +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env python2 - -''' -This is a helper script. See emcc. -''' - -import os, sys -from tools import shared - -raise Exception('TODO: emlibtool') - diff --git a/emlibtool.bat b/emlibtool.bat deleted file mode 100644 index 4ea705be..00000000 --- a/emlibtool.bat +++ /dev/null @@ -1,2 +0,0 @@ -@echo off -python "%~dp0\emlibtool" %*
\ No newline at end of file diff --git a/emscripten.py b/emscripten.py index 257527fe..fcf109bf 100755 --- a/emscripten.py +++ b/emscripten.py @@ -287,6 +287,7 @@ def emscript(infile, settings, outfile, libraries=[], compiler_engine=None, exported_implemented_functions = set() for func_js, curr_forwarded_data in outputs: curr_forwarded_json = json.loads(curr_forwarded_data) + forwarded_json['Types']['hasInlineJS'] = forwarded_json['Types']['hasInlineJS'] or curr_forwarded_json['Types']['hasInlineJS'] forwarded_json['Types']['preciseI64MathUsed'] = forwarded_json['Types']['preciseI64MathUsed'] or curr_forwarded_json['Types']['preciseI64MathUsed'] for key, value in curr_forwarded_json['Functions']['blockAddresses'].iteritems(): forwarded_json['Functions']['blockAddresses'][key] = value @@ -543,7 +544,7 @@ function asmPrintFloat(x, y) { } // EMSCRIPTEN_START_ASM var asm = (function(global, env, buffer) { - 'use asm'; + %s var HEAP8 = new global.Int8Array(buffer); var HEAP16 = new global.Int16Array(buffer); var HEAP32 = new global.Int32Array(buffer); @@ -552,7 +553,7 @@ var asm = (function(global, env, buffer) { var HEAPU32 = new global.Uint32Array(buffer); var HEAPF32 = new global.Float32Array(buffer); var HEAPF64 = new global.Float64Array(buffer); -''' % (asm_setup,) + '\n' + asm_global_vars + ''' +''' % (asm_setup, "'use asm';" if not forwarded_json['Types']['hasInlineJS'] and not settings['SIDE_MODULE'] else "'almost asm';") + '\n' + asm_global_vars + ''' var __THREW__ = 0; var threwValue = 0; var setjmpId = 0; diff --git a/src/intertyper.js b/src/intertyper.js index 31e97bd0..f9633549 100644 --- a/src/intertyper.js +++ b/src/intertyper.js @@ -707,16 +707,25 @@ function intertyper(data, sidePass, baseLineNums) { var tokensLeft = item.tokens.slice(2); item.ident = eatLLVMIdent(tokensLeft); if (item.ident == 'asm') { + if (ASM_JS) { + Types.hasInlineJS = true; + warnOnce('inline JavaScript (asm, EM_ASM) will cause the code to no longer fall in the asm.js subset of JavaScript, which can reduce performance'); + } + assert(TARGET_LE32, 'inline js is only supported in le32'); // Inline assembly is just JavaScript that we paste into the code item.intertype = 'value'; if (tokensLeft[0].text == 'sideeffect') tokensLeft.splice(0, 1); item.ident = tokensLeft[0].text.substr(1, tokensLeft[0].text.length-2) || ';'; // use ; for empty inline assembly var i = 0; + var params = [], args = []; splitTokenList(tokensLeft[3].item.tokens).map(function(element) { var ident = toNiceIdent(element[1].text); var type = element[0].text; - item.ident = item.ident.replace(new RegExp('\\$' + i++, 'g'), ident); + params.push('$' + (i++)); + args.push(ident); }); + if (item.assignTo) item.ident = 'return ' + item.ident; + item.ident = '(function(' + params + ') { ' + item.ident + ' })(' + args + ');'; return { forward: null, ret: [item], item: item }; } if (item.ident.substr(-2) == '()') { diff --git a/src/jsifier.js b/src/jsifier.js index 8592364d..a3b26aa9 100644 --- a/src/jsifier.js +++ b/src/jsifier.js @@ -324,11 +324,13 @@ function JSify(data, functionsOnly, givenFunctions) { assert(typeof constant === 'object');//, [typeof constant, JSON.stringify(constant), item.external]); // This is a flattened object. We need to find its idents, so they can be assigned to later + var structTypes = null; constant.forEach(function(value, i) { if (needsPostSet(value)) { // ident, or expression containing an ident + if (!structTypes) structTypes = generateStructTypes(item.type); ret.push({ intertype: 'GlobalVariablePostSet', - JS: makeSetValue(makeGlobalUse(item.ident), i, value, 'i32', false, true) + ';' // ignore=true, since e.g. rtti and statics cause lots of safe_heap errors + JS: makeSetValue(makeGlobalUse(item.ident), i, value, structTypes[i], false, true) + ';' // ignore=true, since e.g. rtti and statics cause lots of safe_heap errors }); constant[i] = '0'; } @@ -338,6 +340,7 @@ function JSify(data, functionsOnly, givenFunctions) { // External variables in shared libraries should not be declared as // they would shadow similarly-named globals in the parent, so do nothing here. if (BUILD_AS_SHARED_LIB) return ret; + if (SIDE_MODULE) return []; // Library items need us to emit something, but everything else requires nothing. if (!LibraryManager.library[item.ident.slice(1)]) return ret; } @@ -1142,8 +1145,8 @@ function JSify(data, functionsOnly, givenFunctions) { }); var range = maxx - minn; var useIfs = (item.switchLabels.length+1) < 6 || range > 10*1024 || (range/item.switchLabels.length) > 1024; // heuristics - if (VERBOSE && useIfs && item.switchLabels.length > 2) { - warn('not optimizing llvm switch into js switch because ' + [range, range/item.switchLabels.length]); + if (VERBOSE && useIfs && item.switchLabels.length >= 6) { + warn('not optimizing llvm switch into js switch because range of values is ' + range + ', density is ' + range/item.switchLabels.length); } var phiSets = calcPhiSets(item); @@ -1408,7 +1411,7 @@ function JSify(data, functionsOnly, givenFunctions) { var extCall = false; if (ASM_JS && funcData.setjmpTable) forceByPointer = true; // in asm.js mode, we must do an invoke for each call - if (ASM_JS && DLOPEN_SUPPORT && !invoke) extCall = true; // go out, to be able to access other modules TODO: optimize + if (ASM_JS && DLOPEN_SUPPORT && !invoke && !funcData.setjmpTable) extCall = true; // go out, to be able to access other modules TODO: optimize ident = Variables.resolveAliasToIdent(ident); var shortident = ident.slice(1); @@ -1840,6 +1843,9 @@ function JSify(data, functionsOnly, givenFunctions) { 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(read('proxyWorker.js')); + } if (RUNTIME_TYPE_INFO) { Types.cleanForRuntime(); print('Runtime.typeInfo = ' + JSON.stringify(Types.types)); diff --git a/src/library.js b/src/library.js index 7894e033..f3c3c1ec 100644 --- a/src/library.js +++ b/src/library.js @@ -323,10 +323,16 @@ LibraryManager.library = { path = Pointer_stringify(path); // we don't want this in the JS API as the JS API // uses mknod to create all nodes. - var err = FS.mayMknod(mode); - if (err) { - ___setErrNo(err); - return -1; + switch (mode & {{{ cDefine('S_IFMT') }}}) { + case {{{ cDefine('S_IFREG') }}}: + case {{{ cDefine('S_IFCHR') }}}: + case {{{ cDefine('S_IFBLK') }}}: + case {{{ cDefine('S_IFIFO') }}}: + case {{{ cDefine('S_IFSOCK') }}}: + break; + default: + ___setErrNo(ERRNO_CODES.EINVAL); + return -1; } try { FS.mknod(path, mode, dev); @@ -4273,9 +4279,11 @@ LibraryManager.library = { __cxa_guard_release: function() {}, __cxa_guard_abort: function() {}, +#if USE_TYPED_ARRAYS != 2 _ZTVN10__cxxabiv119__pointer_type_infoE: [0], // is a pointer _ZTVN10__cxxabiv117__class_type_infoE: [1], // no inherited classes _ZTVN10__cxxabiv120__si_class_type_infoE: [2], // yes inherited classes +#endif // Exceptions __cxa_allocate_exception: function(size) { @@ -4657,20 +4665,28 @@ LibraryManager.library = { cos: 'Math.cos', cosf: 'Math.cos', + cosl: 'Math.cos', sin: 'Math.sin', sinf: 'Math.sin', + sinl: 'Math.sin', tan: 'Math.tan', tanf: 'Math.tan', + tanl: 'Math.tan', acos: 'Math.acos', acosf: 'Math.acos', + acosl: 'Math.acos', asin: 'Math.asin', asinf: 'Math.asin', + asinl: 'Math.asin', atan: 'Math.atan', atanf: 'Math.atan', + atanl: 'Math.atan', atan2: 'Math.atan2', atan2f: 'Math.atan2', + atan2l: 'Math.atan2', exp: 'Math.exp', expf: 'Math.exp', + expl: 'Math.exp', // The erf and erfc functions are inspired from // http://www.digitalmars.com/archives/cplusplus/3634.html @@ -4706,7 +4722,8 @@ LibraryManager.library = { } while (Math.abs(q1 - q2) / q2 > MATH_TOLERANCE); return (ONE_SQRTPI * Math.exp(- x * x) * q2); }, - erfcf: 'erfcf', + erfcf: 'erfc', + erfcl: 'erfc', erf__deps: ['erfc'], erf: function(x) { var MATH_TOLERANCE = 1E-12; @@ -4730,6 +4747,7 @@ LibraryManager.library = { return (TWO_SQRTPI * sum); }, erff: 'erf', + erfl: 'erf', log: 'Math.log', logf: 'Math.log', logl: 'Math.log', @@ -4818,6 +4836,7 @@ LibraryManager.library = { return __reallyNegative(a) === __reallyNegative(b) ? a : -a; }, copysignf: 'copysign', + copysignl: 'copysign', __signbit__deps: ['copysign'], __signbit: function(x) { // We implement using copysign so that we get support @@ -4830,56 +4849,70 @@ LibraryManager.library = { return Math.sqrt(a*a + b*b); }, hypotf: 'hypot', + hypotl: 'hypot', sinh: function(x) { var p = Math.pow(Math.E, x); return (p - (1 / p)) / 2; }, sinhf: 'sinh', + sinhl: 'sinh', cosh: function(x) { var p = Math.pow(Math.E, x); return (p + (1 / p)) / 2; }, coshf: 'cosh', + coshl: 'cosh', tanh__deps: ['sinh', 'cosh'], tanh: function(x) { return _sinh(x) / _cosh(x); }, tanhf: 'tanh', + tanhl: 'tanh', asinh: function(x) { return Math.log(x + Math.sqrt(x * x + 1)); }, asinhf: 'asinh', + asinhl: 'asinh', acosh: function(x) { return Math.log(x * 1 + Math.sqrt(x * x - 1)); }, acoshf: 'acosh', + acoshl: 'acosh', atanh: function(x) { return Math.log((1 + x) / (1 - x)) / 2; }, atanhf: 'atanh', + atanhl: 'atanh', exp2: function(x) { return Math.pow(2, x); }, exp2f: 'exp2', + exp2l: 'exp2', expm1: function(x) { return Math.exp(x) - 1; }, expm1f: 'expm1', + expm1l: 'expm1', round: function(x) { return (x < 0) ? -Math.round(-x) : Math.round(x); }, roundf: 'round', + roundl: 'round', lround: 'round', lroundf: 'round', + lroundl: 'round', llround: 'round', llroundf: 'round', + llroundl: 'round', rint: function(x) { if (Math.abs(x % 1) !== 0.5) return Math.round(x); return x + x % 2 + ((x < 0) ? 1 : -1); }, rintf: 'rint', + rintl: 'rint', lrint: 'rint', lrintf: 'rint', + lrintl: 'rint', #if USE_TYPED_ARRAYS == 2 llrint: function(x) { x = (x < 0) ? -Math.round(-x) : Math.round(x); @@ -4889,50 +4922,63 @@ LibraryManager.library = { llrint: 'rint', #endif llrintf: 'llrint', + llrintl: 'llrint', nearbyint: 'rint', nearbyintf: 'rint', + nearbyintl: 'rint', trunc: function(x) { return (x < 0) ? Math.ceil(x) : Math.floor(x); }, truncf: 'trunc', + truncl: 'trunc', fdim: function(x, y) { return (x > y) ? x - y : 0; }, fdimf: 'fdim', + fdiml: 'fdim', fmax: function(x, y) { return isNaN(x) ? y : isNaN(y) ? x : Math.max(x, y); }, fmaxf: 'fmax', + fmaxl: 'fmax', fmin: function(x, y) { return isNaN(x) ? y : isNaN(y) ? x : Math.min(x, y); }, fminf: 'fmin', + fminl: 'fmin', fma: function(x, y, z) { return x * y + z; }, fmaf: 'fma', + fmal: 'fma', fmod: function(x, y) { return x % y; }, fmodf: 'fmod', + fmodl: 'fmod', remainder: 'fmod', remainderf: 'fmod', + remainderl: 'fmod', log10: function(x) { return Math.log(x) / Math.LN10; }, log10f: 'log10', + log10l: 'log10', log1p: function(x) { return Math.log(1 + x); }, log1pf: 'log1p', + log1pl: 'log1p', log2: function(x) { return Math.log(x) / Math.LN2; }, log2f: 'log2', + log2l: 'log2', nan: function(x) { return NaN; }, nanf: 'nan', + nanl: 'nan', sincos: function(x, sine, cosine) { var sineVal = Math.sin(x), @@ -4940,6 +4986,7 @@ LibraryManager.library = { {{{ makeSetValue('sine', '0', 'sineVal', 'double') }}}; {{{ makeSetValue('cosine', '0', 'cosineVal', 'double') }}}; }, + sincosl: 'sincos', sincosf: function(x, sine, cosine) { var sineVal = Math.sin(x), @@ -5296,8 +5343,8 @@ LibraryManager.library = { ['i32', 'tm_zone']]), // Statically allocated time struct. __tm_current: 'allocate({{{ Runtime.QUANTUM_SIZE }}}*26, "i8", ALLOC_STATIC)', - // Statically allocated timezone strings. - __tm_timezones: {}, + // Statically allocated timezone string. We only use GMT as a timezone. + __tm_timezone: 'allocate(intArrayFromString("GMT"), "i8", ALLOC_STATIC)', // Statically allocated time strings. __tm_formatted: 'allocate({{{ Runtime.QUANTUM_SIZE }}}*26, "i8", ALLOC_STATIC)', @@ -5325,7 +5372,7 @@ LibraryManager.library = { return _gmtime_r(time, ___tm_current); }, - gmtime_r__deps: ['__tm_struct_layout', '__tm_timezones'], + gmtime_r__deps: ['__tm_struct_layout', '__tm_timezone'], gmtime_r: function(time, tmPtr) { var date = new Date({{{ makeGetValue('time', 0, 'i32') }}}*1000); var offsets = ___tm_struct_layout; @@ -5347,12 +5394,7 @@ LibraryManager.library = { start.setUTCMilliseconds(0); var yday = Math.floor((date.getTime() - start.getTime()) / (1000 * 60 * 60 * 24)); {{{ makeSetValue('tmPtr', 'offsets.tm_yday', 'yday', 'i32') }}} - - var timezone = "GMT"; - if (!(timezone in ___tm_timezones)) { - ___tm_timezones[timezone] = allocate(intArrayFromString(timezone), 'i8', ALLOC_NORMAL); - } - {{{ makeSetValue('tmPtr', 'offsets.tm_zone', '___tm_timezones[timezone]', 'i32') }}} + {{{ makeSetValue('tmPtr', 'offsets.tm_zone', '___tm_timezone', 'i32') }}} return tmPtr; }, @@ -5371,7 +5413,7 @@ LibraryManager.library = { return _localtime_r(time, ___tm_current); }, - localtime_r__deps: ['__tm_struct_layout', '__tm_timezones', 'tzset'], + localtime_r__deps: ['__tm_struct_layout', '__tm_timezone', 'tzset'], localtime_r: function(time, tmPtr) { _tzset(); var offsets = ___tm_struct_layout; @@ -5392,11 +5434,7 @@ LibraryManager.library = { var dst = Number(start.getTimezoneOffset() != date.getTimezoneOffset()); {{{ makeSetValue('tmPtr', 'offsets.tm_isdst', 'dst', 'i32') }}} - var timezone = 'GMT'; // XXX do not rely on browser timezone info, it is very unpredictable | date.toString().match(/\(([A-Z]+)\)/)[1]; - if (!(timezone in ___tm_timezones)) { - ___tm_timezones[timezone] = allocate(intArrayFromString(timezone), 'i8', ALLOC_NORMAL); - } - {{{ makeSetValue('tmPtr', 'offsets.tm_zone', '___tm_timezones[timezone]', 'i32') }}} + {{{ makeSetValue('tmPtr', 'offsets.tm_zone', '___tm_timezone', 'i32') }}} return tmPtr; }, @@ -6078,7 +6116,7 @@ LibraryManager.library = { // int clock_gettime(clockid_t clk_id, struct timespec *tp); var now = Date.now(); {{{ makeSetValue('tp', '___timespec_struct_layout.tv_sec', 'Math.floor(now/1000)', 'i32') }}}; // seconds - {{{ makeSetValue('tp', '___timespec_struct_layout.tv_nsec', '0', 'i32') }}}; // nanoseconds - not supported + {{{ makeSetValue('tp', '___timespec_struct_layout.tv_nsec', '(now % 1000) * 1000 * 1000', 'i32') }}}; // nanoseconds (really milliseconds) return 0; }, clock_settime: function(clk_id, tp) { @@ -6090,7 +6128,7 @@ LibraryManager.library = { clock_getres: function(clk_id, res) { // int clock_getres(clockid_t clk_id, struct timespec *res); {{{ makeSetValue('res', '___timespec_struct_layout.tv_sec', '1', 'i32') }}} - {{{ makeSetValue('res', '___timespec_struct_layout.tv_nsec', '0', 'i32') }}} + {{{ makeSetValue('res', '___timespec_struct_layout.tv_nsec', '1000 * 1000', 'i32') }}} // resolution is milliseconds return 0; }, @@ -7399,7 +7437,7 @@ LibraryManager.library = { var aliasesBuf = _malloc(4); {{{ makeSetValue('aliasesBuf', '0', '0', 'i8*') }}} {{{ makeSetValue('ret', '___hostent_struct_layout.h_aliases', 'aliasesBuf', 'i8**') }}} - var afinet = {{{ cDefine("AF_INET") }}}; + var afinet = {{{ cDefine('AF_INET') }}}; {{{ makeSetValue('ret', '___hostent_struct_layout.h_addrtype', 'afinet', 'i32') }}} {{{ makeSetValue('ret', '___hostent_struct_layout.h_length', '4', 'i32') }}} var addrListBuf = _malloc(12); diff --git a/src/library_fs.js b/src/library_fs.js index 5573dc27..4a150d80 100644 --- a/src/library_fs.js +++ b/src/library_fs.js @@ -14,11 +14,10 @@ mergeInto(LibraryManager.library, { 'Module["FS_createDevice"] = FS.createDevice;', $FS: { root: null, - nodes: [null], devices: [null], streams: [null], nextInode: 1, - name_table: null, + nameTable: null, currentPath: '/', initialized: false, // Whether we are currently ignoring permissions. Useful when preparing the @@ -49,6 +48,76 @@ mergeInto(LibraryManager.library, { }, // + // paths + // + cwd: function() { + return FS.currentPath; + }, + lookupPath: function(path, opts) { + path = PATH.resolve(FS.currentPath, path); + opts = opts || { recurse_count: 0 }; + + if (opts.recurse_count > 8) { // max recursive lookup of 8 + throw new FS.ErrnoError(ERRNO_CODES.ELOOP); + } + + // split the path + var parts = PATH.normalizeArray(path.split('/').filter(function(p) { + return !!p; + }), false); + + // start at the root + var current = FS.root; + var current_path = '/'; + + for (var i = 0; i < parts.length; i++) { + var islast = (i === parts.length-1); + if (islast && opts.parent) { + // stop resolving + break; + } + + current = FS.lookupNode(current, parts[i]); + current_path = PATH.join(current_path, parts[i]); + + // jump to the mount's root node if this is a mountpoint + if (FS.isMountpoint(current)) { + current = current.mount.root; + } + + // follow symlinks + // by default, lookupPath will not follow a symlink if it is the final path component. + // setting opts.follow = true will override this behavior. + if (!islast || opts.follow) { + var count = 0; + while (FS.isLink(current.mode)) { + var link = FS.readlink(current_path); + current_path = PATH.resolve(PATH.dirname(current_path), link); + + var lookup = FS.lookupPath(current_path, { recurse_count: opts.recurse_count }); + current = lookup.node; + + if (count++ > 40) { // limit max consecutive symlinks to 40 (SYMLOOP_MAX). + throw new FS.ErrnoError(ERRNO_CODES.ELOOP); + } + } + } + } + + return { path: current_path, node: current }; + }, + getPath: function(node) { + var path; + while (true) { + if (FS.isRoot(n |