diff options
62 files changed, 10856 insertions, 1744 deletions
@@ -24,7 +24,7 @@ a license to everyone to use it as detailed in LICENSE.) * Pierre Renaux <pierre@talansoft.com> * Brian Anderson <banderson@mozilla.com> * Jon Bardin <diclophis@gmail.com> -* Jukka Jylänki <jujjyl@gmail.com> +* Jukka Jylänki <jujjyl@gmail.com> * Aleksander Guryanov <caiiiycuk@gmail.com> * Chad Austin <chad@chadaustin.me> (copyright owned by IMVU) * nandhp <nandhp@gmail.com> @@ -46,7 +46,7 @@ a license to everyone to use it as detailed in LICENSE.) * Anthony Liot <wolfviking0@yahoo.com> * Michael Riss <Michael.Riss@gmx.de> * Jasper St. Pierre <jstpierre@mecheye.net> -* Manuel Schölling <manuel.schoelling@gmx.de> +* Manuel Schölling <manuel.schoelling@gmx.de> * Bruce Mitchener, Jr. <bruce.mitchener@gmail.com> * Michael Bishop <mbtyke@gmail.com> * Roger Braun <roger@rogerbraun.net> @@ -57,9 +57,14 @@ a license to everyone to use it as detailed in LICENSE.) * Ting-Yuan Huang <thuang@mozilla.com> * Joshua Granick <jgranick@blackberry.com> * Felix H. Dahlke <fhd@ubercode.de> -* Éloi Rivard <azmeuk@gmail.com> +* Éloi Rivard <azmeuk@gmail.com> * Alexander Gladysh <ag@logiceditor.com> * Arlo Breault <arlolra@gmail.com> * Jacob Lee <artdent@gmail.com> (copyright owned by Google, Inc.) * Joe Lee <jlee@imvu.com> (copyright owned by IMVU) - +* Andy Friesen <andy@imvu.com> (copyright owned by IMVU) +* Bill Welden <bwelden@imvu.com> (copyright owned by IMVU) +* Michael Ey <mey@imvu.com> (copyright owned by IMVU) +* Llorens Marti Garcia <lgarcia@imvu.com> (copyright owned by IMVU) +* Jinsuck Kim <jkim@imvu.com> (copyright owned by IMVU) +* Todd Lee <tlee@imvu.com> (copyright owned by IMVU) @@ -79,6 +79,7 @@ import os, sys, shutil, tempfile, subprocess, shlex, time, re from subprocess import PIPE, STDOUT from tools import shared from tools.shared import Compression, execute, suffix, unsuffixed, unsuffixed_basename +from tools.response_file import read_response_file # Mapping of emcc opt levels to llvm opt levels. We use llvm opt level 3 in emcc opt # levels 2 and 3 (emcc 3 is unsafe opts, so unsuitable for the only level to get @@ -129,19 +130,10 @@ while response_file: for index in range(1, len(sys.argv)): if sys.argv[index][0] == '@': # found one, loop again next time - response_file = sys.argv[index][1:] - print >>sys.stderr, 'emcc: using response file: %s' % response_file - if not os.path.exists(response_file): - print >>sys.stderr, 'emcc: error: Response file not found: %s' % response_file - exit(1) - - response_fd = open(response_file, 'r') - extra_args = shlex.split(response_fd.read()) - response_fd.close() - + response_file = True + extra_args = read_response_file(sys.argv[index]) # slice in extra_args in place of the response file arg sys.argv[index:index+1] = extra_args - #if DEBUG: print >>sys.stderr, "Expanded response file: " + " | ".join(sys.argv) break if sys.argv[1] == '--version': @@ -171,7 +163,7 @@ Most normal gcc/g++ options will work, for example: Options that are modified or new in %s include: -O0 No optimizations (default) - -O1 Simple optimizations, including LLVM -O1 + -O1 Simple optimizations, including asm.js, LLVM -O1 optimizations, and no runtime assertions or C++ exception catching (to re-enable C++ exception catching, use @@ -473,7 +465,7 @@ Options that are modified or new in %s include: --memory-init-file <on> If on, we generate a separate memory initialization file. This is more efficient than storing the memory initialization data embedded inside - JavaScript as text. (default is on) + JavaScript as text. (default is off) The target file, if specified (-o <target>), defines what will be generated: @@ -483,8 +475,8 @@ be generated: <name>.bc LLVM bitcode (default) <name>.o LLVM bitcode (same as .bc) -Note that if --memory-init-file is used, then in addition to a -.js or .html file that is generated, a .mem file will also appear. +(Note that if --memory-init-file is used, then in addition to a +.js or .html file that is generated, a .mem file will also appear.) The -c option (which tells gcc not to run the linker) will cause LLVM bitcode to be generated, as %s only generates @@ -727,7 +719,7 @@ try: bind = False jcache = False save_bc = False - memory_init_file = True + memory_init_file = 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. @@ -1010,8 +1002,11 @@ try: # Apply effects from settings if shared.Settings.ASM_JS: - assert opt_level == 2, 'asm.js requires -O2' + assert opt_level >= 1, 'asm.js requires -O1 or above' + if bind: + shared.Settings.ASM_JS = 0 + print >> sys.stderr, 'emcc: warning: disabling asm.js because it is not compatible with embind yet' if closure: print >> sys.stderr, 'emcc: warning: disabling closure because it is not compatible with asm.js code generation' closure = False diff --git a/emscripten.py b/emscripten.py index 996e5cc3..6d384a96 100755 --- a/emscripten.py +++ b/emscripten.py @@ -12,6 +12,7 @@ headers, for the libc implementation in JS). import os, sys, json, optparse, subprocess, re, time, multiprocessing, functools from tools import jsrun, cache as cache_module, tempfiles +from tools.response_file import read_response_file __rootpath__ = os.path.abspath(os.path.dirname(__file__)) def path_from_root(*pathelems): @@ -347,6 +348,14 @@ def emscript(infile, settings, outfile, libraries=[], compiler_engine=None, if settings.get('ASM_JS'): post_funcs, post_rest = post.split('// EMSCRIPTEN_END_FUNCS\n') post = post_rest + + # Move preAsms to their right place + def move_preasm(m): + contents = m.groups(0)[0] + outfile.write(contents + '\n') + return '' + post_funcs = re.sub(r'/\* PRE_ASM \*/(.*)\n', lambda m: move_preasm(m), post_funcs) + funcs_js += ['\n' + post_funcs + '// EMSCRIPTEN_END_FUNCS\n'] simple = os.environ.get('EMCC_SIMPLE_ASM') @@ -384,15 +393,12 @@ def emscript(infile, settings, outfile, libraries=[], compiler_engine=None, if settings['CHECK_HEAP_ALIGN']: basic_funcs += ['CHECK_ALIGN_2', 'CHECK_ALIGN_4', 'CHECK_ALIGN_8'] basic_vars = ['STACKTOP', 'STACK_MAX', 'tempDoublePtr', 'ABORT'] basic_float_vars = ['NaN', 'Infinity'] - if forwarded_json['Types']['preciseI64MathUsed']: - basic_funcs += ['i64Math_' + op for op in ['add', 'subtract', 'multiply', 'divide', 'modulo']] - asm_setup += ''' -var i64Math_add = function(a, b, c, d) { i64Math.add(a, b, c, d) }; -var i64Math_subtract = function(a, b, c, d) { i64Math.subtract(a, b, c, d) }; -var i64Math_multiply = function(a, b, c, d) { i64Math.multiply(a, b, c, d) }; -var i64Math_divide = function(a, b, c, d, e) { i64Math.divide(a, b, c, d, e) }; -var i64Math_modulo = function(a, b, c, d, e) { i64Math.modulo(a, b, c, d, e) }; -''' + + if forwarded_json['Types']['preciseI64MathUsed'] or \ + forwarded_json['Functions']['libraryFunctions'].get('llvm_cttz_i32') or \ + forwarded_json['Functions']['libraryFunctions'].get('llvm_ctlz_i32'): + basic_vars += ['cttz_i8', 'ctlz_i8'] + asm_runtime_funcs = ['stackAlloc', 'stackSave', 'stackRestore', 'setThrew'] + ['setTempRet%d' % i for i in range(10)] # function tables def asm_coerce(value, sig): @@ -420,7 +426,7 @@ function invoke_%s(%s) { try { %sModule.dynCall_%s(%s); } catch(e) { - asm.setThrew(1); + asm.setThrew(1, 0); } } ''' % (sig, args, 'return ' if sig[0] != 'v' else '', sig, args) @@ -442,7 +448,7 @@ function invoke_%s(%s) { pass # If no named globals, only need externals global_vars = map(lambda g: g['name'], filter(lambda g: settings['NAMED_GLOBALS'] or g.get('external') or g.get('unIndexable'), forwarded_json['Variables']['globals'].values())) - global_funcs = ['_' + x for x in forwarded_json['Functions']['libraryFunctions'].keys()] + global_funcs = ['_' + key for key, value in forwarded_json['Functions']['libraryFunctions'].iteritems() if value != 2] def math_fix(g): return g if not g.startswith('Math_') else g.split('_')[1]; asm_global_funcs = ''.join([' var ' + g.replace('.', '_') + '=global.' + g + ';\n' for g in maths]) + \ @@ -483,6 +489,8 @@ var asm = (function(global, env, buffer) { var HEAPF64 = new global.Float64Array(buffer); ''' % (asm_setup,) + '\n' + asm_global_vars + ''' var __THREW__ = 0; + var threwValue = 0; + var setjmpId = 0; var undef = 0; var tempInt = 0, tempBigInt = 0, tempBigIntP = 0, tempBigIntS = 0, tempBigIntR = 0.0, tempBigIntI = 0, tempBigIntD = 0, tempValue = 0, tempDouble = 0.0; ''' + ''.join([''' @@ -503,9 +511,13 @@ var asm = (function(global, env, buffer) { top = top|0; STACKTOP = top; } - function setThrew(threw) { + function setThrew(threw, value) { threw = threw|0; - __THREW__ = threw; + value = value|0; + if ((__THREW__|0) == 0) { + __THREW__ = threw; + threwValue = value; + } } ''' + ''.join([''' function setTempRet%d(value) { @@ -643,6 +655,18 @@ def main(args, compiler_engine, cache, jcache, relooper, temp_files, DEBUG, DEBU jcache=jcache, temp_files=temp_files, DEBUG=DEBUG, DEBUG_CACHE=DEBUG_CACHE) def _main(environ): + response_file = True + while response_file: + response_file = None + for index in range(1, len(sys.argv)): + if sys.argv[index][0] == '@': + # found one, loop again next time + response_file = True + response_file_args = read_response_file(sys.argv[index]) + # slice in extra_args in place of the response file arg + sys.argv[index:index+1] = response_file_args + break + parser = optparse.OptionParser( usage='usage: %prog [-h] [-H HEADERS] [-o OUTFILE] [-c COMPILER_ENGINE] [-s FOO=BAR]* infile', description=('You should normally never use this! Use emcc instead. ' diff --git a/src/analyzer.js b/src/analyzer.js index 3921cab8..3278139b 100644 --- a/src/analyzer.js +++ b/src/analyzer.js @@ -324,12 +324,13 @@ function analyzer(data, sidePass) { } // call, return: Return the first 32 bits, the rest are in temp case 'call': { - bits = getBits(value.type); - var elements = getLegalVars(item.assignTo, bits); var toAdd = [value]; // legalize parameters legalizeFunctionParameters(value.params); + // legalize return value, if any if (value.assignTo && isIllegalType(item.type)) { + bits = getBits(value.type); + var elements = getLegalVars(item.assignTo, bits); // legalize return value value.assignTo = elements[0].ident; for (var j = 1; j < elements.length; j++) { @@ -1388,21 +1389,21 @@ function analyzer(data, sidePass) { var line = label.lines[j]; if ((line.intertype == 'call' || line.intertype == 'invoke') && line.ident == setjmp) { // Add a new label - var oldIdent = label.ident; - var newIdent = func.labelIdCounter++; + var oldLabel = label.ident; + var newLabel = func.labelIdCounter++; if (!func.setjmpTable) func.setjmpTable = []; - func.setjmpTable.push([oldIdent, newIdent, line.assignTo]); + func.setjmpTable.push({ oldLabel: oldLabel, newLabel: newLabel, assignTo: line.assignTo }); func.labels.splice(i+1, 0, { intertype: 'label', - ident: newIdent, + ident: newLabel, lineNum: label.lineNum + 0.5, lines: label.lines.slice(j+1) }); - func.labelsDict[newIdent] = func.labels[i+1]; + func.labelsDict[newLabel] = func.labels[i+1]; label.lines = label.lines.slice(0, j+1); label.lines.push({ intertype: 'branch', - label: toNiceIdent(newIdent), + label: toNiceIdent(newLabel), lineNum: line.lineNum + 0.01, // XXX legalizing might confuse this }); // Correct phis @@ -1411,8 +1412,8 @@ function analyzer(data, sidePass) { if (phi.intertype == 'phi') { for (var i = 0; i < phi.params.length; i++) { var sourceLabelId = getActualLabelId(phi.params[i].label); - if (sourceLabelId == oldIdent) { - phi.params[i].label = newIdent; + if (sourceLabelId == oldLabel) { + phi.params[i].label = newLabel; } } } diff --git a/src/embind/embind.js b/src/embind/embind.js index d40d6ca2..988526b4 100644..100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -1,14 +1,141 @@ /*global Module*/ /*global _malloc, _free, _memcpy*/ -/*global FUNCTION_TABLE, HEAP32*/ -/*global Pointer_stringify, writeStringToMemory*/ +/*global FUNCTION_TABLE, HEAP8, HEAPU8, HEAP16, HEAPU16, HEAP32, HEAPU32*/ +/*global readLatin1String*/ /*global __emval_register, _emval_handle_array, __emval_decref*/ +/*global ___getTypeName*/ +/*jslint sub:true*/ /* The symbols 'fromWireType' and 'toWireType' must be accessed via array notation to be closure-safe since craftInvokerFunction crafts functions as strings that can't be closured. */ +var InternalError = Module.InternalError = extendError(Error, 'InternalError'); +var BindingError = Module.BindingError = extendError(Error, 'BindingError'); +var UnboundTypeError = Module.UnboundTypeError = extendError(BindingError, 'UnboundTypeError'); +function throwInternalError(message) { + throw new InternalError(message); +} + +function throwBindingError(message) { + throw new BindingError(message); +} + +function throwUnboundTypeError(message, types) { + var unboundTypes = []; + var seen = {}; + function visit(type) { + if (seen[type]) { + return; + } + if (registeredTypes[type]) { + return; + } + if (typeDependencies[type]) { + typeDependencies[type].forEach(visit); + return; + } + unboundTypes.push(type); + seen[type] = true; + } + types.forEach(visit); + + throw new UnboundTypeError(message + ': ' + unboundTypes.map(getTypeName).join([', '])); +} + +// Creates a function overload resolution table to the given method 'methodName' in the given prototype, +// if the overload table doesn't yet exist. +function ensureOverloadTable(proto, methodName, humanName) { + if (undefined === proto[methodName].overloadTable) { + var prevFunc = proto[methodName]; + // Inject an overload resolver function that routes to the appropriate overload based on the number of arguments. + proto[methodName] = function() { + // TODO This check can be removed in -O3 level "unsafe" optimizations. + if (!proto[methodName].overloadTable.hasOwnProperty(arguments.length)) { + throwBindingError("Function '" + humanName + "' called with an invalid number of arguments (" + arguments.length + ") - expects one of (" + proto[methodName].overloadTable + ")!"); + } + return proto[methodName].overloadTable[arguments.length].apply(this, arguments); + }; + // Move the previous function into the overload table. + proto[methodName].overloadTable = []; + proto[methodName].overloadTable[prevFunc.argCount] = prevFunc; + } +} + +/* Registers a symbol (function, class, enum, ...) as part of the Module JS object so that + hand-written code is able to access that symbol via 'Module.name'. + name: The name of the symbol that's being exposed. + value: The object itself to expose (function, class, ...) + numArguments: For functions, specifies the number of arguments the function takes in. For other types, unused and undefined. + + To implement support for multiple overloads of a function, an 'overload selector' function is used. That selector function chooses + the appropriate overload to call from an function overload table. This selector function is only used if multiple overloads are + actually registered, since it carries a slight performance penalty. */ +function exposePublicSymbol(name, value, numArguments) { + if (Module.hasOwnProperty(name)) { + if (undefined === numArguments || (undefined !== Module[name].overloadTable && undefined !== Module[name].overloadTable[numArguments])) { + throwBindingError("Cannot register public name '" + name + "' twice"); + } + + // We are exposing a function with the same name as an existing function. Create an overload table and a function selector + // that routes between the two. + ensureOverloadTable(Module, name, name); + if (Module.hasOwnProperty(numArguments)) { + throwBindingError("Cannot register multiple overloads of a function with the same number of arguments (" + numArguments + ")!"); + } + // Add the new function into the overload table. + Module[name].overloadTable[numArguments] = value; + } + else { + Module[name] = value; + if (undefined !== numArguments) { + Module[name].numArguments = numArguments; + } + } +} + +function replacePublicSymbol(name, value, numArguments) { + if (!Module.hasOwnProperty(name)) { + throwInternalError('Replacing nonexistant public symbol'); + } + // If there's an overload table for this symbol, replace the symbol in the overload table instead. + if (undefined !== Module[name].overloadTable && undefined !== numArguments) { + Module[name].overloadTable[numArguments] = value; + } + else { + Module[name] = value; + } +} + +// from https://github.com/imvu/imvujs/blob/master/src/error.js +function extendError(baseErrorType, errorName) { + var errorClass = createNamedFunction(errorName, function(message) { + this.name = errorName; + this.message = message; + + var stack = (new Error(message)).stack; + if (stack !== undefined) { + this.stack = this.toString() + '\n' + + stack.replace(/^Error(:[^\n]*)?\n/, ''); + } + }); + errorClass.prototype = Object.create(baseErrorType.prototype); + errorClass.prototype.constructor = errorClass; + errorClass.prototype.toString = function() { + if (this.message === undefined) { + return this.name; + } else { + return this.name + ': ' + this.message; + } + }; + + return errorClass; +} + + +// from https://github.com/imvu/imvujs/blob/master/src/function.js function createNamedFunction(name, body) |