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) { /*jshint evil:true*/ return new Function( "body", "return function " + name + "() {\n" + + " \"use strict\";" + " return body.apply(this, arguments);\n" + "};\n" )(body); @@ -23,568 +150,1407 @@ function _embind_repr(v) { } } -var typeRegistry = {}; +// typeID -> { toWireType: ..., fromWireType: ... } +var registeredTypes = {}; + +// typeID -> [callback] +var awaitingDependencies = {}; + +// typeID -> [dependentTypes] +var typeDependencies = {}; + +// class typeID -> {pointerType: ..., constPointerType: ...} +var registeredPointers = {}; + +function registerType(rawType, registeredInstance) { + var name = registeredInstance.name; + if (!rawType) { + throwBindingError('type "' + name + '" must have a positive integer typeid pointer'); + } + if (registeredTypes.hasOwnProperty(rawType)) { + throwBindingError("Cannot register type '" + name + "' twice"); + } + + registeredTypes[rawType] = registeredInstance; + delete typeDependencies[rawType]; + + if (awaitingDependencies.hasOwnProperty(rawType)) { + var callbacks = awaitingDependencies[rawType]; + delete awaitingDependencies[rawType]; + callbacks.forEach(function(cb) { + cb(); + }); + } +} + +function whenDependentTypesAreResolved(myTypes, dependentTypes, getTypeConverters) { + myTypes.forEach(function(type) { + typeDependencies[type] = dependentTypes; + }); + + function onComplete(typeConverters) { + var myTypeConverters = getTypeConverters(typeConverters); + if (myTypeConverters.length !== myTypes.length) { + throwInternalError('Mismatched type converter count'); + } + for (var i = 0; i < myTypes.length; ++i) { + registerType(myTypes[i], myTypeConverters[i]); + } + } + + var typeConverters = new Array(dependentTypes.length); + var unregisteredTypes = []; + var registered = 0; + dependentTypes.forEach(function(dt, i) { + if (registeredTypes.hasOwnProperty(dt)) { + typeConverters[i] = registeredTypes[dt]; + } else { + unregisteredTypes.push(dt); + if (!awaitingDependencies.hasOwnProperty(dt)) { + awaitingDependencies[dt] = []; + } + awaitingDependencies[dt].push(function() { + typeConverters[i] = registeredTypes[dt]; + ++registered; + if (registered === unregisteredTypes.length) { + onComplete(typeConverters); + } + }); + } + }); + if (0 === unregisteredTypes.length) { + onComplete(typeConverters); + } +} + +var __charCodes = (function() { + var codes = new Array(256); + for (var i = 0; i < 256; ++i) { + codes[i] = String.fromCharCode(i); + } + return codes; +})(); + +function readLatin1String(ptr) { + var ret = ""; + var c = ptr; + while (HEAPU8[c]) { + ret += __charCodes[HEAPU8[c++]]; + } + return ret; +} + +function getTypeName(type) { + var ptr = ___getTypeName(type); + var rv = readLatin1String(ptr); + _free(ptr); + return rv; +} -function validateType(type, name) { - if (!type) { - throw new BindingError('type "' + name + '" must have a positive integer typeid pointer'); +function heap32VectorToArray(count, firstElement) { + var array = []; + for (var i = 0; i < count; i++) { + array.push(HEAP32[(firstElement >> 2) + i]); } - if (undefined !== typeRegistry[type]) { - throw new BindingError('cannot register type "' + name + '" twice'); + return array; +} + +function requireRegisteredType(rawType, humanName) { + var impl = registeredTypes[rawType]; + if (undefined === impl) { + throwBindingError(humanName + " has unknown type " + getTypeName(rawType)); } + return impl; } -function __embind_register_void(voidType, name) { - name = Pointer_stringify(name); - validateType(voidType, name); - typeRegistry[voidType] = { +function __embind_register_void(rawType, name) { + name = readLatin1String(name); + registerType(rawType, { name: name, - fromWireType: function() { + 'fromWireType': function() { return undefined; - } - }; + }, + }); } -function __embind_register_bool(boolType, name, trueValue, falseValue) { - name = Pointer_stringify(name); - validateType(boolType, name); - typeRegistry[boolType] = { +function __embind_register_bool(rawType, name, trueValue, falseValue) { + name = readLatin1String(name); + registerType(rawType, { name: name, - toWireType: function(destructors, o) { - return o ? trueValue : falseValue; + 'fromWireType': function(wt) { + // ambiguous emscripten ABI: sometimes return values are + // true or false, and sometimes integers (0 or 1) + return !!wt; }, - fromWireType: function(wt) { - return wt === trueValue; + 'toWireType': function(destructors, o) { + return o ? trueValue : falseValue; }, - }; + destructorFunction: null, // This type does not need a destructor + }); } -function __embind_register_integer(primitiveType, name) { - name = Pointer_stringify(name); - validateType(primitiveType, name); - typeRegistry[primitiveType] = { +// When converting a number from JS to C++ side, the valid range of the number is +// [minRange, maxRange], inclusive. +function __embind_register_integer(primitiveType, name, minRange, maxRange) { + name = readLatin1String(name); + if (maxRange === -1) { // LLVM doesn't have signed and unsigned 32-bit types, so u32 literals come out as 'i32 -1'. Always treat those as max u32. + maxRange = 4294967295; + } + registerType(primitiveType, { name: name, - toWireType: function(destructors, value) { + minRange: minRange, + maxRange: maxRange, + 'fromWireType': function(value) { + return value; + }, + 'toWireType': function(destructors, value) { + // todo: Here we have an opportunity for -O3 level "unsafe" optimizations: we could + // avoid the following two if()s and assume value is of proper type. if (typeof value !== "number") { - throw new TypeError('Cannot convert "' + _embind_repr(value) + '" to ' + name); + throw new TypeError('Cannot convert "' + _embind_repr(value) + '" to ' + this.name); + } + if (value < minRange || value > maxRange) { + throw new TypeError('Passing a number "' + _embind_repr(value) + '" from JS side to C/C++ side to an argument of type "' + name + '", which is outside the valid range [' + minRange + ', ' + maxRange + ']!'); } return value | 0; }, - fromWireType: function(value) { - return value; - } - }; + destructorFunction: null, // This type does not need a destructor + }); } -function __embind_register_float(primitiveType, name) { - name = Pointer_stringify(name); - validateType(primitiveType, name); - typeRegistry[primitiveType] = { +function __embind_register_float(rawType, name) { + name = readLatin1String(name); + registerType(rawType, { name: name, - toWireType: function(destructors, value) { + 'fromWireType': function(value) { + return value; + }, + 'toWireType': function(destructors, value) { + // todo: Here we have an opportunity for -O3 level "unsafe" optimizations: we could + // avoid the following if() and assume value is of proper type. if (typeof value !== "number") { - throw new TypeError('Cannot convert "' + _embind_repr(value) + '" to ' + name); + throw new TypeError('Cannot convert "' + _embind_repr(value) + '" to ' +this.name); } return value; }, - fromWireType: function(value) { - return value; - } - }; + destructorFunction: null, // This type does not need a destructor + }); } -function __embind_register_cstring(stringType, name) { - name = Pointer_stringify(name); - validateType(stringType, name); - typeRegistry[stringType] = { +function __embind_register_std_string(rawType, name) { + name = readLatin1String(name); + registerType(rawType, { name: name, - toWireType: function(destructors, value) { - var ptr = _malloc(value.length + 1); - writeStringToMemory(value, ptr); - destructors.push(_free); - destructors.push(ptr); + 'fromWireType': function(value) { + var length = HEAPU32[value >> 2]; + var a = new Array(length); + for (var i = 0; i < length; ++i) { + a[i] = String.fromCharCode(HEAPU8[value + 4 + i]); + } + _free(value); + return a.join(''); + }, + 'toWireType': function(destructors, value) { + if (value instanceof ArrayBuffer) { + value = new Uint8Array(value); + } + + function getTAElement(ta, index) { + return ta[index]; + } + function getStringElement(string, index) { + return string.charCodeAt(index); + } + var getElement; + if (value instanceof Uint8Array) { + getElement = getTAElement; + } else if (value instanceof Int8Array) { + getElement = getTAElement; + } else if (typeof value === 'string') { + getElement = getStringElement; + } else { + throwBindingError('Cannot pass non-string to std::string'); + } + + // assumes 4-byte alignment + var length = value.length; + var ptr = _malloc(4 + length); + HEAPU32[ptr >> 2] = length; + for (var i = 0; i < length; ++i) { + var charCode = getElement(value, i); + if (charCode > 255) { + _free(ptr); + throwBindingError('String has UTF-16 code units that do not fit in 8 bits'); + } + HEAPU8[ptr + 4 + i] = charCode; + } + if (destructors !== null) { + destructors.push(_free, ptr); + } return ptr; }, - fromWireType: function(value) { - var rv = Pointer_stringify(value); - _free(value); - return rv; - } - }; + destructorFunction: function(ptr) { _free(ptr); }, + }); } -function __embind_register_emval(emvalType, name) { - name = Pointer_stringify(name); - validateType(emvalType, name); - typeRegistry[emvalType] = { +function __embind_register_std_wstring(rawType, charSize, name) { + name = readLatin1String(name); + var HEAP, shift; + if (charSize === 2) { + HEAP = HEAPU16; + shift = 1; + } else if (charSize === 4) { + HEAP = HEAPU32; + shift = 2; + } + registerType(rawType, { name: name, - toWireType: function(destructors, value) { - return __emval_register(value); + 'fromWireType': function(value) { + var length = HEAPU32[value >> 2]; + var a = new Array(length); + var start = (value + 4) >> shift; + for (var i = 0; i < length; ++i) { + a[i] = String.fromCharCode(HEAP[start + i]); + } + _free(value); + return a.join(''); + }, + 'toWireType': function(destructors, value) { + // assumes 4-byte alignment + var length = value.length; + var ptr = _malloc(4 + length * charSize); + HEAPU32[ptr >> 2] = length; + var start = (ptr + 4) >> shift; + for (var i = 0; i < length; ++i) { + HEAP[start + i] = value.charCodeAt(i); + } + if (destructors !== null) { + destructors.push(_free, ptr); + } + return ptr; }, - fromWireType: function(handle) { + destructorFunction: function(ptr) { _free(ptr); }, + }); +} + +function __embind_register_emval(rawType, name) { + name = readLatin1String(name); + registerType(rawType, { + name: name, + 'fromWireType': function(handle) { var rv = _emval_handle_array[handle].value; __emval_decref(handle); return rv; - } - }; + }, + 'toWireType': function(destructors, value) { + return __emval_register(value); + }, + destructorFunction: null, // This type does not need a destructor + }); } -var BindingError = Error; -/** @expose */ -Module.BindingError = BindingError; - -function typeName(typeID) { - // could use our carnal knowledge of RTTI but for now just return the pointer... - return typeID; +function runDestructors(destructors) { + while (destructors.length) { + var ptr = destructors.pop(); + var del = destructors.pop(); + del(ptr); + } } -function requireRegisteredType(type, humanName) { - var impl = typeRegistry[type]; - if (undefined === impl) { - throw new BindingError(humanName + " has unknown type: " + typeName(type)); +// Function implementation of operator new, per +// http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf +// 13.2.2 +// ES3 +function new_(constructor, argumentList) { + if (!(constructor instanceof Function)) { + throw new TypeError('new_ called with constructor type ' + typeof(constructor) + " which is not a function"); } - return impl; + + /* + * Previously, the following line was just: + + function dummy() {}; + + * Unfortunately, Chrome was preserving 'dummy' as the object's name, even though at creation, the 'dummy' has the + * correct constructor name. Thus, objects created with IMVU.new would show up in the debugger as 'dummy', which + * isn't very helpful. Using IMVU.createNamedFunction addresses the issue. Doublely-unfortunately, there's no way + * to write a test for this behavior. -NRD 2013.02.22 + */ + var dummy = createNamedFunction(constructor.name, function(){}); + dummy.prototype = constructor.prototype; + var obj = new dummy; + + var r = constructor.apply(obj, argumentList); + return (r instanceof Object) ? r : obj; } -function requireArgumentTypes(argCount, argTypes, name) { - var argTypeImpls = new Array(argCount); - for (var i = 0; i < argCount; ++i) { - var argType = HEAP32[(argTypes >> 2) + i]; - argTypeImpls[i] = requireRegisteredType(argType, name + " parameter " + i); +// The path to interop from JS code to C++ code: +// (hand-written JS code) -> (autogenerated JS invoker) -> (template-generated C++ invoker) -> (target C++ function) +// craftInvokerFunction generates the JS invoker function for each function exposed to JS through embind. +function craftInvokerFunction(humanName, argTypes, classType, cppInvokerFunc, cppTargetFunc) { + // humanName: a human-readable string name for the function to be generated. + // argTypes: An array that contains the embind type objects for all types in the function signature. + // argTypes[0] is the type object for the function return value. + // argTypes[1] is the type object for function this object/class type, or null if not crafting an invoker for a class method. + // argTypes[2...] are the actual function parameters. + // classType: The embind type object for the class to be bound, or null if this is not a method of a class. + // cppInvokerFunc: JS Function object to the C++-side function that interops into C++ code. + // cppTargetFunc: Function pointer (an integer to FUNCTION_TABLE) to the target C++ function the cppInvokerFunc will end up calling. + var argCount = argTypes.length; + + if (argCount < 2) { + throwBindingError("argTypes array size mismatch! Must at least get return value and 'this' types!"); } - return argTypeImpls; -} + + var isClassMethodFunc = (argTypes[1] !== null && classType !== null); -function runDestructors(destructors) { - while (destructors.length) { - var ptr = destructors.pop(); - var del = destructors.pop(); - del(ptr); + if (!isClassMethodFunc && !FUNCTION_TABLE[cppTargetFunc]) { + throwBindingError('Global function '+humanName+' is not defined!'); } -} -function __embind_register_function(name, returnType, argCount, argTypes, invoker, fn) { - name = Pointer_stringify(name); - returnType = requireRegisteredType(returnType, "Function " + name + " return value"); - invoker = FUNCTION_TABLE[invoker]; - argTypes = requireArgumentTypes(argCount, argTypes, name); + // Free functions with signature "void function()" do not need an invoker that marshalls between wire types. +// TODO: This omits argument count check - enable only at -O3 or similar. +// if (ENABLE_UNSAFE_OPTS && argCount == 2 && argTypes[0].name == "void" && !isClassMethodFunc) { +// return FUNCTION_TABLE[fn]; +// } - Module[name] = function() { - if (arguments.length !== argCount) { - throw new BindingError('emscripten binding function ' + name + ' called with ' + arguments.length + ' arguments, expected ' + argCount); - } - var destructors = []; - var args = new Array(argCount + 1); - args[0] = fn; - for (var i = 0; i < argCount; ++i) { - args[i + 1] = argTypes[i].toWireType(destructors, arguments[i]); + var argsList = ""; + var argsListWired = ""; + for(var i = 0; i < argCount-2; ++i) { + argsList += (i!==0?", ":"")+"arg"+i; + argsListWired += (i!==0?", ":"")+"arg"+i+"Wired"; + } + + var invokerFnBody = + "return function "+makeLegalFunctionName(humanName)+"("+argsList+") {\n" + + "if (arguments.length !== "+(argCount - 2)+") {\n" + + "throwBindingError('function "+humanName+" called with ' + arguments.length + ' arguments, expected "+(argCount - 2)+" args!');\n" + + "}\n"; + + // Determine if we need to use a dynamic stack to store the destructors for the function parameters. + // TODO: Remove this completely once all function invokers are being dynamically generated. + var needsDestructorStack = false; + + for(var i = 1; i < argTypes.length; ++i) { // Skip return value at index 0 - it's not deleted here. + if (argTypes[i] !== null && argTypes[i].destructorFunction === undefined) { // The type does not define a destructor function - must use dynamic stack + needsDestructorStack = true; + break; } - var rv = returnType.fromWireType(invoker.apply(null, args)); - runDestructors(destructors); - return rv; - }; -} + } -function __embind_register_tuple(tupleType, name, constructor, destructor) { - name = Pointer_stringify(name); - constructor = FUNCTION_TABLE[constructor]; - destructor = FUNCTION_TABLE[destructor]; + if (needsDestructorStack) { + invokerFnBody += + "var destructors = [];\n"; + } - var elements = []; + var dtorStack = needsDestructorStack ? "destructors" : "null"; + var args1 = ["throwBindingError", "classType", "invoker", "fn", "runDestructors", "retType", "classParam"]; + var args2 = [throwBindingError, classType, cppInvokerFunc, cppTargetFunc, runDestructors, argTypes[0], argTypes[1]]; - typeRegistry[tupleType] = { - name: name, - elements: elements, - fromWireType: function(ptr) { - var len = elements.length; - var rv = new Array(len); - for (var i = 0; i < len; ++i) { - rv[i] = elements[i].read(ptr); - } - destructor(ptr); - return rv; - }, - toWireType: function(destructors, o) { - var len = elements.length; - if (len !== o.length) { - throw new TypeError("Incorrect number of tuple elements"); - } - var ptr = constructor(); - for (var i = 0; i < len; ++i) { - elements[i].write(ptr, o[i]); + if (isClassMethodFunc) { + invokerFnBody += "var thisWired = classParam.toWireType("+dtorStack+", this);\n"; + } + + for(var i = 0; i < argCount-2; ++i) { + invokerFnBody += "var arg"+i+"Wired = argType"+i+".toWireType("+dtorStack+", arg"+i+"); // "+argTypes[i+2].name+"\n"; + args1.push("argType"+i); + args2.push(argTypes[i+2]); + } + + if (isClassMethodFunc) { + argsListWired = "thisWired" + (argsListWired.length > 0 ? ", " : "") + argsListWired; + } + + var returns = (argTypes[0].name !== "void"); + + invokerFnBody += + (returns?"var rv = ":"") + "invoker(fn"+(argsListWired.length>0?", ":"")+argsListWired+");\n"; + + if (needsDestructorStack) { + invokerFnBody += "runDestructors(destructors);\n"; + } else { + for(var i = isClassMethodFunc?1:2; i < argTypes.length; ++i) { // Skip return value at index 0 - it's not deleted here. Also skip class type if not a method. + var paramName = (i === 1 ? "thisWired" : ("arg"+(i-2)+"Wired")); + if (argTypes[i].destructorFunction !== null) { + invokerFnBody += paramName+"_dtor("+paramName+"); // "+argTypes[i].name+"\n"; + args1.push(paramName+"_dtor"); + args2.push(argTypes[i].destructorFunction); } - destructors.push(destructor); - destructors.push(ptr); - return ptr; } - }; + } + + if (returns) { + invokerFnBody += "return retType.fromWireType(rv);\n"; + } + invokerFnBody += "}\n"; + + args1.push(invokerFnBody); + + var invokerFunction = new_(Function, args1).apply(null, args2); + return invokerFunction; } -function copyMemberPointer(memberPointer, memberPointerSize) { - var copy = _malloc(memberPointerSize); - if (!copy) { - throw new Error('Failed to allocate member pointer copy'); - } - _memcpy(copy, memberPointer, memberPointerSize); - return copy; +function __embind_register_function(name, argCount, rawArgTypesAddr, rawInvoker, fn) { + var argTypes = heap32VectorToArray(argCount, rawArgTypesAddr); + name = readLatin1String(name); + rawInvoker = FUNCTION_TABLE[rawInvoker]; + + exposePublicSymbol(name, function() { + throwUnboundTypeError('Cannot call ' + name + ' due to unbound types', argTypes); + }, argCount - 1); + + whenDependentTypesAreResolved([], argTypes, function(argTypes) { + var invokerArgsArray = [argTypes[0] /* return value */, null /* no class 'this'*/].concat(argTypes.slice(1) /* actual params */); + replacePublicSymbol(name, craftInvokerFunction(name, invokerArgsArray, null /* no class 'this'*/, rawInvoker, fn), argCount - 1); + return []; + }); +} + +var tupleRegistrations = {}; + +function __embind_register_tuple(rawType, name, rawConstructor, rawDestructor) { + tupleRegistrations[rawType] = { + name: readLatin1String(name), + rawConstructor: FUNCTION_TABLE[rawConstructor], + rawDestructor: FUNCTION_TABLE[rawDestructor], + elements: [], + }; } function __embind_register_tuple_element( - tupleType, - elementType, + rawTupleType, + getterReturnType, getter, + getterContext, + setterArgumentType, setter, - memberPointerSize, - memberPointer + setterContext ) { - tupleType = requireRegisteredType(tupleType, 'tuple'); - elementType = requireRegisteredType(elementType, "element " + tupleType.name + "[" + tupleType.elements.length + "]"); - getter = FUNCTION_TABLE[getter]; - setter = FUNCTION_TABLE[setter]; - memberPointer = copyMemberPointer(memberPointer, memberPointerSize); - - tupleType.elements.push({ - read: function(ptr) { - return elementType.fromWireType(getter(ptr, memberPointer)); - }, - write: function(ptr, o) { - var destructors = []; - setter(ptr, memberPointer, elementType.toWireType(destructors, o)); - runDestructors(destructors); - } + tupleRegistrations[rawTupleType].elements.push({ + getterReturnType: getterReturnType, + getter: FUNCTION_TABLE[getter], + getterContext: getterContext, + setterArgumentType: setterArgumentType, + setter: FUNCTION_TABLE[setter], + setterContext: setterContext, }); } -function __embind_register_tuple_element_accessor( - tupleType, - elementType, - staticGetter, - getterSize, - getter, - staticSetter, - setterSize, - setter -) { - tupleType = requireRegisteredType(tupleType, 'tuple'); - elementType = requireRegisteredType(elementType, "element " + tupleType.name + "[" + tupleType.elements.length + "]"); - staticGetter = FUNCTION_TABLE[staticGetter]; - getter = copyMemberPointer(getter, getterSize); - staticSetter = FUNCTION_TABLE[staticSetter]; - setter = copyMemberPointer(setter, setterSize); - - tupleType.elements.push({ - read: function(ptr) { - return elementType.fromWireType(staticGetter(ptr, HEAP32[getter >> 2])); - }, - write: function(ptr, o) { - var destructors = []; - staticSetter( - ptr, - HEAP32[setter >> 2], - elementType.toWireType(destructors, o)); - runDestructors(destructors); - } +function __embind_finalize_tuple(rawTupleType) { + var reg = tupleRegistrations[rawTupleType]; + delete tupleRegistrations[rawTupleType]; + var elements = reg.elements; + var elementsLength = elements.length; + var elementTypes = elements.map(function(elt) { return elt.getterReturnType; }). + concat(elements.map(function(elt) { return elt.setterArgumentType; })); + + var rawConstructor = reg.rawConstructor; + var rawDestructor = reg.rawDestructor; + + whenDependentTypesAreResolved([rawTupleType], elementTypes, function(elementTypes) { + elements.forEach(function(elt, i) { + var getterReturnType = elementTypes[i]; + var getter = elt.getter; + var getterContext = elt.getterContext; + var setterArgumentType = elementTypes[i + elementsLength]; + var setter = elt.setter; + var setterContext = elt.setterContext; + elt.read = function(ptr) { + return getterReturnType['fromWireType'](getter(getterContext, ptr)); + }; + elt.write = function(ptr, o) { + var destructors = []; + setter(setterContext, ptr, setterArgumentType['toWireType'](destructors, o)); + runDestructors(destructors); + }; + }); + + return [{ + name: reg.name, + 'fromWireType': function(ptr) { + var rv = new Array(elementsLength); + for (var i = 0; i < elementsLength; ++i) { + rv[i] = elements[i].read(ptr); + } + rawDestructor(ptr); + return rv; + }, + 'toWireType': function(destructors, o) { + if (elementsLength !== o.length) { + throw new TypeError("Incorrect number of tuple elements"); + } + var ptr = rawConstructor(); + for (var i = 0; i < elementsLength; ++i) { + elements[i].write(ptr, o[i]); + } + if (destructors !== null) { + destructors.push(rawDestructor, ptr); + } + return ptr; + }, + destructorFunction: rawDestructor, + }]; }); } +var structRegistrations = {}; + function __embind_register_struct( - structType, + rawType, name, - constructor, - destructor + rawConstructor, + rawDestructor ) { - name = Pointer_stringify(name); - constructor = FUNCTION_TABLE[constructor]; - destructor = FUNCTION_TABLE[destructor]; - - typeRegistry[structType] = { - fields: {}, - fromWireType: function(ptr) { - var fields = this.fields; - var rv = {}; - for (var i in fields) { - rv[i] = fields[i].read(ptr); - } - destructor(ptr); - return rv; - }, - toWireType: function(destructors, o) { - var fields = this.fields; - for (var fieldName in fields) { - if (!(fieldName in o)) { - throw new TypeError('Missing field'); - } - } - var ptr = constructor(); - for (var fieldName in fields) { - fields[fieldName].write(ptr, o[fieldName]); - } - destructors.push(destructor); - destructors.push(ptr); - return ptr; - } + structRegistrations[rawType] = { + name: readLatin1String(name), + rawConstructor: FUNCTION_TABLE[rawConstructor], + rawDestructor: FUNCTION_TABLE[rawDestructor], + fields: [], }; } function __embind_register_struct_field( structType, fieldName, - fieldType, + getterReturnType, getter, + getterContext, + setterArgumentType, setter, - memberPointerSize, - memberPointer + setterContext ) { - structType = requireRegisteredType(structType, 'struct'); - fieldName = Pointer_stringify(fieldName); - fieldType = requireRegisteredType(fieldType, 'field "' + structType.name + '.' + fieldName + '"'); - getter = FUNCTION_TABLE[getter]; - setter = FUNCTION_TABLE[setter]; - memberPointer = copyMemberPointer(memberPointer, memberPointerSize); + structRegistrations[structType].fields.push({ + fieldName: readLatin1String(fieldName), + getterReturnType: getterReturnType, + getter: FUNCTION_TABLE[getter], + getterContext: getterContext, + setterArgumentType: setterArgumentType, + setter: FUNCTION_TABLE[setter], + setterContext: setterContext, + }); +} + +function __embind_finalize_struct(structType) { + var reg = structRegistrations[structType]; + delete structRegistrations[structType]; + + var rawConstructor = reg.rawConstructor; + var rawDestructor = reg.rawDestructor; + var fieldRecords = reg.fields; + var fieldTypes = fieldRecords.map(function(field) { return field.getterReturnType; }). + concat(fieldRecords.map(function(field) { return field.setterArgumentType; })); + whenDependentTypesAreResolved([structType], fieldTypes, function(fieldTypes) { + var fields = {}; + fieldRecords.forEach(function(field, i) { + var fieldName = field.fieldName; + var getterReturnType = fieldTypes[i]; + var getter = field.getter; + var getterContext = field.getterContext; + var setterArgumentType = fieldTypes[i + fieldRecords.length]; + var setter = field.setter; + var setterContext = field.setterContext; + fields[fieldName] = { + read: function(ptr) { + return getterReturnType['fromWireType']( + getter(getterContext, ptr)); + }, + write: function(ptr, o) { + var destructors = []; + setter(setterContext, ptr, setterArgumentType['toWireType'](destructors, o)); + runDestructors(destructors); + } + }; + }); + + return [{ + name: reg.name, + 'fromWireType': function(ptr) { + var rv = {}; + for (var i in fields) { + rv[i] = fields[i].read(ptr); + } + rawDestructor(ptr); + return rv; + }, + 'toWireType': function(destructors, o) { + // todo: Here we have an opportunity for -O3 level "unsafe" optimizations: + // assume all fields are present without checking. + for (var fieldName in fields) { + if (!(fieldName in o)) { + throw new TypeError('Missing field'); + } + } + var ptr = rawConstructor(); + for (fieldName in fields) { + fields[fieldName].write(ptr, o[fieldName]); + } + if (destructors !== null) { + destructors.push(rawDestructor, ptr); + } + return ptr; + }, + destructorFunction: rawDestructor, + }]; + }); +} + +var genericPointerToWireType = function(destructors, handle) { + if (handle === null) { + if (this.isReference) { + throwBindingError('null is not a valid ' + this.name); + } + + if (this.isSmartPointer) { + var ptr = this.rawConstructor(); + destructors.push(this.rawDestructor, ptr); + return ptr; + } else { + return 0; + } + } + + if (!handle.$$) { + throwBindingError('Cannot pass "' + _embind_repr(handle) + '" as a ' + this.name); + } + if (!handle.$$.ptr) { + throwBindingError('Cannot pass deleted object as a pointer of type ' + this.name); + } + if (!this.isConst && handle.$$.ptrType.isConst) { + throwBindingError('Cannot convert argument of type ' + (handle.$$.smartPtrType ? handle.$$.smartPtrType.name : handle.$$.ptrType.name) + ' to parameter type ' + this.name); + } + var handleClass = handle.$$.ptrType.registeredClass; + var ptr = upcastPointer(handle.$$.ptr, handleClass, this.registeredClass); + + if (this.isSmartPointer) { + // TODO: this is not strictly true + // We could support BY_EMVAL conversions from raw pointers to smart pointers + // because the smart pointer can hold a reference to the handle + if (undefined === handle.$$.smartPtr) { + throwBindingError('Passing raw pointer to smart pointer is illegal'); + } + + switch (this.sharingPolicy) { + case 0: // NONE + // no upcasting + if (handle.$$.smartPtrType === this) { + ptr = handle.$$.smartPtr; + } else { + throwBindingError('Cannot convert argument of type ' + (handle.$$.smartPtrType ? handle.$$.smartPtrType.name : handle.$$.ptrType.name) + ' to parameter type ' + this.name); + } + break; + + case 1: // INTRUSIVE + ptr = handle.$$.smartPtr; + break; + + case 2: // BY_EMVAL + if (handle.$$.smartPtrType === this) { + ptr = handle.$$.smartPtr; + } else { + var clonedHandle = handle.clone(); + ptr = this.rawShare( + ptr, + __emval_register(function() { + clonedHandle.delete(); + }) + ); + destructors.push(this.rawDestructor, ptr); + } + break; + + default: + throwBindingError('Unsupporting sharing policy'); + } + } + return ptr; +}; + +// If we know a pointer type is not going to have SmartPtr logic in it, we can +// special-case optimize it a bit (compare to genericPointerToWireType) +var constNoSmartPtrRawPointerToWireType = function(destructors, handle) { + if (handle === null) { + if (this.isReference) { + throwBindingError('null is not a valid ' + this.name); + } + return 0; + } - structType.fields[fieldName] = { - read: function(ptr) { - return fieldType.fromWireType(getter(ptr, memberPointer)); + if (!handle.$$) { + throwBindingError('Cannot pass "' + _embind_repr(handle) + '" as a ' + this.name); + } + if (!handle.$$.ptr) { + throwBindingError('Cannot pass deleted object as a pointer of type ' + this.name); + } + var handleClass = handle.$$.ptrType.registeredClass; + var ptr = upcastPointer(handle.$$.ptr, handleClass, this.registeredClass); + return ptr; +}; + +// An optimized version for non-const method accesses - there we must additionally restrict that +// the pointer is not a const-pointer. +var nonConstNoSmartPtrRawPointerToWireType = function(destructors, handle) { + if (handle === null) { + if (this.isReference) { + throwBindingError('null is not a valid ' + this.name); + } + return 0; + } + + if (!handle.$$) { + throwBindingError('Cannot pass "' + _embind_repr(handle) + '" as a ' + this.name); + } + if (!handle.$$.ptr) { + throwBindingError('Cannot pass deleted object as a pointer of type ' + this.name); + } + if (handle.$$.ptrType.isConst) { + throwBindingError('Cannot convert argument of type ' + handle.$$.ptrType.name + ' to parameter type ' + this.name); + } + var handleClass = handle.$$.ptrType.registeredClass; + var ptr = upcastPointer(handle.$$.ptr, handleClass, this.registeredClass); + return ptr; +}; + +function RegisteredPointer( + name, + registeredClass, + isReference, + isConst, + + // smart pointer properties + isSmartPointer, + pointeeType, + sharingPolicy, + rawGetPointee, + rawConstructor, + rawShare, + rawDestructor +) { + this.name = name; + this.registeredClass = registeredClass; + this.isReference = isReference; + this.isConst = isConst; + + // smart pointer properties + this.isSmartPointer = isSmartPointer; + this.pointeeType = pointeeType; + this.sharingPolicy = sharingPolicy; + this.rawGetPointee = rawGetPointee; + this.rawConstructor = rawConstructor; + this.rawShare = rawShare; + this.rawDestructor = rawDestructor; + + if (!isSmartPointer && registeredClass.baseClass === undefined) { + if (isConst) { + this['toWireType'] = constNoSmartPtrRawPointerToWireType; + this.destructorFunction = null; + } else { + this['toWireType'] = nonConstNoSmartPtrRawPointerToWireType; + this.destructorFunction = null; + } + } else { + this['toWireType'] = genericPointerToWireType; + // Here we must leave this.destructorFunction undefined, since whether genericPointerToWireType returns + // a pointer that needs to be freed up is runtime-dependent, and cannot be evaluated at registration time. + // TODO: Create an alternative mechanism that allows removing the use of var destructors = []; array in + // craftInvokerFunction altogether. + } +} + +RegisteredPointer.prototype.getPointee = function(ptr) { + if (this.rawGetPointee) { + ptr = this.rawGetPointee(ptr); + } + return ptr; +}; + +RegisteredPointer.prototype.destructor = function(ptr) { + if (this.rawDestructor) { + this.rawDestructor(ptr); + } +}; + +RegisteredPointer.prototype['fromWireType'] = function(ptr) { + // ptr is a raw pointer (or a raw smartpointer) + + // rawPointer is a maybe-null raw pointer + var rawPointer = this.getPointee(ptr); + if (!rawPointer) { + this.destructor(ptr); + return null; + } + + function makeDefaultHandle() { + if (this.isSmartPointer) { + return makeClassHandle(this.registeredClass.instancePrototype, { + ptrType: this.pointeeType, + ptr: rawPointer, + smartPtrType: this, + smartPtr: ptr, + }); + } else { + return makeClassHandle(this.registeredClass.instancePrototype, { + ptrType: this, + ptr: ptr, + }); + } + } + + var actualType = this.registeredClass.getActualType(rawPointer); + var registeredPointerRecord = registeredPointers[actualType]; + if (!registeredPointerRecord) { + return makeDefaultHandle.call(this); + } + + var toType; + if (this.isConst) { + toType = registeredPointerRecord.constPointerType; + } else { + toType = registeredPointerRecord.pointerType; + } + var dp = downcastPointer( + rawPointer, + this.registeredClass, + toType.registeredClass); + if (dp === null) { + return makeDefaultHandle.call(this); + } + if (this.isSmartPointer) { + return makeClassHandle(toType.registeredClass.instancePrototype, { + ptrType: toType, + ptr: dp, + smartPtrType: this, + smartPtr: ptr, + }); + } else { + return makeClassHandle(toType.registeredClass.instancePrototype, { + ptrType: toType, + ptr: dp, + }); + } +}; + +function makeClassHandle(prototype, record) { + if (!record.ptrType || !record.ptr) { + throwInternalError('makeClassHandle requires ptr and ptrType'); + } + var hasSmartPtrType = !!record.smartPtrType; + var hasSmartPtr = !!record.smartPtr; + if (hasSmartPtrType !== hasSmartPtr) { + throwInternalError('Both smartPtrType and smartPtr must be specified'); + } + record.count = { value: 1 }; + return Object.create(prototype, { + $$: { + value: record, }, - write: function(ptr, o) { - var destructors = []; - setter(ptr, memberPointer, fieldType.toWireType(destructors, o)); - runDestructors(destructors); + }); +} + +// root of all pointer and smart pointer handles in embind +function ClassHandle() { +} + +function getInstanceTypeName(handle) { + return handle.$$.ptrType.registeredClass.name; +} + +ClassHandle.prototype.clone = function() { + if (!this.$$.ptr) { + throwBindingError(getInstanceTypeName(this) + ' instance already deleted'); + } + + var clone = Object.create(Object.getPrototypeOf(this), { + $$: { + value: shallowCopy(this.$$), } - }; + }); + + clone.$$.count.value += 1; + return clone; +}; + +function runDestructor(handle) { + var $$ = handle.$$; + if ($$.smartPtr) { + $$.smartPtrType.rawDestructor($$.smartPtr); + } else { + $$.ptrType.registeredClass.rawDestructor($$.ptr); + } +} + +ClassHandle.prototype['delete'] = function() { + if (!this.$$.ptr) { + throwBindingError(getInstanceTypeName(this) + ' instance already deleted'); + } + + this.$$.count.value -= 1; + if (0 === this.$$.count.value) { + runDestructor(this); + } + this.$$.smartPtr = undefined; + this.$$.ptr = undefined; +}; + +function RegisteredClass( + name, + constructor, + instancePrototype, + rawDestructor, + baseClass, + getActualType, + upcast, + downcast +) { + this.name = name; + this.constructor = constructor; + this.instancePrototype = instancePrototype; + this.rawDestructor = rawDestructor; + this.baseClass = baseClass; + this.getActualType = getActualType; + this.upcast = upcast; + this.downcast = downcast; +} + +function shallowCopy(o) { + var rv = {}; + for (var k in o) { + rv[k] = o[k]; + } + return rv; } function __embind_register_class( - classType, + rawType, + rawPointerType, + rawConstPointerType, + baseClassRawType, + getActualType, + upcast, + downcast, name, - destructor + rawDestructor ) { - name = Pointer_stringify(name); - destructor = FUNCTION_TABLE[destructor]; + name = readLatin1String(name); + rawDestructor = FUNCTION_TABLE[rawDestructor]; + getActualType = FUNCTION_TABLE[getActualType]; + upcast = FUNCTION_TABLE[upcast]; + downcast = FUNCTION_TABLE[downcast]; + var legalFunctionName = makeLegalFunctionName(name); - var Handle = createNamedFunction(name, function(ptr) { - this.count = {value: 1}; - this.ptr = ptr; + exposePublicSymbol(legalFunctionName, function() { + // this code cannot run if baseClassRawType is zero + throwUnboundTypeError('Cannot construct ' + name + ' due to unbound types', [baseClassRawType]); }); - Handle.prototype.clone = function() { - if (!this.ptr) { - throw new BindingError(classType.name + ' instance already deleted'); - } + whenDependentTypesAreResolved( + [rawType, rawPointerType, rawConstPointerType], + baseClassRawType ? [baseClassRawType] : [], + function(base) { + base = base[0]; + + var baseClass; + var basePrototype; + if (baseClassRawType) { + baseClass = base.registeredClass; + basePrototype = baseClass.instancePrototype; + } else { + basePrototype = ClassHandle.prototype; + } + + var constructor = createNamedFunction(legalFunctionName, function() { + if (Object.getPrototypeOf(this) !== instancePrototype) { + throw new BindingError("Use 'new' to construct " + name); + } + if (undefined === registeredClass.constructor_body) { + throw new BindingError(name + " has no accessible constructor"); + } + var body = registeredClass.constructor_body[arguments.length]; + if (undefined === body) { + throw new BindingError("Tried to invoke ctor of " + name + " with invalid number of parameters (" + arguments.length + ") - expected (" + Object.keys(registeredClass.constructor_body).toString() + ") parameters instead!"); + } + return body.apply(this, arguments); + }); + + var instancePrototype = Object.create(basePrototype, { + constructor: { value: constructor }, + }); + + constructor.prototype = instancePrototype; - var clone = Object.create(Handle.prototype); - clone.count = this.count; - clone.ptr = this.ptr; + var registeredClass = new RegisteredClass( + name, + constructor, + instancePrototype, + rawDestructor, + baseClass, + getActualType, + upcast, + downcast); + + var referenceConverter = new RegisteredPointer( + name, + registeredClass, + true, + false, + false); - clone.count.value += 1; - return clone; - }; + var pointerConverter = new RegisteredPointer( + name + '*', + registeredClass, + false, + false, + false); - Handle.prototype.move = function() { - var rv = this.clone(); - this.delete(); - return rv; - }; + var constPointerConverter = new RegisteredPointer( + name + ' const*', + registeredClass, + false, + true, + false); - Handle.prototype['delete'] = function() { - if (!this.ptr) { - throw new BindingError(classType.name + ' instance already deleted'); - } + registeredPointers[rawType] = { + pointerType: pointerConverter, + constPointerType: constPointerConverter + }; - this.count.value -= 1; - if (0 === this.count.value) { - destructor(this.ptr); - } - this.ptr = undefined; - }; + replacePublicSymbol(legalFunctionName, constructor); - var constructor = createNamedFunction(name, function() { - var body = constructor.body; - body.apply(this, arguments); - }); - constructor.prototype = Object.create(Handle.prototype); - - typeRegistry[classType] = { - name: name, - constructor: constructor, - Handle: Handle, - fromWireType: function(ptr) { - return new Handle(ptr); - }, - toWireType: function(destructors, o) { - return o.ptr; + return [referenceConverter, pointerConverter, constPointerConverter]; } - }; - - Module[name] = constructor; + ); } function __embind_register_class_constructor( - classType, + rawClassType, argCount, - argTypes, - constructor + rawArgTypesAddr, + invoker, + rawConstructor ) { - classType = requireRegisteredType(classType, 'class'); - var humanName = 'constructor ' + classType.name; - argTypes = requireArgumentTypes(argCount, argTypes, humanName); - constructor = FUNCTION_TABLE[constructor]; - - classType.constructor.body = function() { - if (arguments.length !== argCount) { - throw new BindingError('emscripten binding ' + humanName + ' called with ' + arguments.length + ' arguments, expected ' + argCount); + var rawArgTypes = heap32VectorToArray(argCount, rawArgTypesAddr); + invoker = FUNCTION_TABLE[invoker]; + + whenDependentTypesAreResolved([], [rawClassType], function(classType) { + classType = classType[0]; + var humanName = 'constructor ' + classType.name; + + if (undefined === classType.registeredClass.constructor_body) { + classType.registeredClass.constructor_body = []; } - var destructors = []; - var args = new Array(argCount); - for (var i = 0; i < argCount; ++i) { - args[i] = argTypes[i].toWireType(destructors, arguments[i]); + if (undefined !== classType.registeredClass.constructor_body[argCount - 1]) { + throw new BindingError("Cannot register multiple constructors with identical number of parameters (" + (argCount-1) + ") for class '" + classType.name + "'! Overload resolution is currently only performed using the parameter count, not actual type info!"); } + classType.registeredClass.constructor_body[argCount - 1] = function() { + throwUnboundTypeError('Cannot construct ' + classType.name + ' due to unbound types', rawArgTypes); + }; - var ptr = constructor.apply(null, args); - runDestructors(destructors); - classType.Handle.call(this, ptr); - }; + whenDependentTypesAreResolved([], rawArgTypes, function(argTypes) { + classType.registeredClass.constructor_body[argCount - 1] = function() { + if (arguments.length !== argCount - 1) { + throwBindingError(humanName + ' called with ' + arguments.length + ' arguments, expected ' + (argCount-1)); + } + var destructors = []; + var args = new Array(argCount); + args[0] = rawConstructor; + for (var i = 1; i < argCount; ++i) { + args[i] = argTypes[i]['toWireType'](destructors, arguments[i - 1]); + } + + var ptr = invoker.apply(null, args); + runDestructors(destructors); + + return argTypes[0]['fromWireType'](ptr); + }; + return []; + }); + return []; + }); } -function __embind_register_class_method( - classType, +function downcastPointer(ptr, ptrClass, desiredClass) { + if (ptrClass === desiredClass) { + return ptr; + } + if (undefined === desiredClass.baseClass) { + return null; // no conversion + } + // O(depth) stack space used + return desiredClass.downcast( + downcastPointer(ptr, ptrClass, desiredClass.baseClass)); +} + +function upcastPointer(ptr, ptrClass, desiredClass) { + while (ptrClass !== desiredClass) { + if (!ptrClass.upcast) { + throwBindingError("Expected null or instance of " + desiredClass.name + ", got an instance of " + ptrClass.name); + } + ptr = ptrClass.upcast(ptr); + ptrClass = ptrClass.baseClass; + } + return ptr; +} + +function validateThis(this_, classType, humanName) { + if (!(this_ instanceof Object)) { + throwBindingError(humanName + ' with invalid "this": ' + this_); + } + if (!(this_ instanceof classType.registeredClass.constructor)) { + throwBindingError(humanName + ' incompatible with "this" of type ' + this_.constructor.name); + } + if (!this_.$$.ptr) { + throwBindingError('cannot call emscripten binding method ' + humanName + ' on deleted object'); + } + + // todo: kill this + return upcastPointer( + this_.$$.ptr, + this_.$$.ptrType.registeredClass, + classType.registeredClass); +} + +function __embind_register_class_function( + rawClassType, methodName, - returnType, argCount, - argTypes, - invoker, - memberFunctionSize, - memberFunction + rawArgTypesAddr, // [ReturnType, ThisType, Args...] + rawInvoker, + context ) { - classType = requireRegisteredType(classType, 'class'); - methodName = Pointer_stringify(methodName); - var humanName = classType.name + '.' + methodName; - returnType = requireRegisteredType(returnType, 'method ' + humanName + ' return value'); - argTypes = requireArgumentTypes(argCount, argTypes, 'method ' + humanName); - invoker = FUNCTION_TABLE[invoker]; - memberFunction = copyMemberPointer(memberFunction, memberFunctionSize); + var rawArgTypes = heap32VectorToArray(argCount, rawArgTypesAddr); + methodName = readLatin1String(methodName); + rawInvoker = FUNCTION_TABLE[rawInvoker]; - classType.Handle.prototype[methodName] = function() { - if (!this.ptr) { - throw new BindingError('cannot call emscripten binding method ' + humanName + ' on deleted object'); - } - if (arguments.length !== argCount) { - throw new BindingError('emscripten binding method ' + humanName + ' called with ' + arguments.length + ' arguments, expected ' + argCount); + whenDependentTypesAreResolved([], [rawClassType], function(classType) { + classType = classType[0]; + var humanName = classType.name + '.' + methodName; + + var unboundTypesHandler = function() { + throwUnboundTypeError('Cannot call ' + humanName + ' due to unbound types', rawArgTypes); + }; + + var proto = classType.registeredClass.instancePrototype; + var method = proto[methodName]; + if (undefined === method || (undefined === method.overloadTable && method.className !== classType.name && method.argCount === argCount-2)) { + // This is the first overload to be registered, OR we are replacing a function in the base class with a function in the derived class. + unboundTypesHandler.argCount = argCount-2; + unboundTypesHandler.className = classType.name; + proto[methodName] = unboundTypesHandler; + } else { + // There was an existing function with the same name registered. Set up a function overload routing table. + ensureOverloadTable(proto, methodName, humanName); + proto[methodName].overloadTable[argCount-2] = unboundTypesHandler; } + + whenDependentTypesAreResolved([], rawArgTypes, function(argTypes) { - var destructors = []; - var args = new Array(argCount + 2); - args[0] = this.ptr; - args[1] = memberFunction; - for (var i = 0; i < argCount; ++i) { - args[i + 2] = argTypes[i].toWireType(destructors, arguments[i]); - } + var memberFunction = craftInvokerFunction(humanName, argTypes, classType, rawInvoker, context); - var rv = returnType.fromWireType(invoker.apply(null, args)); - runDestructors(destructors); - return rv; - }; + // Replace the initial unbound-handler-stub function with the appropriate member function, now that all types + // are resolved. If multiple overloads are registered for this function, the function goes into an overload table. + if (undefined === proto[methodName].overloadTable) { + proto[methodName] = memberFunction; + } else { + proto[methodName].overloadTable[argCount-2] = memberFunction; + } + + return []; + }); + return []; + }); } -function __embind_register_class_classmethod( - classType, +function __embind_register_class_class_function( + rawClassType, methodName, - returnType, argCount, - argTypes, - method + rawArgTypesAddr, + rawInvoker, + fn ) { - classType = requireRegisteredType(classType, 'class'); - methodName = Pointer_stringify(methodName); - var humanName = classType.name + '.' + methodName; - returnType = requireRegisteredType(returnType, 'classmethod ' + humanName + ' return value'); - argTypes = requireArgumentTypes(argCount, argTypes, 'classmethod ' + humanName); - method = FUNCTION_TABLE[method]; - - classType.constructor[methodName] = function() { - if (arguments.length !== argCount) { - throw new BindingError('emscripten binding method ' + humanName + ' called with ' + arguments.length + ' arguments, expected ' + argCount); - } + var rawArgTypes = heap32VectorToArray(argCount, rawArgTypesAddr); + methodName = readLatin1String(methodName); + rawInvoker = FUNCTION_TABLE[rawInvoker]; + whenDependentTypesAreResolved([], [rawClassType], function(classType) { + classType = classType[0]; + var humanName = classType.name + '.' + methodName; + + var unboundTypesHandler = function() { + throwUnboundTypeError('Cannot call ' + humanName + ' due to unbound types', rawArgTypes); + }; - var destructors = []; - var args = new Array(argCount); - for (var i = 0; i < argCount; ++i) { - args[i] = argTypes[i].toWireType(destructors, arguments[i]); + var proto = classType.registeredClass.constructor; + if (undefined === proto[methodName]) { + // This is the first function to be registered with this name. + unboundTypesHandler.argCount = argCount-1; + proto[methodName] = unboundTypesHandler; + } else { + // There was an existing function with the same name registered. Set up a function overload routing table. + ensureOverloadTable(proto, methodName, humanName); + proto[methodName].overloadTable[argCount-1] = unboundTypesHandler; } - var rv = returnType.fromWireType(method.apply(null, args)); - runDestructors(destructors); - return rv; - }; + whenDependentTypesAreResolved([], rawArgTypes, function(argTypes) { + // Replace the initial unbound-types-handler stub with the proper function. If multiple overloads are registered, + // the function handlers go into an overload table. + var invokerArgsArray = [argTypes[0] /* return value */, null /* no class 'this'*/].concat(argTypes.slice(1) /* actual params */); + var func = craftInvokerFunction(humanName, invokerArgsArray, null /* no class 'this'*/, rawInvoker, fn); + if (undefined === proto[methodName].overloadTable) { + proto[methodName] = func; + } else { + proto[methodName].overloadTable[argCount-1] = func; + } + return []; + }); + return []; + }); } -function __embind_register_class_field( +function __embind_register_class_property( classType, fieldName, - fieldType, + getterReturnType, getter, + getterContext, + setterArgumentType, setter, - memberPointerSize, - memberPointer + setterContext ) { - classType = requireRegisteredType(classType, 'class'); - fieldName = Pointer_stringify(fieldName); - var humanName = classType.name + '.' + fieldName; - fieldType = requireRegisteredType(fieldType, 'field ' + humanName); + fieldName = readLatin1String(fieldName); getter = FUNCTION_TABLE[getter]; - setter = FUNCTION_TABLE[setter]; - memberPointer = copyMemberPointer(memberPointer, memberPointerSize); - Object.defineProperty(classType.Handle.prototype, fieldName, { - get: function() { - if (!this.ptr) { - throw new BindingError('cannot access emscripten binding field ' + humanName + ' on deleted object'); - } - return fieldType.fromWireType(getter(this.ptr, memberPointer)); - }, - set: function(v) { - if (!this.ptr) { - throw new BindingError('cannot modify emscripten binding field ' + humanName + ' on deleted object'); + whenDependentTypesAreResolved([], [classType], function(classType) { + classType = classType[0]; + var humanName = classType.name + '.' + fieldName; + var desc = { + get: function() { + throwUnboundTypeError('Cannot access ' + humanName + ' due to unbound types', [getterReturnType, setterArgumentType]); + }, + enumerable: true, + configurable: true + }; + if (setter) { + desc.set = function() { + throwUnboundTypeError('Cannot access ' + humanName + ' due to unbound types', [getterReturnType, setterArgumentType]); + }; + } else { + desc.set = function(v) { + throwBindingError(humanName + ' is a read-only property'); + }; + } + + Object.defineProperty(classType.registeredClass.instancePrototype, fieldName, desc); + + whenDependentTypesAreResolved( + [], + (setter ? [getterReturnType, setterArgumentType] : [getterReturnType]), + function(types) { + var getterReturnType = types[0]; + var desc = { + get: function() { + var ptr = validateThis(this, classType, humanName + ' getter'); + return getterReturnType['fromWireType'](getter(getterContext, ptr)); + }, + enumerable: true + }; + + if (setter) { + setter = FUNCTION_TABLE[setter]; + var setterArgumentType = types[1]; + desc.set = function(v) { + var ptr = validateThis(this, classType, humanName + ' setter'); + var destructors = []; + setter(setterContext, ptr, setterArgumentType['toWireType'](destructors, v)); + runDestructors(destructors); + }; } - var destructors = []; - setter(this.ptr, memberPointer, fieldType.toWireType(destructors, v)); - runDestructors(destructors); - }, - enumerable: true + + Object.defineProperty(classType.registeredClass.instancePrototype, fieldName, desc); + return []; + }); + + return []; + }); +} + +var char_0 = '0'.charCodeAt(0); +var char_9 = '9'.charCodeAt(0); +function makeLegalFunctionName(name) { + name = name.replace(/[^a-zA-Z0-9_]/g, '$'); + var f = name.charCodeAt(0); + if (f >= char_0 && f <= char_9) { + return '_' + name; + } else { + return name; + } +} + +function __embind_register_smart_ptr( + rawType, + rawPointeeType, + name, + sharingPolicy, + rawGetPointee, + rawConstructor, + rawShare, + rawDestructor +) { + name = readLatin1String(name); + rawGetPointee = FUNCTION_TABLE[rawGetPointee]; + rawConstructor = FUNCTION_TABLE[rawConstructor]; + rawShare = FUNCTION_TABLE[rawShare]; + rawDestructor = FUNCTION_TABLE[rawDestructor]; + + whenDependentTypesAreResolved([rawType], [rawPointeeType], function(pointeeType) { + pointeeType = pointeeType[0]; + + var registeredPointer = new RegisteredPointer( + name, + pointeeType.registeredClass, + false, + false, + // smart pointer properties + true, + pointeeType, + sharingPolicy, + rawGetPointee, + rawConstructor, + rawShare, + rawDestructor); + return [registeredPointer]; }); } function __embind_register_enum( - enumType, + rawType, name ) { - name = Pointer_stringify(name); + name = readLatin1String(name); - function Enum() { + function constructor() { } - Enum.values = {}; + constructor.values = {}; - typeRegistry[enumType] = { + registerType(rawType, { name: name, - constructor: Enum, - toWireType: function(destructors, c) { - return c.value; + constructor: constructor, + 'fromWireType': function(c) { + return this.constructor.values[c]; }, - fromWireType: function(c) { - return Enum.values[c]; + 'toWireType': function(destructors, c) { + return c.value; }, - }; - - Module[name] = Enum; + destructorFunction: null, + }); + exposePublicSymbol(name, constructor); } function __embind_register_enum_value( - enumType, + rawEnumType, name, enumValue ) { - enumType = requireRegisteredType(enumType, 'enum'); - name = Pointer_stringify(name); + var enumType = requireRegisteredType(rawEnumType, 'enum'); + name = readLatin1String(name); var Enum = enumType.constructor; @@ -596,25 +1562,11 @@ function __embind_register_enum_value( Enum[name] = Value; } -function __embind_register_interface( - interfaceType, - name, - constructor, - destructor -) { - name = Pointer_stringify(name); - constructor = FUNCTION_TABLE[constructor]; - destructor = FUNCTION_TABLE[destructor]; - - typeRegistry[interfaceType] = { - name: name, - toWireType: function(destructors, o) { - var handle = __emval_register(o); - var ptr = constructor(handle); - destructors.push(destructor); - destructors.push(ptr); - return ptr; - }, - }; +function __embind_register_constant(name, type, value) { + name = readLatin1String(name); + whenDependentTypesAreResolved([], [type], function(type) { + type = type[0]; + Module[name] = type['fromWireType'](value); + return []; + }); } - diff --git a/src/embind/emval.js b/src/embind/emval.js index 9574ab37..c02ffa92 100644..100755 --- a/src/embind/emval.js +++ b/src/embind/emval.js @@ -1,34 +1,74 @@ /*global Module*/ /*global HEAP32*/ -/*global Pointer_stringify, writeStringToMemory*/ -/*global requireRegisteredType*/ +/*global readLatin1String, writeStringToMemory*/ +/*global requireRegisteredType, throwBindingError*/ -var _emval_handle_array = []; +var _emval_handle_array = [{}]; // reserve zero var _emval_free_list = []; // Public JS API /** @expose */ Module.count_emval_handles = function() { - return _emval_handle_array.length; + var count = 0; + for (var i = 1; i < _emval_handle_array.length; ++i) { + if (_emval_handle_array[i] !== undefined) { + ++count; + } + } + return count; +}; + +/** @expose */ +Module.get_first_emval = function() { + for (var i = 1; i < _emval_handle_array.length; ++i) { + if (_emval_handle_array[i] !== undefined) { + return _emval_handle_array[i]; + } + } + return null; }; // Private C++ API +var _emval_symbols = {}; // address -> string + +function __emval_register_symbol(address) { + _emval_symbols[address] = readLatin1String(address); +} + +function getStringOrSymbol(address) { + var symbol = _emval_symbols[address]; + if (symbol === undefined) { + return readLatin1String(address); + } else { + return symbol; + } +} + +function requireHandle(handle) { + if (!handle) { + throwBindingError('Cannot use deleted val. handle = ' + handle); + } +} + function __emval_register(value) { var handle = _emval_free_list.length ? _emval_free_list.pop() : _emval_handle_array.length; + _emval_handle_array[handle] = {refcount: 1, value: value}; return handle; } function __emval_incref(handle) { - _emval_handle_array[handle].refcount += 1; + if (handle) { + _emval_handle_array[handle].refcount += 1; + } } function __emval_decref(handle) { - if (0 === --_emval_handle_array[handle].refcount) { + if (handle && 0 === --_emval_handle_array[handle].refcount) { delete _emval_handle_array[handle]; _emval_free_list.push(handle); @@ -40,72 +80,153 @@ function __emval_decref(handle) { } } +function __emval_new_array() { + return __emval_register([]); +} + function __emval_new_object() { return __emval_register({}); } -function __emval_new_long(value) { - return __emval_register(value); +function __emval_undefined() { + return __emval_register(undefined); } -function __emval_new_cstring(str) { - return __emval_register(Pointer_stringify(str)); +function __emval_null() { + return __emval_register(null); } -function __emval_get_property(handle, k) { - k = Pointer_stringify(k); - return __emval_register(_emval_handle_array[handle].value[k]); +function __emval_new_cstring(v) { + return __emval_register(getStringOrSymbol(v)); } -function __emval_get_property_by_long(handle, k) { - return __emval_register(_emval_handle_array[handle].value[k]); +function __emval_take_value(type, v) { + type = requireRegisteredType(type, '_emval_take_value'); + v = type.fromWireType(v); + return __emval_register(v); } -function __emval_get_property_by_unsigned_long(handle, k) { - return __emval_register(_emval_handle_array[handle].value[k]); +var __newers = {}; // arity -> function + +function __emval_new(handle, argCount, argTypes) { + requireHandle(handle); + + var args = parseParameters( + argCount, + argTypes, + Array.prototype.slice.call(arguments, 3)); + + // Alas, we are forced to use operator new until WebKit enables + // constructing typed arrays without new. + // In WebKit, Uint8Array(10) throws an error. + // In every other browser, it's identical to new Uint8Array(10). + + var newer = __newers[argCount]; + if (!newer) { + var parameters = new Array(argCount); + for (var i = 0; i < argCount; ++i) { + parameters[i] = 'a' + i; + } + /*jshint evil:true*/ + newer = __newers[argCount] = new Function( + ['c'].concat(parameters), + "return new c(" + parameters.join(',') + ");"); + } + + var constructor = _emval_handle_array[handle].value; + var obj = newer.apply(undefined, [constructor].concat(args)); +/* + // implement what amounts to operator new + function dummy(){} + dummy.prototype = constructor.prototype; + var obj = new constructor; + var rv = constructor.apply(obj, args); + if (typeof rv === 'object') { + obj = rv; + } +*/ + return __emval_register(obj); +} + +// appease jshint (technically this code uses eval) +var global = (function(){return Function;})()('return this')(); + +function __emval_get_global(name) { + name = getStringOrSymbol(name); + return __emval_register(global[name]); } -function __emval_set_property(handle, k, value) { - k = Pointer_stringify(k); - _emval_handle_array[handle].value[k] = _emval_handle_array[value].value; +function __emval_get_module_property(name) { + name = getStringOrSymbol(name); + return __emval_register(Module[name]); } -function __emval_set_property_by_int(handle, k, value) { - _emval_handle_array[handle].value[k] = _emval_handle_array[value].value; +function __emval_get_property(handle, key) { + requireHandle(handle); + return __emval_register(_emval_handle_array[handle].value[_emval_handle_array[key].value]); +} + +function __emval_set_property(handle, key, value) { + requireHandle(handle); + _emval_handle_array[handle].value[_emval_handle_array[key].value] = _emval_handle_array[value].value; } function __emval_as(handle, returnType) { + requireHandle(handle); returnType = requireRegisteredType(returnType, 'emval::as'); var destructors = []; // caller owns destructing return returnType.toWireType(destructors, _emval_handle_array[handle].value); } -function __emval_call(handle, argCount, argTypes) { - var args = Array.prototype.slice.call(arguments, 3); - var fn = _emval_handle_array[handle].value; +function parseParameters(argCount, argTypes, argWireTypes) { var a = new Array(argCount); for (var i = 0; i < argCount; ++i) { var argType = requireRegisteredType( HEAP32[(argTypes >> 2) + i], "parameter " + i); - a[i] = argType.fromWireType(args[i]); + a[i] = argType.fromWireType(argWireTypes[i]); } - var rv = fn.apply(undefined, a); + return a; +} + +function __emval_call(handle, argCount, argTypes) { + requireHandle(handle); + var fn = _emval_handle_array[handle].value; + var args = parseParameters( + argCount, + argTypes, + Array.prototype.slice.call(arguments, 3)); + var rv = fn.apply(undefined, args); return __emval_register(rv); } function __emval_call_method(handle, name, argCount, argTypes) { - name = Pointer_stringify(name); - var args = Array.prototype.slice.call(arguments, 4); + requireHandle(handle); + name = getStringOrSymbol(name); + + var args = parseParameters( + argCount, + argTypes, + Array.prototype.slice.call(arguments, 4)); var obj = _emval_handle_array[handle].value; - var a = new Array(argCount); - for (var i = 0; i < argCount; ++i) { - var argType = requireRegisteredType( - HEAP32[(argTypes >> 2) + i], - "parameter " + i); - a[i] = argType.fromWireType(args[i]); - } - var rv = obj[name].apply(obj, a); + var rv = obj[name].apply(obj, args); return __emval_register(rv); } + +function __emval_call_void_method(handle, name, argCount, argTypes) { + requireHandle(handle); + name = getStringOrSymbol(name); + + var args = parseParameters( + argCount, + argTypes, + Array.prototype.slice.call(arguments, 4)); + var obj = _emval_handle_array[handle].value; + obj[name].apply(obj, args); +} + +function __emval_has_function(handle, name) { + name = getStringOrSymbol(name); + return _emval_handle_array[handle].value[name] instanceof Function; +} diff --git a/src/fastLong.js b/src/fastLong.js new file mode 100644 index 00000000..95f398db --- /dev/null +++ b/src/fastLong.js @@ -0,0 +1,298 @@ +// ======== compiled code from system/lib/compiler-rt , see readme therein +function ___muldsi3($a, $b) { + $a = $a | 0; + $b = $b | 0; + var $1 = 0, $2 = 0, $3 = 0, $6 = 0, $8 = 0, $11 = 0, $12 = 0; + $1 = $a & 65535; + $2 = $b & 65535; + $3 = Math.imul($2, $1); + $6 = $a >>> 16; + $8 = ($3 >>> 16) + Math.imul($2, $6) | 0; + $11 = $b >>> 16; + $12 = Math.imul($11, $1); + return (tempRet0 = (($8 >>> 16) + Math.imul($11, $6) | 0) + ((($8 & 65535) + $12 | 0) >>> 16) | 0, 0 | ($8 + $12 << 16 | $3 & 65535)) | 0; +} +function ___divdi3($a$0, $a$1, $b$0, $b$1) { + $a$0 = $a$0 | 0; + $a$1 = $a$1 | 0; + $b$0 = $b$0 | 0; + $b$1 = $b$1 | 0; + var $1$0 = 0, $1$1 = 0, $2$0 = 0, $2$1 = 0, $4$0 = 0, $4$1 = 0, $6$0 = 0, $7$0 = 0, $7$1 = 0, $8$0 = 0, $10$0 = 0; + $1$0 = $a$1 >> 31 | (($a$1 | 0) < 0 ? -1 : 0) << 1; + $1$1 = (($a$1 | 0) < 0 ? -1 : 0) >> 31 | (($a$1 | 0) < 0 ? -1 : 0) << 1; + $2$0 = $b$1 >> 31 | (($b$1 | 0) < 0 ? -1 : 0) << 1; + $2$1 = (($b$1 | 0) < 0 ? -1 : 0) >> 31 | (($b$1 | 0) < 0 ? -1 : 0) << 1; + $4$0 = _i64Subtract($1$0 ^ $a$0, $1$1 ^ $a$1, $1$0, $1$1); + $4$1 = tempRet0; + $6$0 = _i64Subtract($2$0 ^ $b$0, $2$1 ^ $b$1, $2$0, $2$1); + $7$0 = $2$0 ^ $1$0; + $7$1 = $2$1 ^ $1$1; + $8$0 = ___udivmoddi4($4$0, $4$1, $6$0, tempRet0, 0) | 0; + $10$0 = _i64Subtract($8$0 ^ $7$0, tempRet0 ^ $7$1, $7$0, $7$1); + return (tempRet0 = tempRet0, $10$0) | 0; +} +function ___remdi3($a$0, $a$1, $b$0, $b$1) { + $a$0 = $a$0 | 0; + $a$1 = $a$1 | 0; + $b$0 = $b$0 | 0; + $b$1 = $b$1 | 0; + var $rem = 0, $1$0 = 0, $1$1 = 0, $2$0 = 0, $2$1 = 0, $4$0 = 0, $4$1 = 0, $6$0 = 0, $10$0 = 0, $10$1 = 0, __stackBase__ = 0; + __stackBase__ = STACKTOP; + STACKTOP = STACKTOP + 8 | 0; + $rem = __stackBase__ | 0; + $1$0 = $a$1 >> 31 | (($a$1 | 0) < 0 ? -1 : 0) << 1; + $1$1 = (($a$1 | 0) < 0 ? -1 : 0) >> 31 | (($a$1 | 0) < 0 ? -1 : 0) << 1; + $2$0 = $b$1 >> 31 | (($b$1 | 0) < 0 ? -1 : 0) << 1; + $2$1 = (($b$1 | 0) < 0 ? -1 : 0) >> 31 | (($b$1 | 0) < 0 ? -1 : 0) << 1; + $4$0 = _i64Subtract($1$0 ^ $a$0, $1$1 ^ $a$1, $1$0, $1$1); + $4$1 = tempRet0; + $6$0 = _i64Subtract($2$0 ^ $b$0, $2$1 ^ $b$1, $2$0, $2$1); + ___udivmoddi4($4$0, $4$1, $6$0, tempRet0, $rem); + $10$0 = _i64Subtract(HEAP32[$rem >> 2] ^ $1$0, HEAP32[$rem + 4 >> 2] ^ $1$1, $1$0, $1$1); + $10$1 = tempRet0; + STACKTOP = __stackBase__; + return (tempRet0 = $10$1, $10$0) | 0; +} +function ___muldi3($a$0, $a$1, $b$0, $b$1) { + $a$0 = $a$0 | 0; + $a$1 = $a$1 | 0; + $b$0 = $b$0 | 0; + $b$1 = $b$1 | 0; + var $x_sroa_0_0_extract_trunc = 0, $y_sroa_0_0_extract_trunc = 0, $1$0 = 0, $1$1 = 0, $2 = 0; + $x_sroa_0_0_extract_trunc = $a$0; + $y_sroa_0_0_extract_trunc = $b$0; + $1$0 = ___muldsi3($x_sroa_0_0_extract_trunc, $y_sroa_0_0_extract_trunc) | 0; + $1$1 = tempRet0; + $2 = Math.imul($a$1, $y_sroa_0_0_extract_trunc); + return (tempRet0 = (Math.imul($b$1, $x_sroa_0_0_extract_trunc) + $2 | 0) + $1$1 | $1$1 & 0, 0 | $1$0 & -1) | 0; +} +function ___udivdi3($a$0, $a$1, $b$0, $b$1) { + $a$0 = $a$0 | 0; + $a$1 = $a$1 | 0; + $b$0 = $b$0 | 0; + $b$1 = $b$1 | 0; + var $1$0 = 0; + $1$0 = ___udivmoddi4($a$0, $a$1, $b$0, $b$1, 0) | 0; + return (tempRet0 = tempRet0, $1$0) | 0; +} +function ___uremdi3($a$0, $a$1, $b$0, $b$1) { + $a$0 = $a$0 | 0; + $a$1 = $a$1 | 0; + $b$0 = $b$0 | 0; + $b$1 = $b$1 | 0; + var $rem = 0, __stackBase__ = 0; + __stackBase__ = STACKTOP; + STACKTOP = STACKTOP + 8 | 0; + $rem = __stackBase__ | 0; + ___udivmoddi4($a$0, $a$1, $b$0, $b$1, $rem); + STACKTOP = __stackBase__; + return (tempRet0 = HEAP32[$rem + 4 >> 2] | 0, HEAP32[$rem >> 2] | 0) | 0; +} +function ___udivmoddi4($a$0, $a$1, $b$0, $b$1, $rem) { + $a$0 = $a$0 | 0; + $a$1 = $a$1 | 0; + $b$0 = $b$0 | 0; + $b$1 = $b$1 | 0; + $rem = $rem | 0; + var $n_sroa_0_0_extract_trunc = 0, $n_sroa_1_4_extract_shift$0 = 0, $n_sroa_1_4_extract_trunc = 0, $d_sroa_0_0_extract_trunc = 0, $d_sroa_1_4_extract_shift$0 = 0, $d_sroa_1_4_extract_trunc = 0, $4 = 0, $17 = 0, $37 = 0, $49 = 0, $51 = 0, $57 = 0, $58 = 0, $66 = 0, $78 = 0, $86 = 0, $88 = 0, $89 = 0, $91 = 0, $92 = 0, $95 = 0, $105 = 0, $117 = 0, $119 = 0, $125 = 0, $126 = 0, $130 = 0, $q_sroa_1_1_ph = 0, $q_sroa_0_1_ph = 0, $r_sroa_1_1_ph = 0, $r_sroa_0_1_ph = 0, $sr_1_ph = 0, $d_sroa_0_0_insert_insert99$0 = 0, $d_sroa_0_0_insert_insert99$1 = 0, $137$0 = 0, $137$1 = 0, $carry_0203 = 0, $sr_1202 = 0, $r_sroa_0_1201 = 0, $r_sroa_1_1200 = 0, $q_sroa_0_1199 = 0, $q_sroa_1_1198 = 0, $147 = 0, $149 = 0, $r_sroa_0_0_insert_insert42$0 = 0, $r_sroa_0_0_insert_insert42$1 = 0, $150$1 = 0, $151$0 = 0, $152 = 0, $154$0 = 0, $r_sroa_0_0_extract_trunc = 0, $r_sroa_1_4_extract_trunc = 0, $155 = 0, $carry_0_lcssa$0 = 0, $carry_0_lcssa$1 = 0, $r_sroa_0_1_lcssa = 0, $r_sroa_1_1_lcssa = 0, $q_sroa_0_1_lcssa = 0, $q_sroa_1_1_lcssa = 0, $q_sroa_0_0_insert_ext75$0 = 0, $q_sroa_0_0_insert_ext75$1 = 0, $q_sroa_0_0_insert_insert77$1 = 0, $_0$0 = 0, $_0$1 = 0; + $n_sroa_0_0_extract_trunc = $a$0; + $n_sroa_1_4_extract_shift$0 = $a$1; + $n_sroa_1_4_extract_trunc = $n_sroa_1_4_extract_shift$0; + $d_sroa_0_0_extract_trunc = $b$0; + $d_sroa_1_4_extract_shift$0 = $b$1; + $d_sroa_1_4_extract_trunc = $d_sroa_1_4_extract_shift$0; + if (($n_sroa_1_4_extract_trunc | 0) == 0) { + $4 = ($rem | 0) != 0; + if (($d_sroa_1_4_extract_trunc | 0) == 0) { + if ($4) { + HEAP32[$rem >> 2] = ($n_sroa_0_0_extract_trunc >>> 0) % ($d_sroa_0_0_extract_trunc >>> 0); + HEAP32[$rem + 4 >> 2] = 0; + } + $_0$1 = 0; + $_0$0 = ($n_sroa_0_0_extract_trunc >>> 0) / ($d_sroa_0_0_extract_trunc >>> 0) >>> 0; + return (tempRet0 = $_0$1, $_0$0) | 0; + } else { + if (!$4) { + $_0$1 = 0; + $_0$0 = 0; + return (tempRet0 = $_0$1, $_0$0) | 0; + } + HEAP32[$rem >> 2] = $a$0 & -1; + HEAP32[$rem + 4 >> 2] = $a$1 & 0; + $_0$1 = 0; + $_0$0 = 0; + return (tempRet0 = $_0$1, $_0$0) | 0; + } + } + $17 = ($d_sroa_1_4_extract_trunc | 0) == 0; + do { + if (($d_sroa_0_0_extract_trunc | 0) == 0) { + if ($17) { + if (($rem | 0) != 0) { + HEAP32[$rem >> 2] = ($n_sroa_1_4_extract_trunc >>> 0) % ($d_sroa_0_0_extract_trunc >>> 0); + HEAP32[$rem + 4 >> 2] = 0; + } + $_0$1 = 0; + $_0$0 = ($n_sroa_1_4_extract_trunc >>> 0) / ($d_sroa_0_0_extract_trunc >>> 0) >>> 0; + return (tempRet0 = $_0$1, $_0$0) | 0; + } + if (($n_sroa_0_0_extract_trunc | 0) == 0) { + if (($rem | 0) != 0) { + HEAP32[$rem >> 2] = 0; + HEAP32[$rem + 4 >> 2] = ($n_sroa_1_4_extract_trunc >>> 0) % ($d_sroa_1_4_extract_trunc >>> 0); + } + $_0$1 = 0; + $_0$0 = ($n_sroa_1_4_extract_trunc >>> 0) / ($d_sroa_1_4_extract_trunc >>> 0) >>> 0; + return (tempRet0 = $_0$1, $_0$0) | 0; + } + $37 = $d_sroa_1_4_extract_trunc - 1 | 0; + if (($37 & $d_sroa_1_4_extract_trunc | 0) == 0) { + if (($rem | 0) != 0) { + HEAP32[$rem >> 2] = 0 | $a$0 & -1; + HEAP32[$rem + 4 >> 2] = $37 & $n_sroa_1_4_extract_trunc | $a$1 & 0; + } + $_0$1 = 0; + $_0$0 = $n_sroa_1_4_extract_trunc >>> ((_llvm_cttz_i32($d_sroa_1_4_extract_trunc | 0) | 0) >>> 0); + return (tempRet0 = $_0$1, $_0$0) | 0; + } + $49 = _llvm_ctlz_i32($d_sroa_1_4_extract_trunc | 0) | 0; + $51 = $49 - (_llvm_ctlz_i32($n_sroa_1_4_extract_trunc | 0) | 0) | 0; + if ($51 >>> 0 <= 30) { + $57 = $51 + 1 | 0; + $58 = 31 - $51 | 0; + $sr_1_ph = $57; + $r_sroa_0_1_ph = $n_sroa_1_4_extract_trunc << $58 | $n_sroa_0_0_extract_trunc >>> ($57 >>> 0); + $r_sroa_1_1_ph = $n_sroa_1_4_extract_trunc >>> ($57 >>> 0); + $q_sroa_0_1_ph = 0; + $q_sroa_1_1_ph = $n_sroa_0_0_extract_trunc << $58; + break; + } + if (($rem | 0) == 0) { + $_0$1 = 0; + $_0$0 = 0; + return (tempRet0 = $_0$1, $_0$0) | 0; + } + HEAP32[$rem >> 2] = 0 | $a$0 & -1; + HEAP32[$rem + 4 >> 2] = $n_sroa_1_4_extract_shift$0 | $a$1 & 0; + $_0$1 = 0; + $_0$0 = 0; + return (tempRet0 = $_0$1, $_0$0) | 0; + } else { + if (!$17) { + $117 = _llvm_ctlz_i32($d_sroa_1_4_extract_trunc | 0) | 0; + $119 = $117 - (_llvm_ctlz_i32($n_sroa_1_4_extract_trunc | 0) | 0) | 0; + if ($119 >>> 0 <= 31) { + $125 = $119 + 1 | 0; + $126 = 31 - $119 | 0; + $130 = $119 - 31 >> 31; + $sr_1_ph = $125; + $r_sroa_0_1_ph = $n_sroa_0_0_extract_trunc >>> ($125 >>> 0) & $130 | $n_sroa_1_4_extract_trunc << $126; + $r_sroa_1_1_ph = $n_sroa_1_4_extract_trunc >>> ($125 >>> 0) & $130; + $q_sroa_0_1_ph = 0; + $q_sroa_1_1_ph = $n_sroa_0_0_extract_trunc << $126; + break; + } + if (($rem | 0) == 0) { + $_0$1 = 0; + $_0$0 = 0; + return (tempRet0 = $_0$1, $_0$0) | 0; + } + HEAP32[$rem >> 2] = 0 | $a$0 & -1; + HEAP32[$rem + 4 >> 2] = $n_sroa_1_4_extract_shift$0 | $a$1 & 0; + $_0$1 = 0; + $_0$0 = 0; + return (tempRet0 = $_0$1, $_0$0) | 0; + } + $66 = $d_sroa_0_0_extract_trunc - 1 | 0; + if (($66 & $d_sroa_0_0_extract_trunc | 0) != 0) { + $86 = (_llvm_ctlz_i32($d_sroa_0_0_extract_trunc | 0) | 0) + 33 | 0; + $88 = $86 - (_llvm_ctlz_i32($n_sroa_1_4_extract_trunc | 0) | 0) | 0; + $89 = 64 - $88 | 0; + $91 = 32 - $88 | 0; + $92 = $91 >> 31; + $95 = $88 - 32 | 0; + $105 = $95 >> 31; + $sr_1_ph = $88; + $r_sroa_0_1_ph = $91 - 1 >> 31 & $n_sroa_1_4_extract_trunc >>> ($95 >>> 0) | ($n_sroa_1_4_extract_trunc << $91 | $n_sroa_0_0_extract_trunc >>> ($88 >>> 0)) & $105; + $r_sroa_1_1_ph = $105 & $n_sroa_1_4_extract_trunc >>> ($88 >>> 0); + $q_sroa_0_1_ph = $n_sroa_0_0_extract_trunc << $89 & $92; + $q_sroa_1_1_ph = ($n_sroa_1_4_extract_trunc << $89 | $n_sroa_0_0_extract_trunc >>> ($95 >>> 0)) & $92 | $n_sroa_0_0_extract_trunc << $91 & $88 - 33 >> 31; + break; + } + if (($rem | 0) != 0) { + HEAP32[$rem >> 2] = $66 & $n_sroa_0_0_extract_trunc; + HEAP32[$rem + 4 >> 2] = 0; + } + if (($d_sroa_0_0_extract_trunc | 0) == 1) { + $_0$1 = $n_sroa_1_4_extract_shift$0 | $a$1 & 0; + $_0$0 = 0 | $a$0 & -1; + return (tempRet0 = $_0$1, $_0$0) | 0; + } else { + $78 = _llvm_cttz_i32($d_sroa_0_0_extract_trunc | 0) | 0; + $_0$1 = 0 | $n_sroa_1_4_extract_trunc >>> ($78 >>> 0); + $_0$0 = $n_sroa_1_4_extract_trunc << 32 - $78 | $n_sroa_0_0_extract_trunc >>> ($78 >>> 0) | 0; + return (tempRet0 = $_0$1, $_0$0) | 0; + } + } + } while (0); + if (($sr_1_ph | 0) == 0) { + $q_sroa_1_1_lcssa = $q_sroa_1_1_ph; + $q_sroa_0_1_lcssa = $q_sroa_0_1_ph; + $r_sroa_1_1_lcssa = $r_sroa_1_1_ph; + $r_sroa_0_1_lcssa = $r_sroa_0_1_ph; + $carry_0_lcssa$1 = 0; + $carry_0_lcssa$0 = 0; + } else { + $d_sroa_0_0_insert_insert99$0 = 0 | $b$0 & -1; + $d_sroa_0_0_insert_insert99$1 = $d_sroa_1_4_extract_shift$0 | $b$1 & 0; + $137$0 = _i64Add($d_sroa_0_0_insert_insert99$0, $d_sroa_0_0_insert_insert99$1, -1, -1); + $137$1 = tempRet0; + $q_sroa_1_1198 = $q_sroa_1_1_ph; + $q_sroa_0_1199 = $q_sroa_0_1_ph; + $r_sroa_1_1200 = $r_sroa_1_1_ph; + $r_sroa_0_1201 = $r_sroa_0_1_ph; + $sr_1202 = $sr_1_ph; + $carry_0203 = 0; + while (1) { + $147 = $q_sroa_0_1199 >>> 31 | $q_sroa_1_1198 << 1; + $149 = $carry_0203 | $q_sroa_0_1199 << 1; + $r_sroa_0_0_insert_insert42$0 = 0 | ($r_sroa_0_1201 << 1 | $q_sroa_1_1198 >>> 31); + $r_sroa_0_0_insert_insert42$1 = $r_sroa_0_1201 >>> 31 | $r_sroa_1_1200 << 1 | 0; + _i64Subtract($137$0, $137$1, $r_sroa_0_0_insert_insert42$0, $r_sroa_0_0_insert_insert42$1); + $150$1 = tempRet0; + $151$0 = $150$1 >> 31 | (($150$1 | 0) < 0 ? -1 : 0) << 1; + $152 = $151$0 & 1; + $154$0 = _i64Subtract($r_sroa_0_0_insert_insert42$0, $r_sroa_0_0_insert_insert42$1, $151$0 & $d_sroa_0_0_insert_insert99$0, ((($150$1 | 0) < 0 ? -1 : 0) >> 31 | (($150$1 | 0) < 0 ? -1 : 0) << 1) & $d_sroa_0_0_insert_insert99$1); + $r_sroa_0_0_extract_trunc = $154$0; + $r_sroa_1_4_extract_trunc = tempRet0; + $155 = $sr_1202 - 1 | 0; + if (($155 | 0) == 0) { + break; + } else { + $q_sroa_1_1198 = $147; + $q_sroa_0_1199 = $149; + $r_sroa_1_1200 = $r_sroa_1_4_extract_trunc; + $r_sroa_0_1201 = $r_sroa_0_0_extract_trunc; + $sr_1202 = $155; + $carry_0203 = $152; + } + } + $q_sroa_1_1_lcssa = $147; + $q_sroa_0_1_lcssa = $149; + $r_sroa_1_1_lcssa = $r_sroa_1_4_extract_trunc; + $r_sroa_0_1_lcssa = $r_sroa_0_0_extract_trunc; + $carry_0_lcssa$1 = 0; + $carry_0_lcssa$0 = $152; + } + $q_sroa_0_0_insert_ext75$0 = $q_sroa_0_1_lcssa; + $q_sroa_0_0_insert_ext75$1 = 0; + $q_sroa_0_0_insert_insert77$1 = $q_sroa_1_1_lcssa | $q_sroa_0_0_insert_ext75$1; + if (($rem | 0) != 0) { + HEAP32[$rem >> 2] = 0 | $r_sroa_0_1_lcssa; + HEAP32[$rem + 4 >> 2] = $r_sroa_1_1_lcssa | 0; + } + $_0$1 = (0 | $q_sroa_0_0_insert_ext75$0) >>> 31 | $q_sroa_0_0_insert_insert77$1 << 1 | ($q_sroa_0_0_insert_ext75$1 << 1 | $q_sroa_0_0_insert_ext75$0 >>> 31) & 0 | $carry_0_lcssa$1; + $_0$0 = ($q_sroa_0_0_insert_ext75$0 << 1 | 0 >>> 31) & -2 | $carry_0_lcssa$0; + return (tempRet0 = $_0$1, $_0$0) | 0; +} +// ======================================================================= diff --git a/src/jsifier.js b/src/jsifier.js index 262dd3eb..a01b2655 100644 --- a/src/jsifier.js +++ b/src/jsifier.js @@ -468,7 +468,7 @@ function JSify(data, functionsOnly, givenFunctions) { asmLibraryFunctions.push(contentText); contentText = ' '; EXPORTED_FUNCTIONS[ident] = 1; - delete Functions.libraryFunctions[ident.substr(1)]; + Functions.libraryFunctions[ident.substr(1)] = 2; } } if ((!ASM_JS || phase == 'pre') && @@ -701,18 +701,22 @@ function JSify(data, functionsOnly, givenFunctions) { ret += indent + 'label = ' + getLabelId(block.entries[0]) + '; ' + (SHOW_LABELS ? '/* ' + getOriginalLabelId(block.entries[0]) + ' */' : '') + '\n'; } // otherwise, should have been set before! if (func.setjmpTable) { - assert(!ASM_JS, 'asm.js mode does not support setjmp yet'); - var setjmpTable = {}; - ret += indent + 'var mySetjmpIds = {};\n'; - ret += indent + 'var setjmpTable = {'; - func.setjmpTable.forEach(function(triple) { // original label, label we created for right after the setjmp, variable setjmp result goes into - ret += '"' + getLabelId(triple[0]) + '": ' + 'function(value) { label = ' + getLabelId(triple[1]) + '; ' + triple[2] + ' = value },'; - }); - ret += 'dummy: 0'; - ret += '};\n'; + if (!ASM_JS) { + var setjmpTable = {}; + ret += indent + 'var mySetjmpIds = {};\n'; + ret += indent + 'var setjmpTable = {'; + func.setjmpTable.forEach(function(triple) { // original label, label we created for right after the setjmp, variable setjmp result goes into + ret += '"' + getLabelId(triple.oldLabel) + '": ' + 'function(value) { label = ' + getLabelId(triple.newLabel) + '; ' + triple.assignTo + ' = value },'; + }); + ret += 'dummy: 0'; + ret += '};\n'; + } else { + ret += 'var setjmpLabel = 0;\n'; + ret += 'var setjmpTable = ' + RuntimeGenerator.stackAlloc(4 * (MAX_SETJMPS + 1) * 2) + ';\n'; + } } ret += indent + 'while(1) '; - if (func.setjmpTable) { + if (func.setjmpTable && !ASM_JS) { ret += 'try { '; } ret += 'switch(' + asmCoercion('label', 'i32') + ') {\n'; @@ -720,9 +724,19 @@ function JSify(data, functionsOnly, givenFunctions) { return indent + ' case ' + getLabelId(label.ident) + ': ' + (SHOW_LABELS ? '// ' + getOriginalLabelId(label.ident) : '') + '\n' + getLabelLines(label, indent + ' '); }).join('\n') + '\n'; + if (func.setjmpTable && ASM_JS) { + // emit a label in which we write to the proper local variable, before jumping to the actual label + ret += ' case -1111: '; + ret += func.setjmpTable.map(function(triple) { // original label, label we created for right after the setjmp, variable setjmp result goes into + return 'if ((setjmpLabel|0) == ' + getLabelId(triple.oldLabel) + ') { ' + triple.assignTo + ' = threwValue; label = ' + triple.newLabel + ' }\n'; + }).join(' else '); + if (ASSERTIONS) ret += 'else abort(-3);\n'; + ret += '__THREW__ = threwValue = 0;\n'; + ret += 'break;\n'; + } if (ASSERTIONS) ret += indent + ' default: assert(0' + (ASM_JS ? '' : ', "bad label: " + label') + ');\n'; ret += indent + '}\n'; - if (func.setjmpTable) { + if (func.setjmpTable && !ASM_JS) { ret += ' } catch(e) { if (!e.longjmp || !(e.id in mySetjmpIds)) throw(e); setjmpTable[setjmpLabels[e.id]](e.value) }'; } } else { @@ -1186,7 +1200,7 @@ function JSify(data, functionsOnly, givenFunctions) { } item.reloopingJS = ret; // everything but the actual branching (which the relooper will do for us) item.toLabelJS = getPhiSetsForLabel(phiSets, item.toLabel); - item.unwindLabelJS = getPhiSetsForLabel(phiSets, item.unwindLabel); + item.unwindLabelJS = (ASM_JS ? '__THREW__ = 0;' : '') + getPhiSetsForLabel(phiSets, item.unwindLabel); ret += 'if (!__THREW__) { ' + item.toLabelJS + makeBranch(item.toLabel, item.currLabelId) + ' } else { ' + item.unwindLabelJS + makeBranch(item.unwindLabel, item.currLabelId) + ' }'; return ret; @@ -1210,6 +1224,12 @@ function JSify(data, functionsOnly, givenFunctions) { } }); makeFuncLineActor('landingpad', function(item) { + if (DISABLE_EXCEPTION_CATCHING && USE_TYPED_ARRAYS == 2) { + ret = makeVarDef(item.assignTo) + '$0 = 0; ' + item.assignTo + '$1 = 0;'; + item.assignTo = null; + if (ASSERTIONS) warnOnce('landingpad, but exceptions are disabled!'); + return ret; + } var catchTypeArray = item.catchables.map(finalizeLLVMParameter).map(function(element) { return asmCoercion(element, 'i32') }).join(','); var ret = asmCoercion('___cxa_find_matching_catch(-1, -1' + (catchTypeArray.length > 0 ? ',' + catchTypeArray : '') +')', 'i32'); if (USE_TYPED_ARRAYS == 2) { @@ -1295,6 +1315,8 @@ function JSify(data, functionsOnly, givenFunctions) { // We cannot compile assembly. See comment in intertyper.js:'Call' assert(ident != 'asm', 'Inline assembly cannot be compiled to JavaScript!'); + if (ASM_JS && funcData.setjmpTable) forceByPointer = true; // in asm.js mode, we must do an invoke for each call + ident = Variables.resolveAliasToIdent(ident); var shortident = ident.slice(1); var simpleIdent = shortident; @@ -1449,6 +1471,13 @@ function JSify(data, functionsOnly, givenFunctions) { ret += '; return 0'; // special case: abort() can happen without return, breaking the return type of asm functions. ensure a return } } + + if (ASM_JS && funcData.setjmpTable) { + // check if a longjmp was done. If a setjmp happened, check if ours. If ours, go to -111 to handle it. + // otherwise, just return - the call to us must also have been an invoke, so the setjmp propagates that way + ret += '; if (((__THREW__|0) != 0) & ((threwValue|0) > 0)) { setjmpLabel = _testSetjmp(' + makeGetValue('__THREW__', 0, 'i32') + ', setjmpTable); if ((setjmpLabel|0) > 0) { label = -1111; break } else return ' + (funcData.returnType != 'void' ? asmCoercion('0', funcData.returnType) : '') + ' } __THREW__ = threwValue = 0;\n'; + } + return ret; } makeFuncLineActor('getelementptr', function(item) { return finalizeLLVMFunctionCall(item) }); @@ -1458,11 +1487,12 @@ function JSify(data, functionsOnly, givenFunctions) { }); makeFuncLineActor('unreachable', function(item) { + var ret = ''; + if (ASM_JS && item.funcData.returnType != 'void') ret = 'return ' + asmCoercion('0', item.funcData.returnType) + ';'; if (ASSERTIONS) { - return ASM_JS ? 'abort()' : 'throw "Reached an unreachable!"'; - } else { - return ';'; + ret = (ASM_JS ? 'abort()' : 'throw "Reached an unreachable!"') + ';' + ret; } + return ret || ';'; }); // Final combiner @@ -1606,10 +1636,26 @@ function JSify(data, functionsOnly, givenFunctions) { // "Final shape that will be created"). if (PRECISE_I64_MATH && Types.preciseI64MathUsed) { if (!INCLUDE_FULL_LIBRARY) { - ['i64Add', 'bitshift64Shl', 'bitshift64Lshr', 'bitshift64Ashr'].forEach(function(func) { - print(processLibraryFunction(LibraryManager.library[func], func)); // must be first to be close to generated code - Functions.implementedFunctions['_' + func] = LibraryManager.library[func + '__sig']; + // first row are utilities called from generated code, second are needed from fastLong + ['i64Add', 'i64Subtract', 'bitshift64Shl', 'bitshift64Lshr', 'bitshift64Ashr', + 'llvm_ctlz_i32', 'llvm_cttz_i32'].forEach(function(func) { + if (!Functions.libraryFunctions[func]) { + print(processLibraryFunction(LibraryManager.library[func], func)); // must be first to be close to generated code + Functions.implementedFunctions['_' + func] = LibraryManager.library[func + '__sig']; + Functions.libraryFunctions[func] = 1; + // limited dependency handling + var deps = LibraryManager.library[func + '__deps']; + if (deps) { + deps.forEach(function(dep) { + assert(typeof dep == 'function'); + var text = dep(); + assert(text.indexOf('\n') < 0); + print('/* PRE_ASM */ ' + text + '\n'); + }); + } + } }); + print(read('fastLong.js')); } print('// EMSCRIPTEN_END_FUNCS\n'); print(read('long.js')); diff --git a/src/library.js b/src/library.js index c8205d2b..1ba4f2fa 100644 --- a/src/library.js +++ b/src/library.js @@ -573,7 +573,7 @@ LibraryManager.library = { eof: false, ungotten: [] }; - assert(Math.max(_stdin, _stdout, _stderr) < 128); // make sure these are low, we flatten arrays with these + assert(Math.max(_stdin, _stdout, _stderr) < 1024); // make sure these are low, we flatten arrays with these {{{ makeSetValue(makeGlobalUse('_stdin'), 0, 1, 'void*') }}}; {{{ makeSetValue(makeGlobalUse('_stdout'), 0, 2, 'void*') }}}; {{{ makeSetValue(makeGlobalUse('_stderr'), 0, 3, 'void*') }}}; @@ -4913,16 +4913,20 @@ LibraryManager.library = { } return 8; } - return 'var ctlz_i8 = [' + range(256).map(function(x) { return ctlz(x) }).join(',') + '];'; + return 'var ctlz_i8 = allocate([' + range(256).map(function(x) { return ctlz(x) }).join(',') + '], "i8", ALLOC_STACK);'; }], + llvm_ctlz_i32__asm: true, + llvm_ctlz_i32__sig: 'ii', llvm_ctlz_i32: function(x) { - var ret = ctlz_i8[x >>> 24]; - if (ret < 8) return ret; - var ret = ctlz_i8[(x >> 16)&0xff]; - if (ret < 8) return ret + 8; - var ret = ctlz_i8[(x >> 8)&0xff]; - if (ret < 8) return ret + 16; - return ctlz_i8[x&0xff] + 24; + x = x|0; + var ret = 0; + ret = {{{ makeGetValueAsm('ctlz_i8', 'x >>> 24', 'i8') }}}; + if ((ret|0) < 8) return ret|0; + var ret = {{{ makeGetValueAsm('ctlz_i8', '(x >> 16)&0xff', 'i8') }}}; + if ((ret|0) < 8) return (ret + 8)|0; + var ret = {{{ makeGetValueAsm('ctlz_i8', '(x >> 8)&0xff', 'i8') }}}; + if ((ret|0) < 8) return (ret + 16)|0; + return ({{{ makeGetValueAsm('ctlz_i8', 'x&0xff', 'i8') }}} + 24)|0; }, llvm_ctlz_i64__deps: ['llvm_ctlz_i32'], @@ -4945,16 +4949,20 @@ LibraryManager.library = { } return 8; } - return 'var cttz_i8 = [' + range(256).map(function(x) { return cttz(x) }).join(',') + '];'; + return 'var cttz_i8 = allocate([' + range(256).map(function(x) { return cttz(x) }).join(',') + '], "i8", ALLOC_STACK);'; }], + llvm_cttz_i32__asm: true, + llvm_cttz_i32__sig: 'ii', llvm_cttz_i32: function(x) { - var ret = cttz_i8[x & 0xff]; - if (ret < 8) return ret; - var ret = cttz_i8[(x >> 8)&0xff]; - if (ret < 8) return ret + 8; - var ret = cttz_i8[(x >> 16)&0xff]; - if (ret < 8) return ret + 16; - return cttz_i8[x >>> 24] + 24; + x = x|0; + var ret = 0; + ret = {{{ makeGetValueAsm('cttz_i8', 'x & 0xff', 'i8') }}}; + if ((ret|0) < 8) return ret|0; + var ret = {{{ makeGetValueAsm('cttz_i8', '(x >> 8)&0xff', 'i8') }}}; + if ((ret|0) < 8) return (ret + 8)|0; + var ret = {{{ makeGetValueAsm('cttz_i8', '(x >> 16)&0xff', 'i8') }}}; + if ((ret|0) < 8) return (ret + 16)|0; + return ({{{ makeGetValueAsm('cttz_i8', 'x >>> 24', 'i8') }}} + 24)|0; }, llvm_cttz_i64__deps: ['llvm_cttz_i32'], @@ -5296,9 +5304,11 @@ LibraryManager.library = { llvm_umul_with_overflow_i64__deps: [function() { Types.preciseI64MathUsed = 1 }], llvm_umul_with_overflow_i64: function(xl, xh, yl, yh) { - i64Math.multiply(xl, xh, yl, yh); - {{{ makeStructuralReturn([makeGetTempDouble(0, 'i32'), makeGetTempDouble(1, 'i32'), '0']) }}}; - // XXX Need to hack support for second param in long.js +#if ASSERTIONS + Runtime.warnOnce('no overflow support in llvm_umul_with_overflow_i64'); +#endif + var low = ___muldi3(xl, xh, yl, yh); + {{{ makeStructuralReturn(['low', 'tempRet0', '0']) }}}; }, llvm_stacksave: function() { @@ -6207,13 +6217,74 @@ LibraryManager.library = { // related functionality so the slowdown is more limited. // ========================================================================== + saveSetjmp__asm: true, + saveSetjmp__sig: 'iii', + saveSetjmp: function(env, label, table) { + // Not particularly fast: slow table lookup of setjmpId to label. But setjmp + // prevents relooping anyhow, so slowness is to be expected. And typical case + // is 1 setjmp per invocation, or less. + env = env|0; + label = label|0; + table = table|0; + var i = 0; +#if ASSERTIONS + if ((label|0) == 0) abort(121); +#endif + setjmpId = (setjmpId+1)|0; + {{{ makeSetValueAsm('env', '0', 'setjmpId', 'i32') }}}; + while ((i|0) < {{{ MAX_SETJMPS }}}) { + if ({{{ makeGetValueAsm('table', 'i*4', 'i32') }}} == 0) { + {{{ makeSetValueAsm('table', 'i*4', 'setjmpId', 'i32') }}}; + {{{ makeSetValueAsm('table', 'i*4+4', 'label', 'i32') }}}; + // prepare next slot + {{{ makeSetValueAsm('table', 'i*4+8', '0', 'i32') }}}; + return 0; + } + i = (i+2)|0; + } + abort(987); // if you hit this, adjust MAX_SETJMPS + return 0; + }, + + testSetjmp__asm: true, + testSetjmp__sig: 'iii', + testSetjmp: function(id, table) { + id = id|0; + table = table|0; + var i = 0, curr = 0; + while ((i|0) < {{{ MAX_SETJMPS }}}) { + curr = {{{ makeGetValueAsm('table', 'i*4', 'i32') }}}; + if ((curr|0) == 0) break; + if ((curr|0) == (id|0)) { + return {{{ makeGetValueAsm('table', 'i*4+4', 'i32') }}}; + } + i = (i+2)|0; + } + return 0; + }, + +#if ASM_JS + setjmp__deps: ['saveSetjmp', 'testSetjmp'], +#endif setjmp__inline: function(env) { // Save the label +#if ASM_JS + return '_saveSetjmp(' + env + ', label, setjmpTable)'; +#else return '(tempInt = setjmpId++, mySetjmpIds[tempInt] = 1, setjmpLabels[tempInt] = label,' + makeSetValue(env, '0', 'tempInt', 'i32', undefined, undefined, undefined, undefined, ',') + ', 0)'; +#endif }, +#if ASM_JS + longjmp__deps: ['saveSetjmp', 'testSetjmp'], +#endif longjmp: function(env, value) { +#if ASM_JS + asm.setThrew(env, value || 1); + throw 'longjmp'; +#else throw { longjmp: true, id: {{{ makeGetValue('env', '0', 'i32') }}}, value: value || 1 }; +#endif }, // ========================================================================== @@ -7492,13 +7563,27 @@ LibraryManager.library = { var l = 0, h = 0, overflow = 0; l = (a + c)>>>0; h = (b + d)>>>0; - if ((l>>>0) < (a>>>0)) { // iff we overflowed + if ((h>>>0) < (b>>>0)) overflow = 1; + if ((l>>>0) < (a>>>0)) { h = (h+1)>>>0; - overflow = 1; + if ((h>>>0) == 0) overflow = 1; // two possibilities to overflow here } {{{ makeStructuralReturn(['l|0', 'h', 'overflow'], true) }}}; }, + i64Subtract__asm: true, + i64Subtract__sig: 'iiiii', + i64Subtract: function(a, b, c, d) { + a = a|0; b = b|0; c = c|0; d = d|0; + var l = 0, h = 0; + l = (a - c)>>>0; + h = (b - d)>>>0; + if ((l>>>0) > (a>>>0)) { // iff we overflowed + h = (h-1)>>>0; + } + {{{ makeStructuralReturn(['l|0', 'h'], true) }}}; + }, + bitshift64Shl__asm: true, bitshift64Shl__sig: 'iiii', bitshift64Shl: function(low, high, bits) { diff --git a/src/library_browser.js b/src/library_browser.js index c1add740..85eb93f7 100644 --- a/src/library_browser.js +++ b/src/library_browser.js @@ -704,7 +704,11 @@ mergeInto(LibraryManager.library, { }, emscripten_get_now: function() { - if (window['performance'] && window['performance']['now']) { + if (ENVIRONMENT_IS_NODE) { + var t = process['hrtime'](); + return t[0] * 1e3 + t[1] / 1e6; + } + else if (window['performance'] && window['performance']['now']) { return window['performance']['now'](); } else { return Date.now(); diff --git a/src/library_gl.js b/src/library_gl.js index 0912b5da..b91d782c 100644 --- a/src/library_gl.js +++ b/src/library_gl.js @@ -2282,14 +2282,14 @@ var LibraryGL = { } attributes.sort(function(x, y) { return !x ? (!y ? 0 : 1) : (!y ? -1 : (x.pointer - y.pointer)) }); start = GL.currArrayBuffer ? 0 : attributes[0].pointer; + var multiStrides = false; for (var i = 0; i < attributes.length; i++) { var attribute = attributes[i]; if (!attribute) break; -#if ASSERTIONS - assert(stride == 0 || stride == attribute.stride); // must all be in the same buffer -#endif + if (stride != 0 && stride != attribute.stride) multiStrides = true; if (attribute.stride) stride = attribute.stride; } + if (multiStrides) stride = 0; // we will need to restride var bytes = 0; // total size in bytes if (!stride && !beginEnd) { // beginEnd can not have stride in the attributes, that is fine. otherwise, @@ -2297,7 +2297,7 @@ var LibraryGL = { // our emulation code simple, we perform unpacking/restriding here. this adds overhead, so // it is a good idea to not hit this! #if ASSERTIONS - Runtime.warnOnce('Unpacking/restriding attributes, this is not fast'); + Runtime.warnOnce('Unpacking/restriding attributes, this is slow and dangerous'); #endif if (!GL.immediate.restrideBuffer) GL.immediate.restrideBuffer = _malloc(GL.MAX_TEMP_BUFFER_SIZE); start = GL.immediate.restrideBuffer; diff --git a/src/long.js b/src/long.js index 6d0e873d..42c86c23 100644 --- a/src/long.js +++ b/src/long.js @@ -1530,20 +1530,6 @@ var i64Math = (function() { // Emscripten wrapper // Emscripten wrapper var Wrapper = { - subtract: function(xl, xh, yl, yh) { - var x = new goog.math.Long(xl, xh); - var y = new goog.math.Long(yl, yh); - var ret = x.subtract(y); - HEAP32[tempDoublePtr>>2] = ret.low_; - HEAP32[tempDoublePtr+4>>2] = ret.high_; - }, - multiply: function(xl, xh, yl, yh) { - var x = new goog.math.Long(xl, xh); - var y = new goog.math.Long(yl, yh); - var ret = x.multiply(y); - HEAP32[tempDoublePtr>>2] = ret.low_; - HEAP32[tempDoublePtr+4>>2] = ret.high_; - }, abs: function(l, h) { var x = new goog.math.Long(l, h); var ret; @@ -1576,48 +1562,6 @@ var i64Math = (function() { // Emscripten wrapper c.addTo(b, d); return d; }, - divide: function(xl, xh, yl, yh, unsigned) { - Wrapper.ensureTemps(); - if (!unsigned) { - var x = new goog.math.Long(xl, xh); - var y = new goog.math.Long(yl, yh); - var ret = x.div(y); - HEAP32[tempDoublePtr>>2] = ret.low_; - HEAP32[tempDoublePtr+4>>2] = ret.high_; - } else { - // slow precise bignum division - var x = Wrapper.lh2bignum(xl >>> 0, xh >>> 0); - var y = Wrapper.lh2bignum(yl >>> 0, yh >>> 0); - var z = new BigInteger(); - x.divRemTo(y, z, null); - var l = new BigInteger(); - var h = new BigInteger(); - z.divRemTo(Wrapper.two32, h, l); - HEAP32[tempDoublePtr>>2] = parseInt(l.toString()) | 0; - HEAP32[tempDoublePtr+4>>2] = parseInt(h.toString()) | 0; - } - }, - modulo: function(xl, xh, yl, yh, unsigned) { - Wrapper.ensureTemps(); - if (!unsigned) { - var x = new goog.math.Long(xl, xh); - var y = new goog.math.Long(yl, yh); - var ret = x.modulo(y); - HEAP32[tempDoublePtr>>2] = ret.low_; - HEAP32[tempDoublePtr+4>>2] = ret.high_; - } else { - // slow precise bignum division - var x = Wrapper.lh2bignum(xl >>> 0, xh >>> 0); - var y = Wrapper.lh2bignum(yl >>> 0, yh >>> 0); - var z = new BigInteger(); - x.divRemTo(y, null, z); - var l = new BigInteger(); - var h = new BigInteger(); - z.divRemTo(Wrapper.two32, h, l); - HEAP32[tempDoublePtr>>2] = parseInt(l.toString()) | 0; - HEAP32[tempDoublePtr+4>>2] = parseInt(h.toString()) | 0; - } - }, stringify: function(l, h, unsigned) { var ret = new goog.math.Long(l, h).toString(); if (unsigned && ret[0] == '-') { diff --git a/src/modules.js b/src/modules.js index f2994ada..5b5f7947 100644 --- a/src/modules.js +++ b/src/modules.js @@ -230,7 +230,7 @@ var Types = { var Functions = { // All functions that will be implemented in this file. Maps id to signature implementedFunctions: {}, - libraryFunctions: {}, // functions added from the library + libraryFunctions: {}, // functions added from the library. value 2 means asmLibraryFunction unimplementedFunctions: {}, // library etc. functions that we need to index, maps id to signature indexedFunctions: {}, diff --git a/src/parseTools.js b/src/parseTools.js index 7dafbebe..2eb456f1 100644 --- a/src/parseTools.js +++ b/src/parseTools.js @@ -1756,7 +1756,7 @@ function makeStructuralReturn(values, inAsm) { return 'return ' + asmCoercion(values.slice(1).map(function(value) { i++; return ASM_JS ? (inAsm ? 'tempRet' + i + ' = ' + value : 'asm.setTempRet' + i + '(' + value + ')') - : 'tempRet' + (i++) + ' = ' + value; + : 'tempRet' + i + ' = ' + value; }).concat([values[0]]).join(','), 'i32'); } else { var i = 0; @@ -1998,9 +1998,12 @@ function processMathop(item) { return finish(['(i64Math' + (ASM_JS ? '_' : '.') + type + '(' + asmCoercion(low1, 'i32') + ',' + asmCoercion(high1, 'i32') + ',' + asmCoercion(low2, 'i32') + ',' + asmCoercion(high2, 'i32') + (lastArg ? ',' + asmCoercion(+lastArg, 'i32') : '') + '),' + makeGetValue('tempDoublePtr', 0, 'i32') + ')', makeGetValue('tempDoublePtr', Runtime.getNativeTypeSize('i32'), 'i32')]); } - function i64PreciseLib(type) { + function preciseCall(name) { Types.preciseI64MathUsed = true; - return finish(['_i64' + type[0].toUpperCase() + type.substr(1) + '(' + low1 + ',' + high1 + ',' + low2 + ',' + high2 + ')', 'tempRet0']); + return finish([name + '(' + low1 + ',' + high1 + ',' + low2 + ',' + high2 + ')', 'tempRet0']); + } + function i64PreciseLib(type) { + return preciseCall('_i64' + type[0].toUpperCase() + type.substr(1)); } switch (op) { // basic integer ops @@ -2062,7 +2065,7 @@ function processMathop(item) { } case 'sub': { if (PRECISE_I64_MATH) { - return i64PreciseOp('subtract'); + return i64PreciseLib('subtract'); } else { warnI64_1(); return finish(splitI64(mergeI64(idents[0]) + '-' + mergeI64(idents[1]), true)); @@ -2070,7 +2073,7 @@ function processMathop(item) { } case 'sdiv': case 'udiv': { if (PRECISE_I64_MATH) { - return i64PreciseOp('divide', op[0] === 'u'); + return preciseCall(op[0] === 'u' ? '___udivdi3' : '___divdi3'); } else { warnI64_1(); return finish(splitI64(makeRounding(mergeI64(idents[0], op[0] === 'u') + '/' + mergeI64(idents[1], op[0] === 'u'), bits, op[0] === 's'), true)); @@ -2078,7 +2081,7 @@ function processMathop(item) { } case 'mul': { if (PRECISE_I64_MATH) { - return i64PreciseOp('multiply'); + return preciseCall('___muldi3'); } else { warnI64_1(); return finish(splitI64(mergeI64(idents[0], op[0] === 'u') + '*' + mergeI64(idents[1], op[0] === 'u'), true)); @@ -2086,7 +2089,7 @@ function processMathop(item) { } case 'urem': case 'srem': { if (PRECISE_I64_MATH) { - return i64PreciseOp('modulo', op[0] === 'u'); + return preciseCall(op[0] === 'u' ? '___uremdi3' : '___remdi3'); } else { warnI64_1(); return finish(splitI64(mergeI64(idents[0], op[0] === 'u') + '%' + mergeI64(idents[1], op[0] === 'u'), true)); diff --git a/src/preamble.js b/src/preamble.js index 592363f9..17b74cd9 100644 --- a/src/preamble.js +++ b/src/preamble.js @@ -217,8 +217,10 @@ var START_TIME = Date.now(); //======================================== var __THREW__ = 0; // Used in checking for thrown exceptions. +#if ASM_JS == 0 var setjmpId = 1; // Used in setjmp/longjmp var setjmpLabels = {}; +#endif var ABORT = false; diff --git a/src/settings.js b/src/settings.js index 8f31c2d8..db24cca4 100644 --- a/src/settings.js +++ b/src/settings.js @@ -55,6 +55,7 @@ var ALLOW_MEMORY_GROWTH = 0; // If false, we abort with an error if we try to al // that case we must be careful about optimizations, in particular the // eliminator). Note that memory growth is only supported with typed // arrays. +var MAX_SETJMPS = 10; // size of setjmp table allocated in each function invocation (that has setjmp) // Code embetterments var MICRO_OPTS = 1; // Various micro-optimizations, like nativizing variables diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index 55fda986..4d2f4ac8 100644..100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -3,11 +3,20 @@ #include <stddef.h> #include <assert.h> #include <string> +#include <functional> +#include <vector> +#include <map> #include <type_traits> #include <emscripten/val.h> #include <emscripten/wire.h> namespace emscripten { + enum class sharing_policy { + NONE = 0, + INTRUSIVE = 1, + BY_EMVAL = 2, + }; + namespace internal { typedef void (*GenericFunction)(); typedef long GenericEnumValue; @@ -19,150 +28,224 @@ namespace emscripten { const char* payload) __attribute__((noreturn)); void _embind_register_void( - TypeID voidType, + TYPEID voidType, const char* name); void _embind_register_bool( - TypeID boolType, + TYPEID boolType, const char* name, bool trueValue, bool falseValue); void _embind_register_integer( - TypeID integerType, - const char* name); + TYPEID integerType, + const char* name, + long minRange, + unsigned long maxRange); void _embind_register_float( - TypeID floatType, + TYPEID floatType, const char* name); - void _embind_register_cstring( - TypeID stringType, + void _embind_register_std_string( + TYPEID stringType, + const char* name); + + void _embind_register_std_wstring( + TYPEID stringType, + size_t charSize, const char* name); void _embind_register_emval( - TypeID emvalType, + TYPEID emvalType, const char* name); void _embind_register_function( const char* name, - TypeID returnType, unsigned argCount, - TypeID argTypes[], + TYPEID argTypes[], GenericFunction invoker, GenericFunction function); void _embind_register_tuple( - TypeID tupleType, + TYPEID tupleType, const char* name, GenericFunction constructor, GenericFunction destructor); void _embind_register_tuple_element( - TypeID tupleType, - TypeID elementType, + TYPEID tupleType, + TYPEID getterReturnType, GenericFunction getter, + void* getterContext, + TYPEID setterArgumentType, GenericFunction setter, - size_t memberPointerSize, - void* memberPointer); - - void _embind_register_tuple_element_accessor( - TypeID tupleType, - TypeID elementType, - GenericFunction staticGetter, - size_t getterSize, - void* getter, - GenericFunction staticSetter, - size_t setterSize, - void* setter); + void* setterContext); + + void _embind_finalize_tuple(TYPEID tupleType); void _embind_register_struct( - TypeID structType, - const char* name, + TYPEID structType, + const char* fieldName, GenericFunction constructor, GenericFunction destructor); void _embind_register_struct_field( - TypeID structType, - const char* name, - TypeID fieldType, + TYPEID structType, + const char* fieldName, + TYPEID getterReturnType, GenericFunction getter, + void* getterContext, + TYPEID setterArgumentType, GenericFunction setter, - size_t memberPointerSize, - void* memberPointer); + void* setterContext); + + void _embind_finalize_struct(TYPEID structType); + + void _embind_register_smart_ptr( + TYPEID pointerType, + TYPEID pointeeType, + const char* pointerName, + sharing_policy sharingPolicy, + GenericFunction getPointee, + GenericFunction constructor, + GenericFunction share, + GenericFunction destructor); void _embind_register_class( - TypeID classType, + TYPEID classType, + TYPEID pointerType, + TYPEID constPointerType, + TYPEID baseClassType, + GenericFunction getActualType, + GenericFunction upcast, + GenericFunction downcast, const char* className, GenericFunction destructor); void _embind_register_class_constructor( - TypeID classType, + TYPEID classType, unsigned argCount, - TypeID argTypes[], + TYPEID argTypes[], + GenericFunction invoker, GenericFunction constructor); - void _embind_register_class_method( - TypeID classType, + void _embind_register_class_function( + TYPEID classType, const char* methodName, - TypeID returnType, unsigned argCount, - TypeID argTypes[], + TYPEID argTypes[], GenericFunction invoker, - size_t memberFunctionSize, - void* memberFunction); + void* context); - void _embind_register_class_field( - TypeID classType, + void _embind_register_class_property( + TYPEID classType, const char* fieldName, - TypeID fieldType, + TYPEID getterReturnType, GenericFunction getter, + void* getterContext, + TYPEID setterArgumentType, GenericFunction setter, - size_t memberPointerSize, - void* memberPointer); + void* setterContext); - void _embind_register_class_classmethod( - TypeID classType, + void _embind_register_class_class_function( + TYPEID classType, const char* methodName, - TypeID returnType, unsigned argCount, - TypeID argTypes[], + TYPEID argTypes[], + GenericFunction invoker, GenericFunction method); void _embind_register_enum( - TypeID enumType, + TYPEID enumType, const char* name); void _embind_register_enum_value( - TypeID enumType, + TYPEID enumType, const char* valueName, GenericEnumValue value); void _embind_register_interface( - TypeID interfaceType, + TYPEID interfaceType, const char* name, GenericFunction constructor, GenericFunction destructor); + + void _embind_register_constant( + const char* name, + TYPEID constantType, + uintptr_t value); } + } +} - extern void registerStandardTypes(); +namespace emscripten { + //////////////////////////////////////////////////////////////////////////////// + // POLICIES + //////////////////////////////////////////////////////////////////////////////// - class BindingsDefinition { - public: - template<typename Function> - BindingsDefinition(Function fn) { - fn(); - } + template<int Index> + struct arg { + static constexpr int index = Index + 1; + }; + + struct ret_val { + static constexpr int index = 0; + }; + + /* + template<typename Slot> + struct allow_raw_pointer { + template<typename InputType, int Index> + struct Transform { + typedef typename std::conditional< + Index == Slot::index, + internal::AllowedRawPointer<typename std::remove_pointer<InputType>::type>, + InputType + >::type type; + }; + }; + */ + + // whitelist all raw pointers + struct allow_raw_pointers { + template<typename InputType, int Index> + struct Transform { + typedef typename std::conditional< + std::is_pointer<InputType>::value, + internal::AllowedRawPointer<typename std::remove_pointer<InputType>::type>, + InputType + >::type type; }; + }; + + // this is temporary until arg policies are reworked + template<typename Slot> + struct allow_raw_pointer : public allow_raw_pointers { + }; + + template<typename Signature> + typename std::add_pointer<Signature>::type select_overload(typename std::add_pointer<Signature>::type fn) { + return fn; + } + + namespace internal { + template<typename ClassType, typename Signature> + struct MemberFunctionType { + typedef Signature (ClassType::*type); + }; + } + + template<typename Signature, typename ClassType> + typename internal::MemberFunctionType<ClassType, Signature>::type select_overload(Signature (ClassType::*fn)) { + return fn; } -} -namespace emscripten { namespace internal { template<typename ReturnType, typename... Args> struct Invoker { static typename internal::BindingType<ReturnType>::WireType invoke( - ReturnType (fn)(Args...), + ReturnType (*fn)(Args...), typename internal::BindingType<Args>::WireType... args ) { return internal::BindingType<ReturnType>::toWireType( @@ -176,7 +259,7 @@ namespace emscripten { template<typename... Args> struct Invoker<void, Args...> { static void invoke( - void (fn)(Args...), + void (*fn)(Args...), typename internal::BindingType<Args>::WireType... args ) { return fn( @@ -186,22 +269,38 @@ namespace emscripten { }; } - template<typename ReturnType, typename... Args> - void function(const char* name, ReturnType (fn)(Args...)) { - internal::registerStandardTypes(); + //////////////////////////////////////////////////////////////////////////////// + // FUNCTIONS + //////////////////////////////////////////////////////////////////////////////// + + extern "C" { + void* __getDynamicPointerType(void* p); + } - internal::ArgTypeList<Args...> args; - internal::_embind_register_function( + template<typename ReturnType, typename... Args, typename... Policies> + void function(const char* name, ReturnType (*fn)(Args...), Policies...) { + using namespace internal; + typename WithPolicies<Policies...>::template ArgTypeList<ReturnType, Args...> args; + _embind_register_function( name, - internal::getTypeID<ReturnType>(), args.count, args.types, - reinterpret_cast<internal::GenericFunction>(&internal::Invoker<ReturnType, Args...>::invoke), - reinterpret_cast<internal::GenericFunction>(fn)); + reinterpret_cast<GenericFunction>(&Invoker<ReturnType, Args...>::invoke), + reinterpret_cast<GenericFunction>(fn)); } namespace internal { template<typename ClassType, typename... Args> + ClassType* operator_new(Args... args) { + return new ClassType(args...); + } + + template<typename WrapperType, typename ClassType, typename... Args> + WrapperType wrapped_new(Args&&... args) { + return WrapperType(new ClassType(std::forward<Args>(args)...)); + } + + template<typename ClassType, typename... Args> ClassType* raw_constructor( typename internal::BindingType<Args>::WireType... args ) { @@ -215,302 +314,871 @@ namespace emscripten { delete ptr; } - template<typename ClassType, typename ReturnType, typename... Args> - struct MethodInvoker { - typedef ReturnType (ClassType::*MemberPointer)(Args...); + template<typename FunctionPointerType, typename ReturnType, typename ThisType, typename... Args> + struct FunctionInvoker { static typename internal::BindingType<ReturnType>::WireType invoke( - ClassType* ptr, - const MemberPointer& method, + FunctionPointerType* function, + typename internal::BindingType<ThisType>::WireType wireThis, typename internal::BindingType<Args>::WireType... args ) { return internal::BindingType<ReturnType>::toWireType( - (ptr->*method)( - internal::BindingType<Args>::fromWireType(args)... - ) + (*function)( + internal::BindingType<ThisType>::fromWireType(wireThis), + internal::BindingType<Args>::fromWireType(args)...) ); } }; - template<typename ClassType, typename... Args> - struct MethodInvoker<ClassType, void, Args...> { - typedef void (ClassType::*MemberPointer)(Args...); + template<typename FunctionPointerType, typename ThisType, typename... Args> + struct FunctionInvoker<FunctionPointerType, void, ThisType, Args...> { static void invoke( - ClassType* ptr, - const MemberPointer& method, + FunctionPointerType* function, + typename internal::BindingType<ThisType>::WireType wireThis, typename internal::BindingType<Args>::WireType... args ) { - return (ptr->*method)( - internal::BindingType<Args>::fromWireType(args)... - ); + (*function)( + internal::BindingType<ThisType>::fromWireType(wireThis), + internal::BindingType<Args>::fromWireType(args)...); } }; - template<typename ClassType, typename ReturnType, typename... Args> - struct ConstMethodInvoker { - typedef ReturnType (ClassType::*MemberPointer)(Args...) const; + template<typename MemberPointer, + typename ReturnType, + typename ThisType, + typename... Args> + struct MethodInvoker { static typename internal::BindingType<ReturnType>::WireType invoke( - const ClassType* ptr, const MemberPointer& method, + typename internal::BindingType<ThisType>::WireType wireThis, typename internal::BindingType<Args>::WireType... args ) { return internal::BindingType<ReturnType>::toWireType( - (ptr->*method)( + (internal::BindingType<ThisType>::fromWireType(wireThis)->*method)( internal::BindingType<Args>::fromWireType(args)... ) ); } }; - template<typename ClassType, typename... Args> - struct ConstMethodInvoker<ClassType, void, Args...> { - typedef void (ClassType::*MemberPointer)(Args...) const; + template<typename MemberPointer, + typename ThisType, + typename... Args> + struct MethodInvoker<MemberPointer, void, ThisType, Args...> { static void invoke( - const ClassType* ptr, const MemberPointer& method, + typename internal::BindingType<ThisType>::WireType wireThis, typename internal::BindingType<Args>::WireType... args ) { - return (ptr->*method)( + return (internal::BindingType<ThisType>::fromWireType(wireThis)->*method)( internal::BindingType<Args>::fromWireType(args)... ); } }; - template<typename ClassType, typename FieldType> - struct FieldAccess { - typedef FieldType ClassType::*MemberPointer; - typedef internal::BindingType<FieldType> FieldBinding; - typedef typename FieldBinding::WireType WireType; + template<typename InstanceType, typename MemberType> + struct MemberAccess { + typedef MemberType InstanceType::*MemberPointer; + typedef internal::BindingType<MemberType> MemberBinding; + typedef typename MemberBinding::WireType WireType; - static WireType get( - ClassType& ptr, - const MemberPointer& field + template<typename ClassType> + static WireType getWire( + const MemberPointer& field, + const ClassType& ptr ) { - return FieldBinding::toWireType(ptr.*field); + return MemberBinding::toWireType(ptr.*field); } - static void set( - ClassType& ptr, + template<typename ClassType> + static void setWire( const MemberPointer& field, + ClassType& ptr, WireType value ) { - ptr.*field = FieldBinding::fromWireType(value); + ptr.*field = MemberBinding::fromWireType(value); } + }; - template<typename Getter> - static WireType propertyGet( - ClassType& ptr, - const Getter& getter - ) { - return FieldBinding::toWireType(getter(ptr)); + // TODO: This could do a reinterpret-cast if sizeof(T) === sizeof(void*) + template<typename T> + inline void* getContext(const T& t) { + // not a leak because this is called once per binding + void* p = malloc(sizeof(T)); + assert(p); + memcpy(p, &t, sizeof(T)); + return p; + } + + template<typename T> + struct GetterPolicy; + + template<typename GetterReturnType, typename GetterThisType> + struct GetterPolicy<GetterReturnType (GetterThisType::*)() const> { + typedef GetterReturnType ReturnType; + typedef GetterReturnType (GetterThisType::*Context)() const; + + typedef internal::BindingType<ReturnType> Binding; + typedef typename Binding::WireType WireType; + + template<typename ClassType> + static WireType get(const Context& context, const ClassType& ptr) { + return Binding::toWireType((ptr.*context)()); } - template<typename Setter> - static void propertySet( - ClassType& ptr, - const Setter& setter, - WireType value - ) { - setter(ptr, FieldBinding::fromWireType(value)); + static void* getContext(Context context) { + return internal::getContext(context); + } + }; + + template<typename GetterReturnType, typename GetterThisType> + struct GetterPolicy<GetterReturnType (*)(const GetterThisType&)> { + typedef GetterReturnType ReturnType; + typedef GetterReturnType (*Context)(const GetterThisType&); + + typedef internal::BindingType<ReturnType> Binding; + typedef typename Binding::WireType WireType; + + template<typename ClassType> + static WireType get(const Context& context, const ClassType& ptr) { + return Binding::toWireType(context(ptr)); + } + + static void* getContext(Context context) { + return internal::getContext(context); + } + }; + + template<typename T> + struct SetterPolicy; + + template<typename SetterThisType, typename SetterArgumentType> + struct SetterPolicy<void (SetterThisType::*)(SetterArgumentType)> { + typedef SetterArgumentType ArgumentType; + typedef void (SetterThisType::*Context)(SetterArgumentType); + + typedef internal::BindingType<SetterArgumentType> Binding; + typedef typename Binding::WireType WireType; + + template<typename ClassType> + static void set(const Context& context, ClassType& ptr, WireType wt) { + (ptr.*context)(Binding::fromWireType(wt)); + } + + static void* getContext(Context context) { + return internal::getContext(context); + } + }; + + template<typename SetterThisType, typename SetterArgumentType> + struct SetterPolicy<void (*)(SetterThisType&, SetterArgumentType)> { + typedef SetterArgumentType ArgumentType; + typedef void (*Context)(SetterThisType&, SetterArgumentType); + + typedef internal::BindingType<SetterArgumentType> Binding; + typedef typename Binding::WireType WireType; + + template<typename ClassType> + static void set(const Context& context, ClassType& ptr, WireType wt) { + context(ptr, Binding::fromWireType(wt)); + } + + static void* getContext(Context context) { + return internal::getContext(context); } }; + + class noncopyable { + protected: + noncopyable() {} + ~noncopyable() {} + private: + noncopyable(const noncopyable&) = delete; + const noncopyable& operator=(const noncopyable&) = delete; + }; + + template<typename ClassType, typename ElementType> + typename BindingType<ElementType>::WireType get_by_index(int index, ClassType& ptr) { + return BindingType<ElementType>::toWireType(ptr[index]); + } + + template<typename ClassType, typename ElementType> + void set_by_index(int index, ClassType& ptr, typename BindingType<ElementType>::WireType wt) { + ptr[index] = BindingType<ElementType>::fromWireType(wt); + } } + template<int Index> + struct index { + }; + + //////////////////////////////////////////////////////////////////////////////// + // VALUE TUPLES + //////////////////////////////////////////////////////////////////////////////// + template<typename ClassType> - class value_tuple { + class value_tuple : public internal::noncopyable { public: value_tuple(const char* name) { - internal::registerStandardTypes(); - internal::_embind_register_tuple( - internal::getTypeID<ClassType>(), + using namespace internal; + _embind_register_tuple( + TypeID<ClassType>::get(), name, - reinterpret_cast<internal::GenericFunction>(&internal::raw_constructor<ClassType>), - reinterpret_cast<internal::GenericFunction>(&internal::raw_destructor<ClassType>)); - } - - template<typename ElementType> - value_tuple& element(ElementType ClassType::*field) { - internal::_embind_register_tuple_element( - internal::getTypeID<ClassType>(), - internal::getTypeID<ElementType>(), - reinterpret_cast<internal::GenericFunction>(&internal::FieldAccess<ClassType, ElementType>::get), - reinterpret_cast<internal::GenericFunction>(&internal::FieldAccess<ClassType, ElementType>::set), - sizeof(field), - &field); - - return *this; + reinterpret_cast<GenericFunction>(&raw_constructor<ClassType>), + reinterpret_cast<GenericFunction>(&raw_destructor<ClassType>)); } - template<typename ElementType> - value_tuple& element(ElementType (*getter)(const ClassType&), void (*setter)(ClassType&, ElementType)) { - internal::_embind_register_tuple_element_accessor( - internal::getTypeID<ClassType>(), - internal::getTypeID<ElementType>(), - reinterpret_cast<internal::GenericFunction>(&internal::FieldAccess<ClassType, ElementType>::template propertyGet<ElementType(const ClassType&)>), - sizeof(getter), - &getter, - reinterpret_cast<internal::GenericFunction>(&internal::FieldAccess<ClassType, ElementType>::template propertySet<void(ClassType&, ElementType)>), - sizeof(setter), - &setter); - return *this; + ~value_tuple() { + using namespace internal; + _embind_finalize_tuple(TypeID<ClassType>::get()); } - template<typename ElementType> - value_tuple& element(ElementType (*getter)(const ClassType&), void (*setter)(ClassType&, const ElementType&)) { - internal::_embind_register_tuple_element_accessor( - internal::getTypeID<ClassType>(), - internal::getTypeID<ElementType>(), - reinterpret_cast<internal::GenericFunction>(&internal::FieldAccess<ClassType, ElementType>::template propertyGet<ElementType(const ClassType&)>), - sizeof(getter), - &getter, - reinterpret_cast<internal::GenericFunction>(&internal::FieldAccess<ClassType, ElementType>::template propertySet<void(ClassType&, ElementType)>), - sizeof(setter), - &setter); + template<typename InstanceType, typename ElementType> + value_tuple& element(ElementType InstanceType::*field) { + using namespace internal; + _embind_register_tuple_element( + TypeID<ClassType>::get(), + TypeID<ElementType>::get(), + reinterpret_cast<GenericFunction>( + &MemberAccess<InstanceType, ElementType> + ::template getWire<ClassType>), + getContext(field), + TypeID<ElementType>::get(), + reinterpret_cast<GenericFunction>( + &MemberAccess<InstanceType, ElementType> + ::template setWire<ClassType>), + getContext(field)); return *this; } - template<typename ElementType> - value_tuple& element(ElementType (*getter)(const ClassType&), void (*setter)(ClassType&, const ElementType&&)) { - internal::_embind_register_tuple_element_accessor( - internal::getTypeID<ClassType>(), - internal::getTypeID<ElementType>(), - reinterpret_cast<internal::GenericFunction>(&internal::FieldAccess<ClassType, ElementType>::template propertyGet<ElementType(const ClassType&)>), - sizeof(getter), - &getter, - reinterpret_cast<internal::GenericFunction>(&internal::FieldAccess<ClassType, ElementType>::template propertySet<void(ClassType&, ElementType)>), - sizeof(setter), - &setter); + template<typename Getter, typename Setter> + value_tuple& element(Getter getter, Setter setter) { + using namespace internal; + typedef GetterPolicy<Getter> GP; + typedef SetterPolicy<Setter> SP; + _embind_register_tuple_element( + TypeID<ClassType>::get(), + TypeID<typename GP::ReturnType>::get(), + reinterpret_cast<GenericFunction>(&GP::template get<ClassType>), + GP::getContext(getter), + TypeID<typename SP::ArgumentType>::get(), + reinterpret_cast<GenericFunction>(&SP::template set<ClassType>), + SP::getContext(setter)); return *this; } - template<typename ElementType> - value_tuple& element(ElementType (*getter)(const ClassType&), void (*setter)(ClassType&, ElementType&)) { - internal::_embind_register_tuple_element_accessor( - internal::getTypeID<ClassType>(), - internal::getTypeID<ElementType>(), - reinterpret_cast<internal::GenericFunction>(&internal::FieldAccess<ClassType, ElementType>::template propertyGet<ElementType(const ClassType&)>), - sizeof(getter), - &getter, - reinterpret_cast<internal::GenericFunction>(&internal::FieldAccess<ClassType, ElementType>::template propertySet<void(ClassType&, ElementType)>), - sizeof(setter), - &setter); + template<int Index> + value_tuple& element(index<Index>) { + using namespace internal; + ClassType* null = 0; + typedef typename std::remove_reference<decltype((*null)[Index])>::type ElementType; + _embind_register_tuple_element( + TypeID<ClassType>::get(), + TypeID<ElementType>::get(), + reinterpret_cast<GenericFunction>(&internal::get_by_index<ClassType, ElementType>), + reinterpret_cast<void*>(Index), + TypeID<ElementType>::get(), + reinterpret_cast<GenericFunction>(&internal::set_by_index<ClassType, ElementType>), + reinterpret_cast<void*>(Index)); return *this; } }; + //////////////////////////////////////////////////////////////////////////////// + // VALUE STRUCTS + //////////////////////////////////////////////////////////////////////////////// + template<typename ClassType> - class value_struct { + class value_struct : public internal::noncopyable { public: value_struct(const char* name) { - internal::registerStandardTypes(); - internal::_embind_register_struct( - internal::getTypeID<ClassType>(), + using namespace internal; + _embind_register_struct( + TypeID<ClassType>::get(), name, - reinterpret_cast<internal::GenericFunction>(&internal::raw_constructor<ClassType>), - reinterpret_cast<internal::GenericFunction>(&internal::raw_destructor<ClassType>)); + reinterpret_cast<GenericFunction>(&raw_constructor<ClassType>), + reinterpret_cast<GenericFunction>(&raw_destructor<ClassType>)); + } + + ~value_struct() { + _embind_finalize_struct(internal::TypeID<ClassType>::get()); + } + + template<typename InstanceType, typename FieldType> + value_struct& field(const char* fieldName, FieldType InstanceType::*field) { + using namespace internal; + _embind_register_struct_field( + TypeID<ClassType>::get(), + fieldName, + TypeID<FieldType>::get(), + reinterpret_cast<GenericFunction>( + &MemberAccess<InstanceType, FieldType> + ::template getWire<ClassType>), + getContext(field), + TypeID<FieldType>::get(), + reinterpret_cast<GenericFunction>( + &MemberAccess<InstanceType, FieldType> + ::template setWire<ClassType>), + getContext(field)); + return *this; + } + + template<typename Getter, typename Setter> + value_struct& field( + const char* fieldName, + Getter getter, + Setter setter + ) { + using namespace internal; + typedef GetterPolicy<Getter> GP; + typedef SetterPolicy<Setter> SP; + _embind_register_struct_field( + TypeID<ClassType>::get(), + fieldName, + TypeID<typename GP::ReturnType>::get(), + reinterpret_cast<GenericFunction>(&GP::template get<ClassType>), + GP::getContext(getter), + TypeID<typename SP::ArgumentType>::get(), + reinterpret_cast<GenericFunction>(&SP::template set<ClassType>), + SP::getContext(setter)); + return *this; } - template<typename FieldType> - value_struct& field(const char* fieldName, FieldType ClassType::*field) { - internal::_embind_register_struct_field( - internal::getTypeID<ClassType>(), + template<int Index> + value_struct& field(const char* fieldName, index<Index>) { + using namespace internal; + ClassType* null = 0; + typedef typename std::remove_reference<decltype((*null)[Index])>::type ElementType; + _embind_register_struct_field( + TypeID<ClassType>::get(), fieldName, - internal::getTypeID<FieldType>(), - reinterpret_cast<internal::GenericFunction>(&internal::FieldAccess<ClassType, FieldType>::get), - reinterpret_cast<internal::GenericFunction>(&internal::FieldAccess<ClassType, FieldType>::set), - sizeof(field), - &field); - + TypeID<ElementType>::get(), + reinterpret_cast<GenericFunction>(&internal::get_by_index<ClassType, ElementType>), + reinterpret_cast<void*>(Index), + TypeID<ElementType>::get(), + reinterpret_cast<GenericFunction>(&internal::set_by_index<ClassType, ElementType>), + reinterpret_cast<void*>(Index)); return *this; } }; - // TODO: support class definitions without constructors. - // TODO: support external class constructors - template<typename ClassType> + //////////////////////////////////////////////////////////////////////////////// + // SMART POINTERS + //////////////////////////////////////////////////////////////////////////////// + + template<typename PointerType> + struct default_smart_ptr_trait { + static sharing_policy get_sharing_policy() { + return sharing_policy::NONE; + } + + static void* share(void* v) { + return 0; // no sharing + } + }; + + // specialize if you have a different pointer type + template<typename PointerType> + struct smart_ptr_trait : public default_smart_ptr_trait<PointerType> { + typedef typename PointerType::element_type element_type; + + static element_type* get(const PointerType& ptr) { + return ptr.get(); + } + }; + + template<typename PointeeType> + struct smart_ptr_trait<std::shared_ptr<PointeeType>> { + typedef std::shared_ptr<PointeeType> PointerType; + typedef typename PointerType::element_type element_type; + + static element_type* get(const PointerType& ptr) { + return ptr.get(); + } + + static sharing_policy get_sharing_policy() { + return sharing_policy::BY_EMVAL; + } + + static std::shared_ptr<PointeeType>* share(PointeeType* p, internal::EM_VAL v) { + return new std::shared_ptr<PointeeType>( + p, + val_deleter(val::take_ownership(v))); + } + + private: + class val_deleter { + public: + val_deleter() = delete; + explicit val_deleter(val v) + : v(v) + {} + void operator()(void const*) { + v(); + // eventually we'll need to support emptied out val + v = val::undefined(); + } + private: + val v; + }; + }; + + //////////////////////////////////////////////////////////////////////////////// + // CLASSES + //////////////////////////////////////////////////////////////////////////////// + + // abstract classes + template<typename T> + class wrapper : public T { + public: + explicit wrapper(val&& wrapped) + : wrapped(std::forward<val>(wrapped)) + {} + + template<typename ReturnType, typename... Args> + ReturnType call(const char* name, Args&&... args) const { + return Caller<ReturnType, Args...>::call(wrapped, name, std::forward<Args>(args)...); + } + + template<typename ReturnType, typename... Args, typename Default> + ReturnType optional_call(const char* name, Default def, Args&&... args) const { + if (has_function(name)) { + return Caller<ReturnType, Args...>::call(wrapped, name, std::forward<Args>(args)...); + } else { + return def(); + } + } + + private: + bool has_function(const char* name) const { + return wrapped.has_function(name); + } + + // this class only exists because you can't partially specialize function templates + template<typename ReturnType, typename... Args> + struct Caller { + static ReturnType call(const val& v, const char* name, Args&&... args) { + return v.call(name, std::forward<Args>(args)...).template as<ReturnType>(); + } + }; + + template<typename... Args> + struct Caller<void, Args...> { + static void call(const val& v, const char* name, Args&&... args) { + v.call_void(name, std::forward<Args>(args)...); + } + }; + + val wrapped; + }; + +#define EMSCRIPTEN_WRAPPER(T) \ + T(::emscripten::val&& v): wrapper(std::forward<::emscripten::val>(v)) {} + + namespace internal { + struct NoBaseClass { + template<typename ClassType> + static void verify() { + } + + static TYPEID get() { + return nullptr; + } + + template<typename ClassType> + static GenericFunction getUpcaster() { + return nullptr; + } + + template<typename ClassType> + static GenericFunction getDowncaster() { + return nullptr; + } + }; + + // NOTE: this returns the class type, not the pointer type + template<typename T> + inline TYPEID getActualType(T* ptr) { + assert(ptr); + return reinterpret_cast<TYPEID>(&typeid(*ptr)); + }; + } + + template<typename BaseClass> + struct base { + template<typename ClassType> + static void verify() { + static_assert(!std::is_same<ClassType, BaseClass>::value, "Base must not have same type as class"); + static_assert(std::is_base_of<BaseClass, ClassType>::value, "Derived class must derive from base"); + } + + static internal::TYPEID get() { + return internal::TypeID<BaseClass>::get(); + } + + template<typename ClassType> + static internal::GenericFunction getUpcaster() { + return reinterpret_cast<internal::GenericFunction>(&convertPointer<ClassType, BaseClass>); + } + + template<typename ClassType> + static internal::GenericFunction getDowncaster() { + return reinterpret_cast<internal::GenericFunction>(&convertPointer<BaseClass, ClassType>); + } + + template<typename From, typename To> + static To* convertPointer(From* ptr) { + return static_cast<To*>(ptr); + } + }; + + template<typename PointerType> + struct ptr { + typedef PointerType pointer_type; + }; + + namespace internal { + template<typename T> + struct is_ptr { + enum { value = false }; + }; + + template<typename T> + struct is_ptr<ptr<T>> { + enum { value = true }; + }; + + template<typename T> + struct SmartPtrIfNeeded { + template<typename U> + SmartPtrIfNeeded(U& cls) { + cls.template smart_ptr<T>(); + } + }; + + template<typename T> + struct SmartPtrIfNeeded<T*> { + template<typename U> + SmartPtrIfNeeded(U&) { + } + }; + }; + + template<typename ClassType, typename BaseSpecifier = internal::NoBaseClass> class class_ { public: - class_(const char* name) { - internal::registerStandardTypes(); - internal::_embind_register_class( - internal::getTypeID<ClassType>(), + class_() = delete; + + template<typename = typename std::enable_if<!internal::is_ptr<ClassType>::value>::type> + explicit class_(const char* name) { + using namespace internal; + + BaseSpecifier::template verify<ClassType>(); + + _embind_register_class( + TypeID<ClassType>::get(), + TypeID<AllowedRawPointer<ClassType>>::get(), + TypeID<AllowedRawPointer<const ClassType>>::get(), + BaseSpecifier::get(), + reinterpret_cast<GenericFunction>(&getActualType<ClassType>), + BaseSpecifier::template getUpcaster<ClassType>(), + BaseSpecifier::template getDowncaster<ClassType>(), name, - reinterpret_cast<internal::GenericFunction>(&internal::raw_destructor<ClassType>)); + reinterpret_cast<GenericFunction>(&raw_destructor<ClassType>)); + } + + template<typename PointerType> + class_& smart_ptr() { + using namespace internal; + + typedef smart_ptr_trait<PointerType> PointerTrait; + typedef typename PointerTrait::element_type PointeeType; + + static_assert(std::is_same<ClassType, typename std::remove_cv<PointeeType>::type>::value, "smart pointer must point to this class"); + + _embind_register_smart_ptr( + TypeID<PointerType>::get(), + TypeID<PointeeType>::get(), + typeid(PointerType).name(), + PointerTrait::get_sharing_policy(), + reinterpret_cast<GenericFunction>(&PointerTrait::get), + reinterpret_cast<GenericFunction>(&operator_new<PointerType>), + reinterpret_cast<GenericFunction>(&PointerTrait::share), + reinterpret_cast<GenericFunction>(&raw_destructor<PointerType>)); + return *this; + }; + + template<typename... ConstructorArgs, typename... Policies> + class_& constructor(Policies... policies) { + return constructor( + &internal::operator_new<ClassType, ConstructorArgs...>, + policies...); } - template<typename... ConstructorArgs> - class_& constructor() { - internal::ArgTypeList<ConstructorArgs...> args; - internal::_embind_register_class_constructor( - internal::getTypeID<ClassType>(), + template<typename... Args, typename... Policies> + class_& constructor(ClassType* (*factory)(Args...), Policies...) { + using namespace internal; + + typename WithPolicies<Policies...>::template ArgTypeList<AllowedRawPointer<ClassType>, Args...> args; + _embind_register_class_constructor( + TypeID<ClassType>::get(), args.count, args.types, - reinterpret_cast<internal::GenericFunction>(&internal::raw_constructor<ClassType, ConstructorArgs...>)); + reinterpret_cast<GenericFunction>(&Invoker<ClassType*, Args...>::invoke), + reinterpret_cast<GenericFunction>(factory)); return *this; } - template<typename ReturnType, typename... Args> - class_& method(const char* methodName, ReturnType (ClassType::*memberFunction)(Args...)) { - internal::ArgTypeList<Args...> args; - internal::_embind_register_class_method( - internal::getTypeID<ClassType>(), + template<typename SmartPtr, typename... Args, typename... Policies> + class_& smart_ptr_constructor(SmartPtr (*factory)(Args...), Policies...) { + using namespace internal; + + smart_ptr<SmartPtr>(); + + typename WithPolicies<Policies...>::template ArgTypeList<SmartPtr, Args...> args; + _embind_register_class_constructor( + TypeID<ClassType>::get(), + args.count, + args.types, + reinterpret_cast<GenericFunction>(&Invoker<SmartPtr, Args...>::invoke), + reinterpret_cast<GenericFunction>(factory)); + return *this; + } + + template<typename WrapperType, typename PointerType = WrapperType*> + class_& allow_subclass() { + using namespace internal; + + auto cls = class_<WrapperType, base<ClassType>>(typeid(WrapperType).name()) + ; + SmartPtrIfNeeded<PointerType> _(cls); + + return class_function( + "implement", + &wrapped_new<PointerType, WrapperType, val>, + allow_raw_pointer<ret_val>()); + } + + template<typename ReturnType, typename... Args, typename... Policies> + class_& function(const char* methodName, ReturnType (ClassType::*memberFunction)(Args...), Policies...) { + using namespace internal; + + typename WithPolicies<Policies...>::template ArgTypeList<ReturnType, AllowedRawPointer<ClassType>, Args...> args; + _embind_register_class_function( + TypeID<ClassType>::get(), methodName, - internal::getTypeID<ReturnType>(), args.count, args.types, - reinterpret_cast<internal::GenericFunction>(&internal::MethodInvoker<ClassType, ReturnType, Args...>::invoke), - sizeof(memberFunction), - &memberFunction); + reinterpret_cast<GenericFunction>(&MethodInvoker<decltype(memberFunction), ReturnType, ClassType*, Args...>::invoke), + getContext(memberFunction)); return *this; } - template<typename ReturnType, typename... Args> - class_& method(const char* methodName, ReturnType (ClassType::*memberFunction)(Args...) const) { - internal::ArgTypeList<Args...> args; - internal::_embind_register_class_method( - internal::getTypeID<ClassType>(), + template<typename ReturnType, typename... Args, typename... Policies> + class_& function(const char* methodName, ReturnType (ClassType::*memberFunction)(Args...) const, Policies...) { + using namespace internal; + + typename WithPolicies<Policies...>::template ArgTypeList<ReturnType, AllowedRawPointer<const ClassType>, Args...> args; + _embind_register_class_function( + TypeID<ClassType>::get(), + methodName, + args.count, + args.types, + reinterpret_cast<GenericFunction>(&MethodInvoker<decltype(memberFunction), ReturnType, const ClassType*, Args...>::invoke), + getContext(memberFunction)); + return *this; + } + + template<typename ReturnType, typename ThisType, typename... Args, typename... Policies> + class_& function(const char* methodName, ReturnType (*function)(ThisType, Args...), Policies...) { + using namespace internal; + + typename WithPolicies<Policies...>::template ArgTypeList<ReturnType, ThisType, Args...> args; + _embind_register_class_function( + TypeID<ClassType>::get(), methodName, - internal::getTypeID<ReturnType>(), args.count, args.types, - reinterpret_cast<internal::GenericFunction>(&internal::ConstMethodInvoker<ClassType, ReturnType, Args...>::invoke), - sizeof(memberFunction), - &memberFunction); + reinterpret_cast<GenericFunction>(&FunctionInvoker<decltype(function), ReturnType, ThisType, Args...>::invoke), + getContext(function)); return *this; } - template<typename FieldType> - class_& field(const char* fieldName, FieldType ClassType::*field) { - internal::_embind_register_class_field( - internal::getTypeID<ClassType>(), + template<typename FieldType, typename = typename std::enable_if<!std::is_function<FieldType>::value>::type> + class_& property(const char* fieldName, const FieldType ClassType::*field) { + using namespace internal; + + _embind_register_class_property( + TypeID<ClassType>::get(), fieldName, - internal::getTypeID<FieldType>(), - reinterpret_cast<internal::GenericFunction>(&internal::FieldAccess<ClassType, FieldType>::get), - reinterpret_cast<internal::GenericFunction>(&internal::FieldAccess<ClassType, FieldType>::set), - sizeof(field), - &field); + TypeID<FieldType>::get(), + reinterpret_cast<GenericFunction>(&MemberAccess<ClassType, FieldType>::template getWire<ClassType>), + getContext(field), + 0, + 0, + 0); return *this; } - template<typename ReturnType, typename... Args> - class_& classmethod(const char* methodName, ReturnType (*classMethod)(Args...)) { - internal::ArgTypeList<Args...> args; - internal::_embind_register_class_classmethod( - internal::getTypeID<ClassType>(), + template<typename FieldType, typename = typename std::enable_if<!std::is_function<FieldType>::value>::type> + class_& property(const char* fieldName, FieldType ClassType::*field) { + using namespace internal; + + _embind_register_class_property( + TypeID<ClassType>::get(), + fieldName, + TypeID<FieldType>::get(), + reinterpret_cast<GenericFunction>(&MemberAccess<ClassType, FieldType>::template getWire<ClassType>), + getContext(field), + TypeID<FieldType>::get(), + reinterpret_cast<GenericFunction>(&MemberAccess<ClassType, FieldType>::template setWire<ClassType>), + getContext(field)); + return *this; + } + + template<typename Getter> + class_& property(const char* fieldName, Getter getter) { + using namespace internal; + typedef GetterPolicy<Getter> GP; + _embind_register_class_property( + TypeID<ClassType>::get(), + fieldName, + TypeID<typename GP::ReturnType>::get(), + reinterpret_cast<GenericFunction>(&GP::template get<ClassType>), + GP::getContext(getter), + 0, + 0, + 0); + return *this; + } + + template<typename Getter, typename Setter> + class_& property(const char* fieldName, Getter getter, Setter setter) { + using namespace internal; + typedef GetterPolicy<Getter> GP; + typedef SetterPolicy<Setter> SP; + _embind_register_class_property( + TypeID<ClassType>::get(), + fieldName, + TypeID<typename GP::ReturnType>::get(), + reinterpret_cast<GenericFunction>(&GP::template get<ClassType>), + GP::getContext(getter), + TypeID<typename SP::ArgumentType>::get(), + reinterpret_cast<GenericFunction>(&SP::template set<ClassType>), + SP::getContext(setter)); + return *this; + } + + template<typename ReturnType, typename... Args, typename... Policies> + class_& class_function(const char* methodName, ReturnType (*classMethod)(Args...), Policies...) { + using namespace internal; + + typename WithPolicies<Policies...>::template ArgTypeList<ReturnType, Args...> args; + _embind_register_class_class_function( + TypeID<ClassType>::get(), methodName, - internal::getTypeID<ReturnType>(), args.count, args.types, - reinterpret_cast<internal::GenericFunction>(classMethod)); + reinterpret_cast<internal::GenericFunction>(&internal::Invoker<ReturnType, Args...>::invoke), + reinterpret_cast<GenericFunction>(classMethod)); return *this; } }; + //////////////////////////////////////////////////////////////////////////////// + // VECTORS + //////////////////////////////////////////////////////////////////////////////// + + namespace internal { + template<typename VectorType> + struct VectorAccess { + static val get( + const VectorType& v, + typename VectorType::size_type index + ) { + if (index < v.size()) { + return val(v[index]); + } else { + return val::undefined(); + } + } + + static bool set( + VectorType& v, + typename VectorType::size_type index, + const typename VectorType::value_type& value + ) { + v[index] = value; + return true; + } + }; + } + + template<typename T> + class_<std::vector<T>> register_vector(const char* name) { + typedef std::vector<T> VecType; + + void (VecType::*push_back)(const T&) = &VecType::push_back; + return class_<std::vector<T>>(name) + .template constructor<>() + .function("push_back", push_back) + .function("size", &VecType::size) + .function("get", &internal::VectorAccess<VecType>::get) + .function("set", &internal::VectorAccess<VecType>::set) + ; + } + + //////////////////////////////////////////////////////////////////////////////// + // MAPS + //////////////////////////////////////////////////////////////////////////////// + + namespace internal { + template<typename MapType> + struct MapAccess { + static val get( + const MapType& m, + const typename MapType::key_type& k + ) { + auto i = m.find(k); + if (i == m.end()) { + return val::undefined(); + } else { + return val(i->second); + } + } + + static void set( + MapType& m, + const typename MapType::key_type& k, + const typename MapType::mapped_type& v + ) { + m[k] = v; + } + }; + } + + template<typename K, typename V> + class_<std::map<K, V>> register_map(const char* name) { + typedef std::map<K,V> MapType; + + return class_<MapType>(name) + .template constructor<>() + .function("size", &MapType::size) + .function("get", internal::MapAccess<MapType>::get) + .function("set", internal::MapAccess<MapType>::set) + ; + } + + + //////////////////////////////////////////////////////////////////////////////// + // ENUMS + //////////////////////////////////////////////////////////////////////////////// + template<typename EnumType> class enum_ { public: enum_(const char* name) { _embind_register_enum( - internal::getTypeID<EnumType>(), + internal::TypeID<EnumType>::get(), name); } @@ -520,13 +1188,39 @@ namespace emscripten { static_assert(sizeof(value) <= sizeof(internal::GenericEnumValue), "enum type must fit in a GenericEnumValue"); _embind_register_enum_value( - internal::getTypeID<EnumType>(), + internal::TypeID<EnumType>::get(), name, static_cast<internal::GenericEnumValue>(value)); return *this; } }; + //////////////////////////////////////////////////////////////////////////////// + // CONSTANTS + //////////////////////////////////////////////////////////////////////////////// + + namespace internal { + template<typename T> + uintptr_t asGenericValue(T t) { + return static_cast<uintptr_t>(t); + } + + template<typename T> + uintptr_t asGenericValue(T* p) { + return reinterpret_cast<uintptr_t>(p); + } + } + + template<typename ConstantType> + void constant(const char* name, const ConstantType& v) { + using namespace internal; + typedef BindingType<const ConstantType&> BT; + _embind_register_constant( + name, + TypeID<const ConstantType&>::get(), + asGenericValue(BindingType<const ConstantType&>::toWireType(v))); + } + namespace internal { template<typename T> class optional { @@ -541,13 +1235,22 @@ namespace emscripten { } } - optional(const optional&) = delete; + optional(const optional& rhs) + : initialized(false) + { + *this = rhs; + } T& operator*() { assert(initialized); return *get(); } + const T& operator*() const { + assert(initialized); + return *get(); + } + explicit operator bool() const { return initialized; } @@ -560,89 +1263,47 @@ namespace emscripten { initialized = true; return *this; } - + + optional& operator=(const optional& o) { + if (initialized) { + get()->~T(); + } + if (o.initialized) { + new(get()) T(*o); + } + initialized = o.initialized; + return *this; + } + private: T* get() { return reinterpret_cast<T*>(&data); } + T const* get() const { + return reinterpret_cast<T const*>(&data); + } + bool initialized; typename std::aligned_storage<sizeof(T)>::type data; }; } +} - template<typename InterfaceType> - class wrapper : public InterfaceType { - public: - // Not necessary in any example so far, but appeases a compiler warning. - virtual ~wrapper() {} - - typedef InterfaceType interface; - - void initialize(internal::EM_VAL handle) { - if (jsobj) { - internal::_embind_fatal_error( - "Cannot initialize interface wrapper twice", - typeid(InterfaceType).name()); - } - jsobj = val::take_ownership(handle); - } - - template<typename ReturnType, typename... Args> - ReturnType call(const char* name, Args... args) { - assertInitialized(); - return Caller<ReturnType, Args...>::call(*jsobj, name, args...); - } - - private: - // this class only exists because you can't partially specialize function templates - template<typename ReturnType, typename... Args> - struct Caller { - static ReturnType call(val& v, const char* name, Args... args) { - return v.call(name, args...).template as<ReturnType>(); - } - }; - - template<typename... Args> - struct Caller<void, Args...> { - static void call(val& v, const char* name, Args... args) { - v.call(name, args...); +namespace emscripten { + namespace internal { + class BindingsDefinition { + public: + template<typename Function> + BindingsDefinition(Function fn) { + fn(); } }; - - void assertInitialized() { - if (!jsobj) { - internal::_embind_fatal_error( - "Cannot invoke call on uninitialized interface wrapper.", - typeid(InterfaceType).name()); - } - } - - internal::optional<val> jsobj; - }; - - namespace internal { - template<typename WrapperType> - WrapperType* create_interface_wrapper(EM_VAL e) { - WrapperType* p = new WrapperType; - p->initialize(e); - return p; - } } - - template<typename WrapperType> - class interface { - public: - typedef typename WrapperType::interface InterfaceType; - - interface(const char* name) { - _embind_register_interface( - internal::getTypeID<InterfaceType>(), - name, - reinterpret_cast<internal::GenericFunction>(&internal::create_interface_wrapper<WrapperType>), - reinterpret_cast<internal::GenericFunction>(&internal::raw_destructor<WrapperType>)); - } - }; } -#define EMSCRIPTEN_BINDINGS(fn) static emscripten::internal::BindingsDefinition anon_symbol(fn); +#define EMSCRIPTEN_BINDINGS(name) \ + static struct BindingInitializer_##name { \ + BindingInitializer_##name(); \ + } BindingInitializer_##name##_instance; \ + BindingInitializer_##name::BindingInitializer_##name() diff --git a/system/include/emscripten/val.h b/system/include/emscripten/val.h index 96db9326..09cad80e 100644 --- a/system/include/emscripten/val.h +++ b/system/include/emscripten/val.h @@ -2,58 +2,136 @@ #include <stdint.h> // uintptr_t #include <emscripten/wire.h> +#include <vector> namespace emscripten { namespace internal { // Implemented in JavaScript. Don't call these directly. extern "C" { + void _emval_register_symbol(const char*); + typedef struct _EM_VAL* EM_VAL; void _emval_incref(EM_VAL value); void _emval_decref(EM_VAL value); + + EM_VAL _emval_new_array(); EM_VAL _emval_new_object(); - EM_VAL _emval_new_long(long value); - EM_VAL _emval_new_cstring(const char* str); - EM_VAL _emval_get_property(EM_VAL object, const char* key); - EM_VAL _emval_get_property_by_long(EM_VAL object, long key); - EM_VAL _emval_get_property_by_unsigned_long(EM_VAL object, unsigned long key); - void _emval_set_property(EM_VAL object, const char* key, EM_VAL value); - void _emval_set_property_by_int(EM_VAL object, long key, EM_VAL value); - void _emval_as(EM_VAL value, emscripten::internal::TypeID returnType); + EM_VAL _emval_undefined(); + EM_VAL _emval_null(); + EM_VAL _emval_new_cstring(const char*); + void _emval_take_value(TYPEID type/*, ...*/); + + EM_VAL _emval_new( + EM_VAL value, + unsigned argCount, + internal::TYPEID argTypes[] + /*, ... */); + + EM_VAL _emval_get_global(const char* name); + EM_VAL _emval_get_module_property(const char* name); + EM_VAL _emval_get_property(EM_VAL object, EM_VAL key); + void _emval_set_property(EM_VAL object, EM_VAL key, EM_VAL value); + void _emval_as(EM_VAL value, TYPEID returnType); EM_VAL _emval_call( EM_VAL value, unsigned argCount, - internal::TypeID argTypes[] + internal::TYPEID argTypes[] /*, ... */); EM_VAL _emval_call_method( EM_VAL value, const char* methodName, unsigned argCount, - internal::TypeID argTypes[] + internal::TYPEID argTypes[] /*, ... */); + void _emval_call_void_method( + EM_VAL value, + const char* methodName, + unsigned argCount, + internal::TYPEID argTypes[] + /*, ...*/); + bool _emval_has_function( + EM_VAL value, + const char* methodName); } } + template<const char* address> + struct symbol_registrar { + symbol_registrar() { + internal::_emval_register_symbol(address); + } + }; + +#define EMSCRIPTEN_SYMBOL(name) \ + static const char name##_symbol[] = #name; \ + static const symbol_registrar<name##_symbol> name##_registrar + class val { public: + // missing operators: + // * delete + // * in + // * instanceof + // * typeof + // * ! ~ - + ++ -- + // * * / % + // * + - + // * << >> >>> + // * < <= > >= + // * == != === !== + // * & ^ | && || ?: + // + // exposing void, comma, and conditional is unnecessary + // same with: = += -= *= /= %= <<= >>= >>>= &= ^= |= + + static val array() { + return val(internal::_emval_new_array()); + } + static val object() { return val(internal::_emval_new_object()); - }; + } + + static val undefined() { + return val(internal::_emval_undefined()); + } + + static val null() { + return val(internal::_emval_null()); + } static val take_ownership(internal::EM_VAL e) { return val(e); } - explicit val(long l) - : handle(internal::_emval_new_long(l)) - {} + static val global(const char* name) { + return val(internal::_emval_get_global(name)); + } - explicit val(const char* str) - : handle(internal::_emval_new_cstring(str)) - {} + static val module_property(const char* name) { + return val(internal::_emval_get_module_property(name)); + } + + template<typename T> + explicit val(T&& value) { + typedef internal::BindingType<T> BT; + auto taker = reinterpret_cast<internal::EM_VAL (*)(internal::TYPEID, typename BT::WireType)>(&internal::_emval_take_value); + handle = taker(internal::TypeID<T>::get(), BT::toWireType(std::forward<T>(value))); + } val() = delete; + explicit val(const char* v) + : handle(internal::_emval_new_cstring(v)) + {} + + val(val&& v) + : handle(v.handle) + { + v.handle = 0; + } + val(const val& v) : handle(v.handle) { @@ -64,89 +142,132 @@ namespace emscripten { internal::_emval_decref(handle); } - val& operator=(const val& v) { - internal::_emval_incref(v.handle); + val& operator=(val&& v) { internal::_emval_decref(handle); handle = v.handle; + v.handle = 0; return *this; } - val get(const char* key) const { - return val(internal::_emval_get_property(handle, key)); + val& operator=(const val& v) { + internal::_emval_incref(v.handle); + internal::_emval_decref(handle); + handle = v.handle; + return *this; } - val get(int key) const { - return get(long(key)); + bool hasOwnProperty(const char* key) const { + return val::global("Object")["prototype"]["hasOwnProperty"].call("call", *this, val(key)).as<bool>(); } - val get(unsigned int key) const { - typedef unsigned long T; - return get(T(key)); - } + template<typename... Args> + val new_(Args&&... args) const { + using namespace internal; - val get(long key) const { - return val(internal::_emval_get_property_by_long(handle, key)); + WithPolicies<>::ArgTypeList<Args...> argList; + // todo: this is awfully similar to operator(), can we + // merge them somehow? + typedef EM_VAL (*TypedNew)( + EM_VAL, + unsigned, + TYPEID argTypes[], + typename BindingType<Args>::WireType...); + TypedNew typedNew = reinterpret_cast<TypedNew>(&_emval_new); + return val( + typedNew( + handle, + argList.count, + argList.types, + toWireType(std::forward<Args>(args))...)); } - - val get(unsigned long key) const { - return val(internal::_emval_get_property_by_unsigned_long(handle, key)); + + template<typename T> + val operator[](const T& key) const { + return val(internal::_emval_get_property(handle, val(key).handle)); } - void set(const char* key, val v) { - internal::_emval_set_property(handle, key, v.handle); + template<typename T> + void set(const T& key, val v) { + internal::_emval_set_property(handle, val(key).handle, v.handle); } - void set(long key, val v) { - internal::_emval_set_property_by_int(handle, key, v.handle); - } + template<typename... Args> + val operator()(Args&&... args) { + using namespace internal; - template<typename ...Args> - val operator()(Args... args) { - internal::ArgTypeList<Args...> argList; - typedef internal::EM_VAL (*TypedCall)( - internal::EM_VAL, + WithPolicies<>::ArgTypeList<Args...> argList; + typedef EM_VAL (*TypedCall)( + EM_VAL, unsigned, - internal::TypeID argTypes[], - typename internal::BindingType<Args>::WireType...); - TypedCall typedCall = reinterpret_cast<TypedCall>(&internal::_emval_call); + TYPEID argTypes[], + typename BindingType<Args>::WireType...); + TypedCall typedCall = reinterpret_cast<TypedCall>(&_emval_call); return val( typedCall( handle, argList.count, argList.types, - internal::toWireType(args)...)); + toWireType(std::forward<Args>(args))...)); } template<typename ...Args> - val call(const char* name, Args... args) { - internal::ArgTypeList<Args...> argList; - typedef internal::EM_VAL (*TypedCall)( - internal::EM_VAL, + val call(const char* name, Args&&... args) const { + using namespace internal; + + WithPolicies<>::ArgTypeList<Args...> argList; + typedef EM_VAL (*TypedCall)( + EM_VAL, const char* name, unsigned, - internal::TypeID argTypes[], - typename internal::BindingType<Args>::WireType...); - TypedCall typedCall = reinterpret_cast<TypedCall>(&internal::_emval_call_method); + TYPEID argTypes[], + typename BindingType<Args>::WireType...); + TypedCall typedCall = reinterpret_cast<TypedCall>(&_emval_call_method); return val( typedCall( handle, name, argList.count, argList.types, - internal::toWireType(args)...)); + toWireType(std::forward<Args>(args))...)); + } + + template<typename ...Args> + void call_void(const char* name, Args&&... args) const { + using namespace internal; + + WithPolicies<>::ArgTypeList<Args...> argList; + typedef void (*TypedCall)( + EM_VAL, + const char* name, + unsigned, + TYPEID argTypes[], + typename BindingType<Args>::WireType...); + TypedCall typedCall = reinterpret_cast<TypedCall>(&_emval_call_void_method); + return typedCall( + handle, + name, + argList.count, + argList.types, + toWireType(std::forward<Args>(args))...); + } + + bool has_function(const char* name) const { + return _emval_has_function(handle, name); } template<typename T> T as() const { - typedef internal::BindingType<T> BT; + using namespace internal; + + typedef BindingType<T> BT; typedef typename BT::WireType (*TypedAs)( - internal::EM_VAL value, - emscripten::internal::TypeID returnType); - TypedAs typedAs = reinterpret_cast<TypedAs>(&internal::_emval_as); + EM_VAL value, + TYPEID returnType); + TypedAs typedAs = reinterpret_cast<TypedAs>(&_emval_as); - typename BT::WireType wt = typedAs(handle, internal::getTypeID<T>()); - internal::WireDeleter<T> deleter(wt); + typename BT::WireType wt = typedAs(handle, TypeID<T>::get()); + WireDeleter<T> deleter(wt); return BT::fromWireType(wt); } @@ -165,13 +286,27 @@ namespace emscripten { template<> struct BindingType<val> { typedef internal::EM_VAL WireType; - static WireType toWireType(val v) { + static WireType toWireType(const val& v) { _emval_incref(v.handle); return v.handle; } static val fromWireType(WireType v) { - return val(v); + return val::take_ownership(v); + } + static void destroy(WireType v) { } }; } + + template<typename T> + std::vector<T> vecFromJSArray(val v) { + auto l = v["length"].as<unsigned>(); + + std::vector<T> rv; + for(unsigned i = 0; i < l; ++i) { + rv.push_back(v[i].as<T>()); + } + + return rv; + }; } diff --git a/system/include/emscripten/wire.h b/system/include/emscripten/wire.h index fbf0897d..6fb15fc7 100644..100755 --- a/system/include/emscripten/wire.h +++ b/system/include/emscripten/wire.h @@ -7,66 +7,110 @@ // // We'll call the on-the-wire type WireType. +#include <cstdlib> +#include <memory> +#include <string> + namespace emscripten { namespace internal { - typedef const struct _TypeID* TypeID; + typedef const struct _TYPEID* TYPEID; // This implementation is technically not legal, as it's not // required that two calls to typeid produce the same exact - // std::type_info instance. That said, it's likely to work. - // Should it not work in the future: replace TypeID with - // an int, and store all TypeInfo we see in a map, allocating - // new TypeIDs as we add new items to the map. + // std::type_info instance. That said, it's likely to work + // given Emscripten compiles everything into one binary. + // Should it not work in the future: replace TypeID with an + // int, and store all TypeInfo we see in a map, allocating new + // TypeIDs as we add new items to the map. template<typename T> - inline TypeID getTypeID() { - return reinterpret_cast<TypeID>(&typeid(T)); - } + struct TypeID { + static TYPEID get() { + return reinterpret_cast<TYPEID>(&typeid(T)); + } + }; - // count<> + template<typename T> + struct TypeID<std::unique_ptr<T>> { + static TYPEID get() { + return TypeID<T>::get(); + } + }; - template<typename... Args> - struct count; + template<typename T> + struct TypeID<T*> { + static_assert(!std::is_pointer<T*>::value, "Implicitly binding raw pointers is illegal. Specify allow_raw_pointer<arg<?>>"); + }; - template<> - struct count<> { - enum { value = 0 }; + template<typename T> + struct AllowedRawPointer { }; - template<typename T, typename... Args> - struct count<T, Args...> { - enum { value = 1 + count<Args...>::value }; + template<typename T> + struct TypeID<AllowedRawPointer<T>> { + static TYPEID get() { + return reinterpret_cast<TYPEID>(&typeid(T*)); + } }; + + // ExecutePolicies<> - // ArgTypeList<> + template<typename... Policies> + struct ExecutePolicies; - template<typename... Args> + template<> + struct ExecutePolicies<> { + template<typename T, int Index> + struct With { + typedef T type; + }; + }; + + template<typename Policy, typename... Remaining> + struct ExecutePolicies<Policy, Remaining...> { + template<typename T, int Index> + struct With { + typedef typename Policy::template Transform< + typename ExecutePolicies<Remaining...>::template With<T, Index>::type, + Index + >::type type; + }; + }; + + // ArgTypes<> + + template<int Index, typename... Args> struct ArgTypes; - template<> - struct ArgTypes<> { - static void fill(TypeID* argTypes) { + template<int Index> + struct ArgTypes<Index> { + template<typename... Policies> + static void fill(TYPEID* argTypes) { } }; - template<typename T, typename... Args> - struct ArgTypes<T, Args...> { - static void fill(TypeID* argTypes) { - *argTypes = getTypeID<T>(); - return ArgTypes<Args...>::fill(argTypes + 1); + template<int Index, typename T, typename... Remaining> + struct ArgTypes<Index, T, Remaining...> { + template<typename... Policies> + static void fill(TYPEID* argTypes) { + typedef typename ExecutePolicies<Policies...>::template With<T, Index>::type TransformT; + *argTypes = TypeID<TransformT>::get(); + return ArgTypes<Index + 1, Remaining...>::template fill<Policies...>(argTypes + 1); } }; - template<typename... Args> - struct ArgTypeList { - enum { args_count = count<Args...>::value }; - - ArgTypeList() { - count = args_count; - ArgTypes<Args...>::fill(types); - } + // WithPolicies<...>::ArgTypeList<...> + template<typename... Policies> + struct WithPolicies { + template<typename... Args> + struct ArgTypeList { + ArgTypeList() { + count = sizeof...(Args); + ArgTypes<0, Args...>::template fill<Policies...>(types); + } - unsigned count; - TypeID types[args_count]; + unsigned count; + TYPEID types[sizeof...(Args)]; + }; }; // BindingType<T> @@ -74,19 +118,18 @@ namespace emscripten { template<typename T> struct BindingType; -#define EMSCRIPTEN_DEFINE_NATIVE_BINDING_TYPE(type) \ - template<> \ - struct BindingType<type> { \ - typedef type WireType; \ - \ - constexpr static WireType toWireType(type v) { \ - return v; \ - } \ - constexpr static type fromWireType(WireType v) { \ - return v; \ - } \ - static void destroy(WireType) { \ - } \ +#define EMSCRIPTEN_DEFINE_NATIVE_BINDING_TYPE(type) \ + template<> \ + struct BindingType<type> { \ + typedef type WireType; \ + constexpr static WireType toWireType(const type& v) { \ + return v; \ + } \ + constexpr static type fromWireType(WireType v) { \ + return v; \ + } \ + static void destroy(WireType) { \ + } \ } EMSCRIPTEN_DEFINE_NATIVE_BINDING_TYPE(char); @@ -120,35 +163,75 @@ namespace emscripten { template<> struct BindingType<std::string> { - typedef char* WireType; - static WireType toWireType(std::string v) { - return strdup(v.c_str()); + typedef struct { + size_t length; + char data[1]; // trailing data + }* WireType; + static WireType toWireType(const std::string& v) { + WireType wt = (WireType)malloc(sizeof(size_t) + v.length()); + wt->length = v.length(); + memcpy(wt->data, v.data(), v.length()); + return wt; + } + static std::string fromWireType(WireType v) { + return std::string(v->data, v->length); } - static std::string fromWireType(char* v) { - return std::string(v); + static void destroy(WireType v) { + free(v); } }; template<> - struct BindingType<const std::string&> { - typedef char* WireType; - static WireType toWireType(std::string v) { - return strdup(v.c_str()); + struct BindingType<std::wstring> { + typedef struct { + size_t length; + wchar_t data[1]; // trailing data + }* WireType; + static WireType toWireType(const std::wstring& v) { + WireType wt = (WireType)malloc(sizeof(size_t) + v.length() * sizeof(wchar_t)); + wt->length = v.length(); + wmemcpy(wt->data, v.data(), v.length()); + return wt; + } + static std::wstring fromWireType(WireType v) { + return std::wstring(v->data, v->length); } - static std::string fromWireType(char* v) { - return std::string(v); + static void destroy(WireType v) { + free(v); } }; - template<typename Enum> - struct EnumBindingType { - typedef Enum WireType; + template<typename T> + struct BindingType<const T> : public BindingType<T> { + }; - static WireType toWireType(Enum v) { - return v; + template<typename T> + struct BindingType<T&> : public BindingType<T> { + }; + + template<typename T> + struct BindingType<const T&> : public BindingType<T> { + }; + + template<typename T> + struct BindingType<T&&> { + typedef typename BindingType<T>::WireType WireType; + static WireType toWireType(const T& v) { + return BindingType<T>::toWireType(v); } - static Enum fromWireType(WireType v) { - return v; + static T fromWireType(WireType wt) { + return BindingType<T>::fromWireType(wt); + } + }; + + template<typename T> + struct BindingType<T*> { + typedef T* WireType; + static WireType toWireType(T* p) { + return p; + } + static T* fromWireType(WireType wt) { + return wt; } }; @@ -157,33 +240,16 @@ namespace emscripten { typedef typename std::remove_reference<T>::type ActualT; typedef ActualT* WireType; - struct Marshaller { - explicit Marshaller(WireType wt) - : wireType(wt) - {} - - Marshaller(Marshaller&& wt) - : wireType(wt.wireType) - { - wt.wireType = 0; - } - - operator ActualT&() const { - return *wireType; - } - - private: - Marshaller() = delete; - Marshaller(const Marshaller&) = delete; - ActualT* wireType; - }; - - static WireType toWireType(T v) { + static WireType toWireType(const T& v) { return new T(v); } - static Marshaller fromWireType(WireType p) { - return Marshaller(p); + static WireType toWireType(T&& v) { + return new T(std::forward<T>(v)); + } + + static ActualT& fromWireType(WireType p) { + return *p; } static void destroy(WireType p) { @@ -191,19 +257,28 @@ namespace emscripten { } }; + // Is this necessary? template<typename T> - struct WireDeleter { + struct GenericBindingType<std::unique_ptr<T>> { typedef typename BindingType<T>::WireType WireType; - - WireDeleter(WireType wt) - : wt(wt) - {} - - ~WireDeleter() { - BindingType<T>::destroy(wt); + + static WireType toWireType(std::unique_ptr<T> p) { + return BindingType<T>::toWireType(*p); + } + }; + + template<typename Enum> + struct EnumBindingType { + typedef Enum WireType; + + static WireType toWireType(Enum v) { + return v; + } + static Enum fromWireType(WireType v) { + return v; + } + static void destroy(WireType) { } - - WireType wt; }; // catch-all generic binding @@ -215,9 +290,23 @@ namespace emscripten { {}; template<typename T> - auto toWireType(const T& v) -> typename BindingType<T>::WireType { - return BindingType<T>::toWireType(v); + auto toWireType(T&& v) -> typename BindingType<T>::WireType { + return BindingType<T>::toWireType(std::forward<T>(v)); } + template<typename T> + struct WireDeleter { + typedef typename BindingType<T>::WireType WireType; + + WireDeleter(WireType wt) + : wt(wt) + {} + + ~WireDeleter() { + BindingType<T>::destroy(wt); + } + + WireType wt; + }; } } diff --git a/system/lib/compiler-rt/LICENSE.TXT b/system/lib/compiler-rt/LICENSE.TXT new file mode 100644 index 00000000..6aab1f69 --- /dev/null +++ b/system/lib/compiler-rt/LICENSE.TXT @@ -0,0 +1,97 @@ +============================================================================== +compiler_rt License +============================================================================== + +The compiler_rt library is dual licensed under both the University of Illinois +"BSD-Like" license and the MIT license. As a user of this code you may choose +to use it under either license. As a contributor, you agree to allow your code +to be used under both. + +Full text of the relevant licenses is included below. + +============================================================================== + +University of Illinois/NCSA +Open Source License + +Copyright (c) 2009-2013 by the contributors listed in CREDITS.TXT + +All rights reserved. + +Developed by: + + LLVM Team + + University of Illinois at Urbana-Champaign + + http://llvm.org + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal with +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimers. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimers in the + documentation and/or other materials provided with the distribution. + + * Neither the names of the LLVM Team, University of Illinois at + Urbana-Champaign, nor the names of its contributors may be used to + endorse or promote products derived from this Software without specific + prior written permission. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE +SOFTWARE. + +============================================================================== + +Copyright (c) 2009-2013 by the contributors listed in CREDITS.TXT + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +============================================================================== +Copyrights and Licenses for Third Party Software Distributed with LLVM: +============================================================================== +The LLVM software contains code written by third parties. Such software will +have its own individual LICENSE.TXT file in the directory in which it appears. +This file will describe the copyrights, license, and restrictions which apply +to that code. + +The disclaimer of warranty in the University of Illinois Open Source License +applies to all code in the LLVM Distribution, and nothing in any of the +other licenses gives permission to use the names of the LLVM Team or the +University of Illinois to endorse or promote products derived from this +Software. + +The following pieces of software have additional or alternate copyrights, +licenses, and/or restrictions: + +Program Directory +------- --------- +mach_override lib/interception/mach_override diff --git a/system/lib/compiler-rt/divdi3.c b/system/lib/compiler-rt/divdi3.c new file mode 100644 index 00000000..09f4a04e --- /dev/null +++ b/system/lib/compiler-rt/divdi3.c @@ -0,0 +1,47 @@ +/* ===-- divdi3.c - Implement __divdi3 -------------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __divdi3 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +du_int COMPILER_RT_ABI __udivmoddi4(du_int a, du_int b, du_int* rem); + +/* Returns: a / b */ + +COMPILER_RT_ABI di_int +__divdi3(di_int a, di_int b) +{ + const int bits_in_dword_m1 = (int)(sizeof(di_int) * CHAR_BIT) - 1; + di_int s_a = a >> bits_in_dword_m1; /* s_a = a < 0 ? -1 : 0 */ + di_int s_b = b >> bits_in_dword_m1; /* s_b = b < 0 ? -1 : 0 */ + a = (a ^ s_a) - s_a; /* negate if s_a == -1 */ + b = (b ^ s_b) - s_b; /* negate if s_b == -1 */ + s_a ^= s_b; /*sign of quotient */ + return (__udivmoddi4(a, b, (du_int*)0) ^ s_a) - s_a; /* negate if s_a == -1 */ +} + +/* XXX EMSCRIPTEN */ + +COMPILER_RT_ABI di_int +__remdi3(di_int a, di_int b) +{ + const int bits_in_dword_m1 = (int)(sizeof(di_int) * CHAR_BIT) - 1; + di_int s_a = a >> bits_in_dword_m1; /* s_a = a < 0 ? -1 : 0 */ + di_int s_b = b >> bits_in_dword_m1; /* s_b = b < 0 ? -1 : 0 */ + a = (a ^ s_a) - s_a; /* negate if s_a == -1 */ + b = (b ^ s_b) - s_b; /* negate if s_b == -1 */ + du_int rem; + __udivmoddi4(a, b, &rem); + return (rem ^ s_a) - s_a; /* negate if s_a == -1 */ +} + diff --git a/system/lib/compiler-rt/int_endianness.h b/system/lib/compiler-rt/int_endianness.h new file mode 100644 index 00000000..17905355 --- /dev/null +++ b/system/lib/compiler-rt/int_endianness.h @@ -0,0 +1,116 @@ +/* ===-- int_endianness.h - configuration header for compiler-rt ------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file is a configuration header for compiler-rt. + * This file is not part of the interface of this library. + * + * ===----------------------------------------------------------------------=== + */ + +#ifndef INT_ENDIANNESS_H +#define INT_ENDIANNESS_H + +#if defined(__SVR4) && defined(__sun) +#include <sys/byteorder.h> + +#if _BYTE_ORDER == _BIG_ENDIAN +#define _YUGA_LITTLE_ENDIAN 0 +#define _YUGA_BIG_ENDIAN 1 +#elif _BYTE_ORDER == _LITTLE_ENDIAN +#define _YUGA_LITTLE_ENDIAN 1 +#define _YUGA_BIG_ENDIAN 0 +#endif /* _BYTE_ORDER */ + +#endif /* Solaris and AuroraUX. */ + +/* .. */ + +#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__) || defined(__minix) +#include <sys/endian.h> + +#if _BYTE_ORDER == _BIG_ENDIAN +#define _YUGA_LITTLE_ENDIAN 0 +#define _YUGA_BIG_ENDIAN 1 +#elif _BYTE_ORDER == _LITTLE_ENDIAN +#define _YUGA_LITTLE_ENDIAN 1 +#define _YUGA_BIG_ENDIAN 0 +#endif /* _BYTE_ORDER */ + +#endif /* *BSD */ + +#if defined(__OpenBSD__) || defined(__Bitrig__) +#include <machine/endian.h> + +#if _BYTE_ORDER == _BIG_ENDIAN +#define _YUGA_LITTLE_ENDIAN 0 +#define _YUGA_BIG_ENDIAN 1 +#elif _BYTE_ORDER == _LITTLE_ENDIAN +#define _YUGA_LITTLE_ENDIAN 1 +#define _YUGA_BIG_ENDIAN 0 +#endif /* _BYTE_ORDER */ + +#endif /* OpenBSD and Bitrig. */ + +/* .. */ + +/* Mac OSX has __BIG_ENDIAN__ or __LITTLE_ENDIAN__ automatically set by the compiler (at least with GCC) */ +#if defined(__APPLE__) && defined(__MACH__) || defined(__ellcc__ ) + +#ifdef __BIG_ENDIAN__ +#if __BIG_ENDIAN__ +#define _YUGA_LITTLE_ENDIAN 0 +#define _YUGA_BIG_ENDIAN 1 +#endif +#endif /* __BIG_ENDIAN__ */ + +#ifdef __LITTLE_ENDIAN__ +#if __LITTLE_ENDIAN__ +#define _YUGA_LITTLE_ENDIAN 1 +#define _YUGA_BIG_ENDIAN 0 +#endif +#endif /* __LITTLE_ENDIAN__ */ + +#endif /* Mac OSX */ + +/* .. */ + +#if defined(__linux__) +#include <endian.h> + +#if __BYTE_ORDER == __BIG_ENDIAN +#define _YUGA_LITTLE_ENDIAN 0 +#define _YUGA_BIG_ENDIAN 1 +#elif __BYTE_ORDER == __LITTLE_ENDIAN +#define _YUGA_LITTLE_ENDIAN 1 +#define _YUGA_BIG_ENDIAN 0 +#endif /* __BYTE_ORDER */ + +#endif /* GNU/Linux */ + +#if defined(_WIN32) + +#define _YUGA_LITTLE_ENDIAN 1 +#define _YUGA_BIG_ENDIAN 0 + +#endif /* Windows */ + +#if defined(EMSCRIPTEN) + +#define _YUGA_LITTLE_ENDIAN 1 +#define _YUGA_BIG_ENDIAN 0 + +#endif /* emscripten */ + +/* . */ + +#if !defined(_YUGA_LITTLE_ENDIAN) || !defined(_YUGA_BIG_ENDIAN) +#error Unable to determine endian +#endif /* Check we found an endianness correctly. */ + +#endif /* INT_ENDIANNESS_H */ diff --git a/system/lib/compiler-rt/int_lib.h b/system/lib/compiler-rt/int_lib.h new file mode 100644 index 00000000..a87426c5 --- /dev/null +++ b/system/lib/compiler-rt/int_lib.h @@ -0,0 +1,46 @@ +/* ===-- int_lib.h - configuration header for compiler-rt -----------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file is a configuration header for compiler-rt. + * This file is not part of the interface of this library. + * + * ===----------------------------------------------------------------------=== + */ + +#ifndef INT_LIB_H +#define INT_LIB_H + +/* Assumption: Signed integral is 2's complement. */ +/* Assumption: Right shift of signed negative is arithmetic shift. */ +/* Assumption: Endianness is little or big (not mixed). */ + +/* ABI macro definitions */ + +#if __ARM_EABI__ +# define ARM_EABI_FNALIAS(aeabi_name, name) \ + void __aeabi_##aeabi_name() __attribute__((alias("__" #name))); +# define COMPILER_RT_ABI __attribute__((pcs("aapcs"))) +#else +# define ARM_EABI_FNALIAS(aeabi_name, name) +# define COMPILER_RT_ABI +#endif + +/* Include the standard compiler builtin headers we use functionality from. */ +#include <limits.h> +#include <stdint.h> +#include <stdbool.h> +#include <float.h> + +/* Include the commonly used internal type definitions. */ +#include "int_types.h" + +/* Include internal utility function declarations. */ +#include "int_util.h" + +#endif /* INT_LIB_H */ diff --git a/system/lib/compiler-rt/int_math.h b/system/lib/compiler-rt/int_math.h new file mode 100644 index 00000000..d6b4bdae --- /dev/null +++ b/system/lib/compiler-rt/int_math.h @@ -0,0 +1,67 @@ +/* ===-- int_math.h - internal math inlines ---------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===-----------------------------------------------------------------------=== + * + * This file is not part of the interface of this library. + * + * This file defines substitutes for the libm functions used in some of the + * compiler-rt implementations, defined in such a way that there is not a direct + * dependency on libm or math.h. Instead, we use the compiler builtin versions + * where available. This reduces our dependencies on the system SDK by foisting + * the responsibility onto the compiler. + * + * ===-----------------------------------------------------------------------=== + */ + +#ifndef INT_MATH_H +#define INT_MATH_H + +#ifndef __has_builtin +# define __has_builtin(x) 0 +#endif + +#define CRT_INFINITY __builtin_huge_valf() + +#define crt_isinf(x) __builtin_isinf((x)) +#define crt_isnan(x) __builtin_isnan((x)) + +/* Define crt_isfinite in terms of the builtin if available, otherwise provide + * an alternate version in terms of our other functions. This supports some + * versions of GCC which didn't have __builtin_isfinite. + */ +#if __has_builtin(__builtin_isfinite) +# define crt_isfinite(x) __builtin_isfinite((x)) +#else +# define crt_isfinite(x) \ + __extension__(({ \ + __typeof((x)) x_ = (x); \ + !crt_isinf(x_) && !crt_isnan(x_); \ + })) +#endif + +#define crt_copysign(x, y) __builtin_copysign((x), (y)) +#define crt_copysignf(x, y) __builtin_copysignf((x), (y)) +#define crt_copysignl(x, y) __builtin_copysignl((x), (y)) + +#define crt_fabs(x) __builtin_fabs((x)) +#define crt_fabsf(x) __builtin_fabsf((x)) +#define crt_fabsl(x) __builtin_fabsl((x)) + +#define crt_fmax(x, y) __builtin_fmax((x), (y)) +#define crt_fmaxf(x, y) __builtin_fmaxf((x), (y)) +#define crt_fmaxl(x, y) __builtin_fmaxl((x), (y)) + +#define crt_logb(x) __builtin_logb((x)) +#define crt_logbf(x) __builtin_logbf((x)) +#define crt_logbl(x) __builtin_logbl((x)) + +#define crt_scalbn(x, y) __builtin_scalbn((x), (y)) +#define crt_scalbnf(x, y) __builtin_scalbnf((x), (y)) +#define crt_scalbnl(x, y) __builtin_scalbnl((x), (y)) + +#endif /* INT_MATH_H */ diff --git a/system/lib/compiler-rt/int_types.h b/system/lib/compiler-rt/int_types.h new file mode 100644 index 00000000..fcce390f --- /dev/null +++ b/system/lib/compiler-rt/int_types.h @@ -0,0 +1,140 @@ +/* ===-- int_lib.h - configuration header for compiler-rt -----------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file is not part of the interface of this library. + * + * This file defines various standard types, most importantly a number of unions + * used to access parts of larger types. + * + * ===----------------------------------------------------------------------=== + */ + +#ifndef INT_TYPES_H +#define INT_TYPES_H + +#include "int_endianness.h" + +typedef int si_int; +typedef unsigned su_int; + +typedef long long di_int; +typedef unsigned long long du_int; + +typedef union +{ + di_int all; + struct + { +#if _YUGA_LITTLE_ENDIAN + su_int low; + si_int high; +#else + si_int high; + su_int low; +#endif /* _YUGA_LITTLE_ENDIAN */ + }s; +} dwords; + +typedef union +{ + du_int all; + struct + { +#if _YUGA_LITTLE_ENDIAN + su_int low; + su_int high; +#else + su_int high; + su_int low; +#endif /* _YUGA_LITTLE_ENDIAN */ + }s; +} udwords; + +#if __x86_64 + +typedef int ti_int __attribute__ ((mode (TI))); +typedef unsigned tu_int __attribute__ ((mode (TI))); + +typedef union +{ + ti_int all; + struct + { +#if _YUGA_LITTLE_ENDIAN + du_int low; + di_int high; +#else + di_int high; + du_int low; +#endif /* _YUGA_LITTLE_ENDIAN */ + }s; +} twords; + +typedef union +{ + tu_int all; + struct + { +#if _YUGA_LITTLE_ENDIAN + du_int low; + du_int high; +#else + du_int high; + du_int low; +#endif /* _YUGA_LITTLE_ENDIAN */ + }s; +} utwords; + +static inline ti_int make_ti(di_int h, di_int l) { + twords r; + r.s.high = h; + r.s.low = l; + return r.all; +} + +static inline tu_int make_tu(du_int h, du_int l) { + utwords r; + r.s.high = h; + r.s.low = l; + return r.all; +} + +#endif /* __x86_64 */ + +typedef union +{ + su_int u; + float f; +} float_bits; + +typedef union +{ + udwords u; + double f; +} double_bits; + +typedef struct +{ +#if _YUGA_LITTLE_ENDIAN + udwords low; + udwords high; +#else + udwords high; + udwords low; +#endif /* _YUGA_LITTLE_ENDIAN */ +} uqwords; + +typedef union +{ + uqwords u; + long double f; +} long_double_bits; + +#endif /* INT_TYPES_H */ + diff --git a/system/lib/compiler-rt/int_util.h b/system/lib/compiler-rt/int_util.h new file mode 100644 index 00000000..1348b85e --- /dev/null +++ b/system/lib/compiler-rt/int_util.h @@ -0,0 +1,29 @@ +/* ===-- int_util.h - internal utility functions ----------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===-----------------------------------------------------------------------=== + * + * This file is not part of the interface of this library. + * + * This file defines non-inline utilities which are available for use in the + * library. The function definitions themselves are all contained in int_util.c + * which will always be compiled into any compiler-rt library. + * + * ===-----------------------------------------------------------------------=== + */ + +#ifndef INT_UTIL_H +#define INT_UTIL_H + +/** \brief Trigger a program abort (or panic for kernel code). */ +#define compilerrt_abort() compilerrt_abort_impl(__FILE__, __LINE__, \ + __FUNCTION__) + +void compilerrt_abort_impl(const char *file, int line, + const char *function) __attribute__((noreturn)); + +#endif /* INT_UTIL_H */ diff --git a/system/lib/compiler-rt/muldi3.c b/system/lib/compiler-rt/muldi3.c new file mode 100644 index 00000000..2dae44c1 --- /dev/null +++ b/system/lib/compiler-rt/muldi3.c @@ -0,0 +1,56 @@ +/* ===-- muldi3.c - Implement __muldi3 -------------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __muldi3 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +/* Returns: a * b */ + +static +di_int +__muldsi3(su_int a, su_int b) +{ + dwords r; + const int bits_in_word_2 = (int)(sizeof(si_int) * CHAR_BIT) / 2; + const su_int lower_mask = (su_int)~0 >> bits_in_word_2; + r.s.low = (a & lower_mask) * (b & lower_mask); + su_int t = r.s.low >> bits_in_word_2; + r.s.low &= lower_mask; + t += (a >> bits_in_word_2) * (b & lower_mask); + r.s.low += (t & lower_mask) << bits_in_word_2; + r.s.high = t >> bits_in_word_2; + t = r.s.low >> bits_in_word_2; + r.s.low &= lower_mask; + t += (b >> bits_in_word_2) * (a & lower_mask); + r.s.low += (t & lower_mask) << bits_in_word_2; + r.s.high += t >> bits_in_word_2; + r.s.high += (a >> bits_in_word_2) * (b >> bits_in_word_2); + return r.all; +} + +/* Returns: a * b */ + +ARM_EABI_FNALIAS(lmul, muldi3) + +COMPILER_RT_ABI di_int +__muldi3(di_int a, di_int b) +{ + dwords x; + x.all = a; + dwords y; + y.all = b; + dwords r; + r.all = __muldsi3(x.s.low, y.s.low); + r.s.high += x.s.high * y.s.low + x.s.low * y.s.high; + return r.all; +} diff --git a/system/lib/compiler-rt/readme.txt b/system/lib/compiler-rt/readme.txt new file mode 100644 index 00000000..d10f53e4 --- /dev/null +++ b/system/lib/compiler-rt/readme.txt @@ -0,0 +1,20 @@ +These files are from compiler-rt, + +Last Changed Rev: 179380 +Last Changed Date: 2013-04-12 07:57:03 -0700 (Fri, 12 Apr 2013) + +=========================================================================== + +Changes: + + * add emscripten endianness to int_endianness.h + * add rem functions + +=========================================================================== + +Compile with something like + +./emcc system/lib/compiler-rt/*.c -Isystem/lib/compiler-rt/ -o rt.bc +./emcc -O2 -s ASM_JS=1 -g rt.bc -s LINKABLE=1 +manually replace Math_imul with Math.imul + diff --git a/system/lib/compiler-rt/udivdi3.c b/system/lib/compiler-rt/udivdi3.c new file mode 100644 index 00000000..3d55785c --- /dev/null +++ b/system/lib/compiler-rt/udivdi3.c @@ -0,0 +1,36 @@ +/* ===-- udivdi3.c - Implement __udivdi3 -----------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __udivdi3 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +du_int COMPILER_RT_ABI __udivmoddi4(du_int a, du_int b, du_int* rem); + +/* Returns: a / b */ + +COMPILER_RT_ABI du_int +__udivdi3(du_int a, du_int b) +{ + return __udivmoddi4(a, b, 0); +} + +/* XXX EMSCRIPTEN */ + +COMPILER_RT_ABI du_int +__uremdi3(du_int a, du_int b) +{ + du_int rem; + __udivmoddi4(a, b, &rem); + return rem; +} + diff --git a/system/lib/compiler-rt/udivmoddi4.c b/system/lib/compiler-rt/udivmoddi4.c new file mode 100644 index 00000000..57282d5b --- /dev/null +++ b/system/lib/compiler-rt/udivmoddi4.c @@ -0,0 +1,251 @@ +/* ===-- udivmoddi4.c - Implement __udivmoddi4 -----------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __udivmoddi4 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +/* Effects: if rem != 0, *rem = a % b + * Returns: a / b + */ + +/* Translated from Figure 3-40 of The PowerPC Compiler Writer's Guide */ + +COMPILER_RT_ABI du_int +__udivmoddi4(du_int a, du_int b, du_int* rem) +{ + const unsigned n_uword_bits = sizeof(su_int) * CHAR_BIT; + const unsigned n_udword_bits = sizeof(du_int) * CHAR_BIT; + udwords n; + n.all = a; + udwords d; + d.all = b; + udwords q; + udwords r; + unsigned sr; + /* special cases, X is unknown, K != 0 */ + if (n.s.high == 0) + { + if (d.s.high == 0) + { + /* 0 X + * --- + * 0 X + */ + if (rem) + *rem = n.s.low % d.s.low; + return n.s.low / d.s.low; + } + /* 0 X + * --- + * K X + */ + if (rem) + *rem = n.s.low; + return 0; + } + /* n.s.high != 0 */ + if (d.s.low == 0) + { + if (d.s.high == 0) + { + /* K X + * --- + * 0 0 + */ + if (rem) + *rem = n.s.high % d.s.low; + return n.s.high / d.s.low; + } + /* d.s.high != 0 */ + if (n.s.low == 0) + { + /* K 0 + * --- + * K 0 + */ + if (rem) + { + r.s.high = n.s.high % d.s.high; + r.s.low = 0; + *rem = r.all; + } + return n.s.high / d.s.high; + } + /* K K + * --- + * K 0 + */ + if ((d.s.high & (d.s.high - 1)) == 0) /* if d is a power of 2 */ + { + if (rem) + { + r.s.low = n.s.low; + r.s.high = n.s.high & (d.s.high - 1); + *rem = r.all; + } + return n.s.high >> __builtin_ctz(d.s.high); + } + /* K K + * --- + * K 0 + */ + sr = __builtin_clz(d.s.high) - __builtin_clz(n.s.high); + /* 0 <= sr <= n_uword_bits - 2 or sr large */ + if (sr > n_uword_bits - 2) + { + if (rem) + *rem = n.all; + return 0; + } + ++sr; + /* 1 <= sr <= n_uword_bits - 1 */ + /* q.all = n.all << (n_udword_bits - sr); */ + q.s.low = 0; + q.s.high = n.s.low << (n_uword_bits - sr); + /* r.all = n.all >> sr; */ + r.s.high = n.s.high >> sr; + r.s.low = (n.s.high << (n_uword_bits - sr)) | (n.s.low >> sr); + } + else /* d.s.low != 0 */ + { + if (d.s.high == 0) + { + /* K X + * --- + * 0 K + */ + if ((d.s.low & (d.s.low - 1)) == 0) /* if d is a power of 2 */ + { + if (rem) + *rem = n.s.low & (d.s.low - 1); + if (d.s.low == 1) + return n.all; + sr = __builtin_ctz(d.s.low); + q.s.high = n.s.high >> sr; + q.s.low = (n.s.high << (n_uword_bits - sr)) | (n.s.low >> sr); + return q.all; + } + /* K X + * --- + *0 K + */ + sr = 1 + n_uword_bits + __builtin_clz(d.s.low) - __builtin_clz(n.s.high); + /* 2 <= sr <= n_udword_bits - 1 + * q.all = n.all << (n_udword_bits - sr); + * r.all = n.all >> sr; + * if (sr == n_uword_bits) + * { + * q.s.low = 0; + * q.s.high = n.s.low; + * r.s.high = 0; + * r.s.low = n.s.high; + * } + * else if (sr < n_uword_bits) // 2 <= sr <= n_uword_bits - 1 + * { + * q.s.low = 0; + * q.s.high = n.s.low << (n_uword_bits - sr); + * r.s.high = n.s.high >> sr; + * r.s.low = (n.s.high << (n_uword_bits - sr)) | (n.s.low >> sr); + * } + * else // n_uword_bits + 1 <= sr <= n_udword_bits - 1 + * { + * q.s.low = n.s.low << (n_udword_bits - sr); + * q.s.high = (n.s.high << (n_udword_bits - sr)) | + * (n.s.low >> (sr - n_uword_bits)); + * r.s.high = 0; + * r.s.low = n.s.high >> (sr - n_uword_bits); + * } + */ + q.s.low = (n.s.low << (n_udword_bits - sr)) & + ((si_int)(n_uword_bits - sr) >> (n_uword_bits-1)); + q.s.high = ((n.s.low << ( n_uword_bits - sr)) & + ((si_int)(sr - n_uword_bits - 1) >> (n_uword_bits-1))) | + (((n.s.high << (n_udword_bits - sr)) | + (n.s.low >> (sr - n_uword_bits))) & + ((si_int)(n_uword_bits - sr) >> (n_uword_bits-1))); + r.s.high = (n.s.high >> sr) & + ((si_int)(sr - n_uword_bits) >> (n_uword_bits-1)); + r.s.low = ((n.s.high >> (sr - n_uword_bits)) & + ((si_int)(n_uword_bits - sr - 1) >> (n_uword_bits-1))) | + (((n.s.high << (n_uword_bits - sr)) | + (n.s.low >> sr)) & + ((si_int)(sr - n_uword_bits) >> (n_uword_bits-1))); + } + else + { + /* K X + * --- + * K K + */ + sr = __builtin_clz(d.s.high) - __builtin_clz(n.s.high); + /* 0 <= sr <= n_uword_bits - 1 or sr large */ + if (sr > n_uword_bits - 1) + { + if (rem) + *rem = n.all; + return 0; + } + ++sr; + /* 1 <= sr <= n_uword_bits */ + /* q.all = n.all << (n_udword_bits - sr); */ + q.s.low = 0; + q.s.high = n.s.low << (n_uword_bits - sr); + /* r.all = n.all >> sr; + * if (sr < n_uword_bits) + * { + * r.s.high = n.s.high >> sr; + * r.s.low = (n.s.high << (n_uword_bits - sr)) | (n.s.low >> sr); + * } + * else + * { + * r.s.high = 0; + * r.s.low = n.s.high; + * } + */ + r.s.high = (n.s.high >> sr) & + ((si_int)(sr - n_uword_bits) >> (n_uword_bits-1)); + r.s.low = (n.s.high << (n_uword_bits - sr)) | + ((n.s.low >> sr) & + ((si_int)(sr - n_uword_bits) >> (n_uword_bits-1))); + } + } + /* Not a special case + * q and r are initialized with: + * q.all = n.all << (n_udword_bits - sr); + * r.all = n.all >> sr; + * 1 <= sr <= n_udword_bits - 1 + */ + su_int carry = 0; + for (; sr > 0; --sr) + { + /* r:q = ((r:q) << 1) | carry */ + r.s.high = (r.s.high << 1) | (r.s.low >> (n_uword_bits - 1)); + r.s.low = (r.s.low << 1) | (q.s.high >> (n_uword_bits - 1)); + q.s.high = (q.s.high << 1) | (q.s.low >> (n_uword_bits - 1)); + q.s.low = (q.s.low << 1) | carry; + /* carry = 0; + * if (r.all >= d.all) + * { + * r.all -= d.all; + * carry = 1; + * } + */ + const di_int s = (di_int)(d.all - r.all - 1) >> (n_udword_bits - 1); + carry = s & 1; + r.all -= d.all & s; + } + q.all = (q.all << 1) | carry; + if (rem) + *rem = r.all; + return q.all; +} diff --git a/system/lib/embind/bind.cpp b/system/lib/embind/bind.cpp index b63a86aa..35d99dad 100755 --- a/system/lib/embind/bind.cpp +++ b/system/lib/embind/bind.cpp @@ -1,34 +1,80 @@ #include <emscripten/bind.h>
+#ifdef USE_CXA_DEMANGLE
+#include <../lib/libcxxabi/include/cxxabi.h>
+#endif
+#include <list>
+#include <vector>
+#include <typeinfo>
+#include <algorithm>
+#include <emscripten/emscripten.h>
+#include <climits>
using namespace emscripten;
-namespace emscripten {
- namespace internal {
- void registerStandardTypes() {
- static bool first = true;
- if (first) {
- first = false;
-
- _embind_register_void(getTypeID<void>(), "void");
-
- _embind_register_bool(getTypeID<bool>(), "bool", true, false);
-
- _embind_register_integer(getTypeID<char>(), "char");
- _embind_register_integer(getTypeID<signed char>(), "signed char");
- _embind_register_integer(getTypeID<unsigned char>(), "unsigned char");
- _embind_register_integer(getTypeID<signed short>(), "short");
- _embind_register_integer(getTypeID<unsigned short>(), "unsigned short");
- _embind_register_integer(getTypeID<signed int>(), "int");
- _embind_register_integer(getTypeID<unsigned int>(), "unsigned int");
- _embind_register_integer(getTypeID<signed long>(), "long");
- _embind_register_integer(getTypeID<unsigned long>(), "unsigned long");
-
- _embind_register_float(getTypeID<float>(), "float");
- _embind_register_float(getTypeID<double>(), "double");
-
- _embind_register_cstring(getTypeID<std::string>(), "std::string");
- _embind_register_emval(getTypeID<val>(), "emscripten::val");
- }
+extern "C" {
+ const char* __attribute__((used)) __getTypeName(const std::type_info* ti) {
+#ifdef USE_CXA_DEMANGLE
+ int stat;
+ char* demangled = abi::__cxa_demangle(ti->name(), NULL, NULL, &stat);
+ if (stat == 0 && demangled) {
+ return demangled;
}
+
+ switch (stat) {
+ case -1:
+ return strdup("<allocation failure>");
+ case -2:
+ return strdup("<invalid C++ symbol>");
+ case -3:
+ return strdup("<invalid argument>");
+ default:
+ return strdup("<unknown error>");
+ }
+#else
+ return strdup(ti->name());
+#endif
+ }
+}
+
+// TODO: fix in library.js or a proper emscripten libc
+extern "C" wchar_t *wmemset(wchar_t *dest, wchar_t c, size_t count) {
+ wchar_t *o = dest;
+ while (count--) {
+ *o++ = c;
+ }
+ return dest;
+}
+
+// TODO: fix in library.js or a proper emscripten libc
+extern "C" wchar_t *wmemcpy(wchar_t *dest, const wchar_t *src, size_t count) {
+ wchar_t *o = dest;
+ while (count--) {
+ *dest++ = *src++;
}
+ return dest;
+}
+
+EMSCRIPTEN_BINDINGS(native_and_builtin_types) {
+ using namespace emscripten::internal;
+
+ _embind_register_void(TypeID<void>::get(), "void");
+
+ _embind_register_bool(TypeID<bool>::get(), "bool", true, false);
+
+ _embind_register_integer(TypeID<char>::get(), "char", CHAR_MIN, CHAR_MAX);
+ _embind_register_integer(TypeID<signed char>::get(), "signed char", SCHAR_MIN, SCHAR_MAX);
+ _embind_register_integer(TypeID<unsigned char>::get(), "unsigned char", 0, UCHAR_MAX);
+ _embind_register_integer(TypeID<signed short>::get(), "short", SHRT_MIN, SHRT_MAX);
+ _embind_register_integer(TypeID<unsigned short>::get(), "unsigned short", 0, USHRT_MAX);
+ _embind_register_integer(TypeID<signed int>::get(), "int", INT_MIN, INT_MAX);
+ _embind_register_integer(TypeID<unsigned int>::get(), "unsigned int", 0, UINT_MAX);
+ _embind_register_integer(TypeID<signed long>::get(), "long", LONG_MIN, LONG_MAX);
+ _embind_register_integer(TypeID<unsigned long>::get(), "unsigned long", 0, ULONG_MAX);
+
+ _embind_register_float(TypeID<float>::get(), "float");
+ _embind_register_float(TypeID<double>::get(), "double");
+
+ _embind_register_std_string(TypeID<std::string>::get(), "std::string");
+ _embind_register_std_wstring(TypeID<std::wstring>::get(), sizeof(wchar_t), "std::wstring");
+ _embind_register_emval(TypeID<val>::get(), "emscripten::val");
}
diff --git a/tests/cases/call_i64_noret.ll b/tests/cases/call_i64_noret.ll new file mode 100644 index 00000000..a8a30fc0 --- /dev/null +++ b/tests/cases/call_i64_noret.ll @@ -0,0 +1,17 @@ +; ModuleID = 'tests/hello_world.bc' +target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32-n8:16:32-S128" +target triple = "i386-pc-linux-gnu" + +@.str = private unnamed_addr constant [15 x i8] c"hello, world!\0A\00", align 1 ; [#uses=1 type=[15 x i8]*] + +; [#uses=0] +define i32 @main() { +entry: + %retval = alloca i32, align 4 ; [#uses=1 type=i32*] + store i32 0, i32* %retval + call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([15 x i8]* @.str, i32 0, i32 0), i64 0) ; [#uses=0 type=i32] + ret i32 1 +} + +; [#uses=1] +declare i32 @printf(i8*, ...) diff --git a/tests/cases/legalizer_ta2.ll b/tests/cases/legalizer_ta2.ll index 7e17c707..89ebcef6 100644 --- a/tests/cases/legalizer_ta2.ll +++ b/tests/cases/legalizer_ta2.ll @@ -188,4 +188,5 @@ done: declare i32 @puts(i8*) declare i32 @__gxx_personality_v0(...) +declare void @__cxa_throw(i32, i32, i32) ; for asm1, where exceptions are enabled but this test needs a throw to bring in lib stuff diff --git a/tests/cases/longjmp_tiny_noasm.ll b/tests/cases/longjmp_tiny.ll index 0045847c..0045847c 100644 --- a/tests/cases/longjmp_tiny_noasm.ll +++ b/tests/cases/longjmp_tiny.ll diff --git a/tests/cases/longjmp_tiny_noasm.txt b/tests/cases/longjmp_tiny.txt index 8a0aa386..8a0aa386 100644 --- a/tests/cases/longjmp_tiny_noasm.txt +++ b/tests/cases/longjmp_tiny.txt diff --git a/tests/cases/longjmp_tiny_noasm_invoke.ll b/tests/cases/longjmp_tiny_invoke.ll index e1a72e00..e1a72e00 100644 --- a/tests/cases/longjmp_tiny_noasm_invoke.ll +++ b/tests/cases/longjmp_tiny_invoke.ll diff --git a/tests/cases/longjmp_tiny_noasm_invoke.txt b/tests/cases/longjmp_tiny_invoke.txt index 8a0aa386..8a0aa386 100644 --- a/tests/cases/longjmp_tiny_noasm_invoke.txt +++ b/tests/cases/longjmp_tiny_invoke.txt diff --git a/tests/cases/longjmp_tiny_phi_noasm.ll b/tests/cases/longjmp_tiny_phi.ll index cced7cab..cced7cab 100644 --- a/tests/cases/longjmp_tiny_phi_noasm.ll +++ b/tests/cases/longjmp_tiny_phi.ll diff --git a/tests/cases/longjmp_tiny_phi_noasm.txt b/tests/cases/longjmp_tiny_phi.txt index 16f5a93e..16f5a93e 100644 --- a/tests/cases/longjmp_tiny_phi_noasm.txt +++ b/tests/cases/longjmp_tiny_phi.txt diff --git a/tests/cases/longjmp_tiny_phi2_noasm.ll b/tests/cases/longjmp_tiny_phi2.ll index 1d7761c3..1d7761c3 100644 --- a/tests/cases/longjmp_tiny_phi2_noasm.ll +++ b/tests/cases/longjmp_tiny_phi2.ll diff --git a/tests/cases/longjmp_tiny_phi2_noasm.txt b/tests/cases/longjmp_tiny_phi2.txt index 37e85737..37e85737 100644 --- a/tests/cases/longjmp_tiny_phi2_noasm.txt +++ b/tests/cases/longjmp_tiny_phi2.txt diff --git a/tests/cases/uadd_overflow_64_ta2.ll b/tests/cases/uadd_overflow_64_ta2.ll new file mode 100644 index 00000000..a4f3e40b --- /dev/null +++ b/tests/cases/uadd_overflow_64_ta2.ll @@ -0,0 +1,30 @@ +; ModuleID = 'tests/hello_world.bc' +target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32-n8:16:32-S128" +target triple = "i386-pc-linux-gnu" + +@.str2 = private constant [11 x i8] c"*%llx,%d*\0A\00", align 1 ; [#uses=1] + +; [#uses=0] +define i32 @main() { +entry: + %uadd1 = tail call { i64, i1 } @llvm.uadd.with.overflow.i64(i64 18446744073709551606, i64 9999) + %a0 = extractvalue { i64, i1 } %uadd1, 0 + %a1 = extractvalue { i64, i1 } %uadd1, 1 + %a2 = zext i1 %a1 to i32 + call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([11 x i8]* @.str2, i32 0, i32 0), i64 %a0, i32 %a2) ; [#uses=0] + + %uadd2 = tail call { i64, i1 } @llvm.uadd.with.overflow.i64(i64 18446744073709, i64 9999) + %b0 = extractvalue { i64, i1 } %uadd2, 0 + %b1 = extractvalue { i64, i1 } %uadd2, 1 + %b2 = zext i1 %b1 to i32 + call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([11 x i8]* @.str2, i32 0, i32 0), i64 %b0, i32 %b2) ; [#uses=0] + + ret i32 1 +} + +; [#uses=1] +declare i32 @printf(i8*, ...) + +declare { i32, i1 } @llvm.uadd.with.overflow.i32(i32, i32) nounwind readnone +declare { i64, i1 } @llvm.uadd.with.overflow.i64(i64, i64) nounwind readnone + diff --git a/tests/cases/uadd_overflow_64_ta2.txt b/tests/cases/uadd_overflow_64_ta2.txt new file mode 100644 index 00000000..f5711309 --- /dev/null +++ b/tests/cases/uadd_overflow_64_ta2.txt @@ -0,0 +1,2 @@ +*2705,1* +*10c6f7a0dcfc,0* diff --git a/tests/embind/embind.benchmark.js b/tests/embind/embind.benchmark.js new file mode 100644 index 00000000..4ce9355c --- /dev/null +++ b/tests/embind/embind.benchmark.js @@ -0,0 +1,201 @@ +function _increment_counter_benchmark_js(N) { + var ctr = _get_counter(); + var a = _emscripten_get_now(); + for(i = 0; i < N; ++i) { + _increment_counter(); + _increment_counter(); + _increment_counter(); + _increment_counter(); + _increment_counter(); + _increment_counter(); + _increment_counter(); + _increment_counter(); + _increment_counter(); + _increment_counter(); + } + var b = _emscripten_get_now(); + var ctr2 = _get_counter(); + Module.print("JS increment_counter " + N + " iters: " + (b-a) + " msecs. result: " + (ctr2-ctr)); +} + +function _increment_class_counter_benchmark_embind_js(N) { + var foo = new Module['Foo'](); + var a = _emscripten_get_now(); + for(i = 0; i < N; ++i) { + foo['incr_class_counter'](); + foo['incr_class_counter'](); + foo['incr_class_counter'](); + foo['incr_class_counter'](); + foo['incr_class_counter'](); + foo['incr_class_counter'](); + foo['incr_class_counter'](); + foo['incr_class_counter'](); + foo['incr_class_counter'](); + foo['incr_class_counter'](); + } + var b = _emscripten_get_now(); + Module.print("JS embind increment_class_counter " + N + " iters: " + (b-a) + " msecs. result: " + foo['class_counter_val']()); + foo['delete'](); +} + +function _returns_input_benchmark_js() { + var a = _emscripten_get_now(); + var t = 0; + for(i = 0; i < 100000; ++i) { + t += _returns_input(i); + t += _returns_input(i); + t += _returns_input(i); + t += _returns_input(i); + t += _returns_input(i); + t += _returns_input(i); + t += _returns_input(i); + t += _returns_input(i); + t += _returns_input(i); + t += _returns_input(i); + } + var b = _emscripten_get_now(); + Module.print("JS returns_input 100000 iters: " + (b-a) + " msecs. result: " + t); +} + +function _sum_int_benchmark_js() { + var a = _emscripten_get_now(); + var r = 0; + for(i = 0; i < 100000; ++i) { + r += _sum_int(i, 2, 3, 4, 5, 6, 7, 8, 9); + r += _sum_int(i, 2, 3, 4, 5, 6, 7, 8, 9); + r += _sum_int(i, 2, 3, 4, 5, 6, 7, 8, 9); + r += _sum_int(i, 2, 3, 4, 5, 6, 7, 8, 9); + r += _sum_int(i, 2, 3, 4, 5, 6, 7, 8, 9); + r += _sum_int(i, 2, 3, 4, 5, 6, 7, 8, 9); + r += _sum_int(i, 2, 3, 4, 5, 6, 7, 8, 9); + r += _sum_int(i, 2, 3, 4, 5, 6, 7, 8, 9); + r += _sum_int(i, 2, 3, 4, 5, 6, 7, 8, 9); + r += _sum_int(i, 2, 3, 4, 5, 6, 7, 8, 9); + } + var b = _emscripten_get_now(); + Module.print("JS sum_int 100000 iters: " + (b-a) + " msecs. result: " + r); +} + +function _sum_float_benchmark_js() { + var a = _emscripten_get_now(); + var r = 0; + for(i = 0; i < 100000; ++i) { + r += _sum_float(i, 2, 3, 4, 5, 6, 7, 8, 9); + r += _sum_float(i, 2, 3, 4, 5, 6, 7, 8, 9); + r += _sum_float(i, 2, 3, 4, 5, 6, 7, 8, 9); + r += _sum_float(i, 2, 3, 4, 5, 6, 7, 8, 9); + r += _sum_float(i, 2, 3, 4, 5, 6, 7, 8, 9); + r += _sum_float(i, 2, 3, 4, 5, 6, 7, 8, 9); + r += _sum_float(i, 2, 3, 4, 5, 6, 7, 8, 9); + r += _sum_float(i, 2, 3, 4, 5, 6, 7, 8, 9); + r += _sum_float(i, 2, 3, 4, 5, 6, 7, 8, 9); + r += _sum_float(i, 2, 3, 4, 5, 6, 7, 8, 9); + } + var b = _emscripten_get_now(); + Module.print("JS sum_float 100000 iters: " + (b-a) + " msecs. result: " + r); +} + +function _increment_counter_benchmark_embind_js(N) { + var ctr = _get_counter(); + var a = _emscripten_get_now(); + for(i = 0; i < N; ++i) { + Module['increment_counter'](); + Module['increment_counter'](); + Module['increment_counter'](); + Module['increment_counter'](); + Module['increment_counter'](); + Module['increment_counter'](); + Module['increment_counter'](); + Module['increment_counter'](); + Module['increment_counter'](); + Module['increment_counter'](); + } + var b = _emscripten_get_now(); + var ctr2 = _get_counter(); + Module.print("JS embind increment_counter " + N + " iters: " + (b-a) + " msecs. result: " + (ctr2-ctr)); +} + +function _returns_input_benchmark_embind_js() { + var a = _emscripten_get_now(); + var t = 0; + for(i = 0; i < 100000; ++i) { + t += Module['returns_input'](i); + t += Module['returns_input'](i); + t += Module['returns_input'](i); + t += Module['returns_input'](i); + t += Module['returns_input'](i); + t += Module['returns_input'](i); + t += Module['returns_input'](i); + t += Module['returns_input'](i); + t += Module['returns_input'](i); + t += Module['returns_input'](i); + } + var b = _emscripten_get_now(); + Module.print("JS embind returns_input 100000 iters: " + (b-a) + " msecs. result: " + t); +} + +function _sum_int_benchmark_embind_js() { + var a = _emscripten_get_now(); + var r = 0; + for(i = 0; i < 100000; ++i) { + r += Module['sum_int'](i, 2, 3, 4, 5, 6, 7, 8, 9); + r += Module['sum_int'](i, 2, 3, 4, 5, 6, 7, 8, 9); + r += Module['sum_int'](i, 2, 3, 4, 5, 6, 7, 8, 9); + r += Module['sum_int'](i, 2, 3, 4, 5, 6, 7, 8, 9); + r += Module['sum_int'](i, 2, 3, 4, 5, 6, 7, 8, 9); + r += Module['sum_int'](i, 2, 3, 4, 5, 6, 7, 8, 9); + r += Module['sum_int'](i, 2, 3, 4, 5, 6, 7, 8, 9); + r += Module['sum_int'](i, 2, 3, 4, 5, 6, 7, 8, 9); + r += Module['sum_int'](i, 2, 3, 4, 5, 6, 7, 8, 9); + r += Module['sum_int'](i, 2, 3, 4, 5, 6, 7, 8, 9); + } + var b = _emscripten_get_now(); + Module.print("JS embind sum_int 100000 iters: " + (b-a) + " msecs. result: " + r); +} + +function _sum_float_benchmark_embind_js() { + var a = _emscripten_get_now(); + var r = 0; + for(i = 0; i < 100000; ++i) { + r += Module['sum_float'](i, 2, 3, 4, 5, 6, 7, 8, 9); + r += Module['sum_float'](i, 2, 3, 4, 5, 6, 7, 8, 9); + r += Module['sum_float'](i, 2, 3, 4, 5, 6, 7, 8, 9); + r += Module['sum_float'](i, 2, 3, 4, 5, 6, 7, 8, 9); + r += Module['sum_float'](i, 2, 3, 4, 5, 6, 7, 8, 9); + r += Module['sum_float'](i, 2, 3, 4, 5, 6, 7, 8, 9); + r += Module['sum_float'](i, 2, 3, 4, 5, 6, 7, 8, 9); + r += Module['sum_float'](i, 2, 3, 4, 5, 6, 7, 8, 9); + r += Module['sum_float'](i, 2, 3, 4, 5, 6, 7, 8, 9); + r += Module['sum_float'](i, 2, 3, 4, 5, 6, 7, 8, 9); + } + var b = _emscripten_get_now(); + Module.print("JS embind sum_float 100000 iters: " + (b-a) + " msecs. result: " + r); +} + +function _move_gameobjects_benchmark_embind_js() { + var N = 10000; + var objects = []; + for(i = 0; i < N; ++i) { + objects.push(Module['create_game_object']()); + } + + var a = _emscripten_get_now(); + for(i = 0; i < N; ++i) { + var t = objects[i]['GetTransform'](); + var pos = Module['add'](t['GetPosition'](), [2, 0, 1]); + var rot = Module['add'](t['GetRotation'](), [0.1, 0.2, 0.3]); + t['SetPosition'](pos); + t['SetRotation'](rot); + t['delete'](); + } + var b = _emscripten_get_now(); + + var accum = [0,0,0]; + for(i = 0; i < N; ++i) { + var t = objects[i]['GetTransform'](); + accum = Module['add'](Module['add'](accum, t['GetPosition']()), t['GetRotation']()); + t['delete'](); + } + + Module.print("JS embind move_gameobjects " + N + " iters: " + (b-a) + " msecs. Result: " + (accum[0] + accum[1] + accum[2])); +} diff --git a/tests/embind/embind.test.js b/tests/embind/embind.test.js new file mode 100755 index 00000000..8ef46ad8 --- /dev/null +++ b/tests/embind/embind.test.js @@ -0,0 +1,1691 @@ +module({ + Emscripten: '../../../../build/embind_test.js', +}, function(imports) { + var cm = imports.Emscripten; + + var CheckForLeaks = fixture("check for leaks", function() { + this.setUp(function() { + if (typeof INVOKED_FROM_EMSCRIPTEN_TEST_RUNNER === "undefined") { // TODO: Enable this to work in Emscripten runner as well! + cm._mallocDebug(2); + assert.equal(0, cm.count_emval_handles()); + cm._mallocAssertAllMemoryFree(); + } + }); + this.tearDown(function() { + if (typeof INVOKED_FROM_EMSCRIPTEN_TEST_RUNNER === "undefined") { // TODO: Enable this to work in Emscripten runner as well! + cm._mallocAssertAllMemoryFree(); + assert.equal(0, cm.count_emval_handles()); + } + }); + }); + + var BaseFixture = CheckForLeaks; + + BaseFixture.extend("temp jig", function() { + test("temp test", function() { + }); + }); + + if (typeof INVOKED_FROM_EMSCRIPTEN_TEST_RUNNER === "undefined") { // TODO: Enable this to work in Emscripten runner as well! + + BaseFixture.extend("leak testing", function() { + test("no memory allocated at start of test", function() { + cm._mallocAssertAllMemoryFree(); + }); + test("assert when memory is allocated", function() { + var ptr = cm._malloc(42); + assert.throws(cm._MemoryAllocationError, function() { + cm._mallocAssertAllMemoryFree(); + }); + cm._free(ptr); + }); + test("allocated memory counts down again for free", function() { + var ptr = cm._malloc(42); + cm._free(ptr); + cm._mallocAssertAllMemoryFree(); + }); + test("free without malloc throws MemoryAllocationError", function() { + var ptr = cm._malloc(42); + cm._free(ptr); + assert.throws(cm._MemoryAllocationError, function() { + cm._free(ptr); + }); + }); + }); + + } + + BaseFixture.extend("access to base class members", function() { + test("method name in derived class silently overrides inherited name", function() { + var derived = new cm.Derived(); + assert.equal("Derived", derived.getClassName()); + derived.delete(); + }); + test("can reference base method from derived class", function(){ + var derived = new cm.Derived(); + assert.equal("Base", derived.getClassNameFromBase()); + derived.delete(); + }); + test("can reference base method from doubly derived class", function() { + var derivedTwice = new cm.DerivedTwice(); + assert.equal("Base", derivedTwice.getClassNameFromBase()); + derivedTwice.delete(); + }); + test("can reference base method through unbound classes", function() { + var derivedThrice = new cm.DerivedThrice(); + assert.equal("Base", derivedThrice.getClassNameFromBase()); + derivedThrice.delete(); + }); + test("property name in derived class hides identically named property in base class for set", function() { + var derived = new cm.Derived(); + derived.setMember(7); + + derived.member = 17; + + assert.equal(17, derived.getMember()); + derived.delete(); + }); + test("can reference base property from derived class for get", function(){ + var derived = new cm.Derived(); + derived.setBaseMember(5); + + assert.equal(5, derived.baseMember); + + derived.delete(); + }); + test("can reference property of any base class for get when multiply derived", function(){ + var derived = new cm.MultiplyDerived(); + derived.setBaseMember(11); + + assert.equal(11, derived.baseMember); + + derived.delete(); + }); + test("can reference base property from derived class for set", function(){ + var derived = new cm.Derived(); + + derived.baseMember = 32; + + assert.equal(32, derived.getBaseMember()); + derived.delete(); + }); + test("can reference property of any base for set when multiply derived", function(){ + var derived = new cm.MultiplyDerived(); + derived.setBaseMember(97); + + derived.baseMember = 32; + + assert.equal(32, derived.getBaseMember()); + derived.delete(); + }); + test("can reach around derived property to access base property with same name for get", function() { + var derived = new cm.Derived(); + derived.setMember(12); + derived.delete(); + }); + + test("if deriving from second base adjusts pointer", function() { + var derived = new cm.HasTwoBases; + assert.equal("Base2", derived.getField()); + derived.delete(); + }); + + test("properties adjust pointer", function() { + var derived = new cm.HasTwoBases; + derived.field = "Foo"; + assert.equal("Foo", derived.getField()); + assert.equal("Foo", derived.field); + derived.delete(); + }); + + test("calling method on unrelated class throws error", function() { + var a = new cm.HasTwoBases; + var e = assert.throws(cm.BindingError, function() { + cm.Derived.prototype.setMember.call(a, "foo"); + }); + assert.equal('Expected null or instance of Derived, got an instance of Base2', e.message); + a.delete(); + + // Base1 and Base2 both have the method 'getField()' exposed - make sure + // that calling the Base2 function with a 'this' instance of Base1 doesn't accidentally work! + var b = new cm.Base1; + var e = assert.throws(cm.BindingError, function() { + cm.Base2.prototype.getField.call(b); + }); + assert.equal('Expected null or instance of Base2, got an instance of Base1', e.message); + b.delete(); + }); + + test("calling method with invalid this throws error", function() { + var e = assert.throws(cm.BindingError, function() { + cm.Derived.prototype.setMember.call(undefined, "foo"); + }); + assert.equal('Cannot pass "[object global]" as a Derived*', e.message); + + var e = assert.throws(cm.BindingError, function() { + cm.Derived.prototype.setMember.call(true, "foo"); + }); + assert.equal('Cannot pass "true" as a Derived*', e.message); + + var e = assert.throws(cm.BindingError, function() { + cm.Derived.prototype.setMember.call(null, "foo"); + }); + assert.equal('Cannot pass "[object global]" as a Derived*', e.message); + + var e = assert.throws(cm.BindingError, function() { + cm.Derived.prototype.setMember.call(42, "foo"); + }); + assert.equal('Cannot pass "42" as a Derived*', e.message); + + var e = assert.throws(cm.BindingError, function() { + cm.Derived.prototype.setMember.call("this", "foo"); + }); + assert.equal('Cannot pass "this" as a Derived*', e.message); + + var e = assert.throws(cm.BindingError, function() { + cm.Derived.prototype.setMember.call({}, "foo"); + }); + assert.equal('Cannot pass "[object Object]" as a Derived*', e.message); + }); + + test("setting and getting property on unrelated class throws error", function() { + var a = new cm.HasTwoBases; + var e = assert.throws(cm.BindingError, function() { + Object.getOwnPropertyDescriptor(cm.HeldBySmartPtr.prototype, 'i').set.call(a, 10); + }); + assert.equal('HeldBySmartPtr.i setter incompatible with "this" of type HasTwoBases', e.message); + + var e = assert.throws(cm.BindingError, function() { + Object.getOwnPropertyDescriptor(cm.HeldBySmartPtr.prototype, 'i').get.call(a); + }); + assert.equal('HeldBySmartPtr.i getter incompatible with "this" of type HasTwoBases', e.message); + + a.delete(); + }); + }); + + BaseFixture.extend("automatic upcasting of parameters passed to C++", function() { + // raw + test("raw pointer argument is upcast to parameter type", function() { + var derived = new cm.Derived(); + var name = cm.embind_test_get_class_name_via_base_ptr(derived); + assert.equal("Base", name); + derived.delete(); + }); + + test("automatic raw pointer upcasting works with multiple inheritance", function() { + var derived = new cm.MultiplyDerived(); + var name = cm.embind_test_get_class_name_via_base_ptr(derived); + assert.equal("Base", name); + derived.delete(); + }); + + test("automatic raw pointer upcasting does not change local pointer", function() { + var derived = new cm.MultiplyDerived(); + cm.embind_test_get_class_name_via_base_ptr(derived); + assert.equal("MultiplyDerived", derived.getClassName()); + derived.delete(); + }); + + test("passing incompatible raw pointer to method throws exception", function() { + var base = new cm.Base(); + assert.throws(cm.BindingError, function() { + cm.embind_test_get_class_name_via_second_base_ptr(base); + }); + base.delete(); + }); + + // raw polymorphic + test("polymorphic raw pointer argument is upcast to parameter type", function() { + var derived = new cm.PolyDerived(); + var name = cm.embind_test_get_class_name_via_polymorphic_base_ptr(derived); + assert.equal("PolyBase", name); + derived.delete(); + }); + + test("automatic polymorphic raw pointer upcasting works with multiple inheritance", function() { + var derived = new cm.PolyMultiplyDerived(); + var name = cm.embind_test_get_class_name_via_polymorphic_base_ptr(derived); + assert.equal("PolyBase", name); + derived.delete(); + }); + + test("passing incompatible raw polymorphic pointer to method throws exception", function() { + var base = new cm.PolyBase(); + assert.throws(cm.BindingError, function() { + cm.embind_test_get_class_name_via_polymorphic_second_base_ptr(base); + }); + base.delete(); + + }); + + // smart + test("can pass smart pointer to raw pointer parameter", function() { + var smartBase = cm.embind_test_return_smart_base_ptr(); + assert.equal("Base", cm.embind_test_get_class_name_via_base_ptr(smartBase)); + smartBase.delete(); + }); + + test("can pass and upcast smart pointer to raw pointer parameter", function() { + var smartDerived = cm.embind_test_return_smart_derived_ptr(); + assert.equal("Base", cm.embind_test_get_class_name_via_base_ptr(smartDerived)); + smartDerived.delete(); + }); + + test("smart pointer argument is upcast to parameter type", function() { + var derived = cm.embind_test_return_smart_derived_ptr(); + assert.instanceof(derived, cm.Derived); + assert.instanceof(derived, cm.Base); + var name = cm.embind_test_get_class_name_via_smart_base_ptr(derived); + assert.equal("Base", name); + derived.delete(); + }); + + test("return smart derived ptr as base", function() { + var derived = cm.embind_test_return_smart_derived_ptr_as_base(); + assert.equal("PolyDerived", cm.embind_test_get_virtual_class_name_via_smart_polymorphic_base_ptr(derived)); + assert.equal("PolyDerived", derived.getClassName()); + derived.delete(); + }); + + test("return smart derived ptr as val", function() { + var derived = cm.embind_test_return_smart_derived_ptr_as_val(); + assert.equal("PolyDerived", cm.embind_test_get_virtual_class_name_via_smart_polymorphic_base_ptr(derived)); + derived.delete(); + }); + + test("automatic smart pointer upcasting works with multiple inheritance", function() { + var derived = cm.embind_test_return_smart_multiply_derived_ptr(); + var name = cm.embind_test_get_class_name_via_smart_base_ptr(derived); + assert.equal("Base", name); + derived.delete(); + }); + + test("automatically upcasted smart pointer parameter shares ownership with original argument", function() { + var derived = cm.embind_test_return_smart_multiply_derived_ptr(); + assert.equal(1, cm.MultiplyDerived.getInstanceCount()); + cm.embind_save_smart_base_pointer(derived); + assert.equal(1, cm.MultiplyDerived.getInstanceCount()); + derived.delete(); + assert.equal(1, cm.MultiplyDerived.getInstanceCount()); + cm.embind_save_smart_base_pointer(null); + assert.equal(0, cm.MultiplyDerived.getInstanceCount()); + }); + + // smart polymorphic + test("smart polymorphic pointer argument is upcast to parameter type", function() { + var derived = cm.embind_test_return_smart_polymorphic_derived_ptr(); + var name = cm.embind_test_get_class_name_via_smart_polymorphic_base_ptr(derived); + assert.equal("PolyBase", name); + derived.delete(); + }); + + test("automatic smart polymorphic pointer upcasting works with multiple inheritance", function() { + var derived = cm.embind_test_return_smart_polymorphic_multiply_derived_ptr(); + var name = cm.embind_test_get_class_name_via_smart_polymorphic_base_ptr(derived); + assert.equal("PolyBase", name); + derived.delete(); + }); + }); + + BaseFixture.extend("automatic downcasting of return values received from C++", function() { + // raw + test("non-polymorphic raw pointers are not downcast and do not break automatic casting mechanism", function() { + var base = cm.embind_test_return_raw_derived_ptr_as_base(); + assert.equal("Base", base.getClassName()); + assert.instanceof(base, cm.Base); + base.delete(); + }); + + // raw polymorphic + test("polymorphic raw pointer return value is downcast to allocated type (if that is bound)", function() { + var derived = cm.embind_test_return_raw_polymorphic_derived_ptr_as_base(); + assert.instanceof(derived, cm.PolyBase); + assert.instanceof(derived, cm.PolyDerived); + assert.equal("PolyDerived", derived.getClassName()); + var siblingDerived = cm.embind_test_return_raw_polymorphic_sibling_derived_ptr_as_base(); + assert.equal("PolySiblingDerived", siblingDerived.getClassName()); + siblingDerived.delete(); + derived.delete(); + }); + + test("polymorphic raw pointer return value is downcast to the most derived bound type", function() { + var derivedThrice = cm.embind_test_return_raw_polymorphic_derived_four_times_not_bound_as_base(); + // if the actual returned type is not bound, then don't assume anything + assert.equal("PolyBase", derivedThrice.getClassName()); + // if we ever fix this, then reverse the assertion + //assert.equal("PolyDerivedThrice", derivedThrice.getClassName()); + derivedThrice.delete(); + }); + + test("polymorphic smart pointer return value is downcast to the most derived type which has an associated smart pointer", function() { + var derived = cm.embind_test_return_poly_derived_twice_without_smart_pointer_as_poly_base(); + // if the actual returned type is not bound, then don't assume anything + assert.equal("PolyBase", derived.getClassName()); + // if we ever fix this, then remove the assertion + //assert.equal("PolyDerived", derived.getClassName()); + derived.delete(); + }); + + test("automatic downcasting works with multiple inheritance", function() { + var base = cm.embind_test_return_raw_polymorphic_multiply_derived_ptr_as_base(); + var secondBase = cm.embind_test_return_raw_polymorphic_multiply_derived_ptr_as_second_base(); + assert.equal("PolyMultiplyDerived", base.getClassName()); + // embind does not support multiple inheritance + //assert.equal("PolyMultiplyDerived", secondBase.getClassName()); + secondBase.delete(); + base.delete(); + }); + + // smart + test("non-polymorphic smart pointers do not break automatic casting mechanism", function() { + }); + + // smart polymorphic + test("automatically downcasting a smart pointer does not change the underlying pointer", function() { + cm.PolyDerived.setPtrDerived(); + assert.equal("PolyBase", cm.PolyDerived.getPtrClassName()); + var derived = cm.PolyDerived.getPtr(); + assert.equal("PolyDerived", derived.getClassName()); + assert.equal("PolyBase", cm.PolyDerived.getPtrClassName()); + derived.delete(); + cm.PolyDerived.releasePtr(); + }); + + test("polymorphic smart pointer return value is actual allocated type (when bound)", function() { + var derived = cm.embind_test_return_smart_polymorphic_derived_ptr_as_base(); + assert.equal("PolyDerived", derived.getClassName()); + + var siblingDerived = cm.embind_test_return_smart_polymorphic_sibling_derived_ptr_as_base(); + assert.equal("PolySiblingDerived", siblingDerived.getClassName()); + + siblingDerived.delete(); + derived.delete(); + }); + }); + + BaseFixture.extend("string", function() { + test("non-ascii strings", function() { + var expected = ''; + for (var i = 0; i < 128; ++i) { + expected += String.fromCharCode(128 + i); + } + assert.equal(expected, cm.get_non_ascii_string()); + }); + + test("passing non-8-bit strings from JS to std::string throws", function() { + assert.throws(cm.BindingError, function() { + cm.emval_test_take_and_return_std_string("\u1234"); + }); + }); + + test("can't pass integers as strings", function() { + var e = assert.throws(cm.BindingError, function() { + cm.emval_test_take_and_return_std_string(10); + }); + }); + + test("can pass Uint8Array to std::string", function() { + var e = cm.emval_test_take_and_return_std_string(new Uint8Array([65, 66, 67, 68])); + assert.equal('ABCD', e); + }); + + test("can pass Int8Array to std::string", function() { + var e = cm.emval_test_take_and_return_std_string(new Int8Array([65, 66, 67, 68])); + assert.equal('ABCD', e); + }); + + test("can pass ArrayBuffer to std::string", function() { + var e = cm.emval_test_take_and_return_std_string((new Int8Array([65, 66, 67, 68])).buffer); + assert.equal('ABCD', e); + }); + + test("non-ascii wstrings", function() { + var expected = String.fromCharCode(10) + + String.fromCharCode(1234) + + String.fromCharCode(2345) + + String.fromCharCode(65535); + assert.equal(expected, cm.get_non_ascii_wstring()); + }); + + test("passing unicode string into C++", function() { + var expected = String.fromCharCode(10) + + String.fromCharCode(1234) + + String.fromCharCode(2345) + + String.fromCharCode(65535); + assert.equal(expected, cm.take_and_return_std_wstring(expected)); + }); + }); + + BaseFixture.extend("embind", function() { + test("value creation", function() { + assert.equal(15, cm.emval_test_new_integer()); + assert.equal("Hello everyone", cm.emval_test_new_string()); + assert.equal("Hello everyone", cm.emval_test_get_string_from_val({key: "Hello everyone"})); + + var object = cm.emval_test_new_object(); + assert.equal('bar', object.foo); + assert.equal(1, object.baz); + }); + + test("pass const reference to primitive", function() { + assert.equal(3, cm.const_ref_adder(1, 2)); + }); + + test("passthrough", function() { + var a = {foo: 'bar'}; + var b = cm.emval_test_passthrough(a); + a.bar = 'baz'; + assert.equal('baz', b.bar); + + assert.equal(0, cm.count_emval_handles()); + }); + + test("void return converts to undefined", function() { + assert.equal(undefined, cm.emval_test_return_void()); + }); + + test("booleans can be marshalled", function() { + assert.equal(false, cm.emval_test_not(true)); + assert.equal(true, cm.emval_test_not(false)); + }); + + test("convert double to unsigned", function() { + var rv = cm.emval_test_as_unsigned(1.5); + assert.equal('number', typeof rv); + assert.equal(1, rv); + assert.equal(0, cm.count_emval_handles()); + }); + + test("get length of array", function() { + assert.equal(10, cm.emval_test_get_length([0, 1, 2, 3, 4, 5, 'a', 'b', 'c', 'd'])); + assert.equal(0, cm.count_emval_handles()); + }); + + test("add a bunch of things", function() { + assert.equal(66.0, cm.emval_test_add(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)); + assert.equal(0, cm.count_emval_handles()); + }); + + test("sum array", function() { + assert.equal(66, cm.emval_test_sum([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])); + assert.equal(0, cm.count_emval_handles()); + }); + + test("strings", function() { + assert.equal("foobar", "foo" + "bar"); + assert.equal("foobar", cm.emval_test_take_and_return_std_string("foobar")); + + assert.equal("foobar", cm.emval_test_take_and_return_std_string_const_ref("foobar")); + }); + + test("nuls pass through strings", function() { + assert.equal("foo\0bar", cm.emval_test_take_and_return_std_string("foo\0bar")); + }); + + test("no memory leak when passing strings in by const reference", function() { + cm.emval_test_take_and_return_std_string_const_ref("foobar"); + }); + + test("can create new object", function() { + assert.deepEqual({}, cm.embind_test_new_Object()); + }); + + test("can invoke constructors with arguments", function() { + function constructor(i, s, argument) { + this.i = i; + this.s = s; + this.argument = argument; + } + constructor.prototype.method = function() { + return this.argument; + }; + var x = {}; + var instance = cm.embind_test_new_factory(constructor, x); + assert.equal(10, instance.i); + assert.equal("hello", instance.s); + assert.equal(x, instance.argument); + }); + + test("can return module property objects", function() { + assert.equal(cm.HEAP8, cm.get_module_property("HEAP8")); + }); + + test("can return big class instances", function() { + var c = cm.embind_test_return_big_class_instance(); + assert.equal(11, c.member); + c.delete(); + }); + + test("can return small class instances", function() { + var c = cm.embind_test_return_small_class_instance(); + assert.equal(7, c.member); + c.delete(); + }); + + test("can pass small class instances", function() { + var c = new cm.SmallClass(); + var m = cm.embind_test_accept_small_class_instance(c); + assert.equal(7, m); + c.delete(); + }); + + test("can pass big class instances", function() { + var c = new cm.BigClass(); + var m = cm.embind_test_accept_big_class_instance(c); + assert.equal(11, m); + c.delete(); + }); + + test("can get member classes then call its member functions", function() { + var p = new cm.ParentClass(); + var c = p.getBigClass(); + var m = c.getMember(); + assert.equal(11, m); + c.delete(); + p.delete(); + }); + + test('C++ -> JS primitive type range checks', function() { + // all types should have zero. + assert.equal("0", cm.char_to_string(0)); + assert.equal("0", cm.signed_char_to_string(0)); + assert.equal("0", cm.unsigned_char_to_string(0)); + assert.equal("0", cm.short_to_string(0)); + assert.equal("0", cm.unsigned_short_to_string(0)); + assert.equal("0", cm.int_to_string(0)); + assert.equal("0", cm.unsigned_int_to_string(0)); + assert.equal("0", cm.long_to_string(0)); + assert.equal("0", cm.unsigned_long_to_string(0)); + + // all types should have positive values. + assert.equal("5", cm.char_to_string(5)); + assert.equal("5", cm.signed_char_to_string(5)); + assert.equal("5", cm.unsigned_char_to_string(5)); + assert.equal("5", cm.short_to_string(5)); + assert.equal("5", cm.unsigned_short_to_string(5)); + assert.equal("5", cm.int_to_string(5)); + assert.equal("5", cm.unsigned_int_to_string(5)); + assert.equal("5", cm.long_to_string(5)); + assert.equal("5", cm.unsigned_long_to_string(5)); + + // signed types should have negative values. + assert.equal("-5", cm.char_to_string(-5)); // Assuming char as signed. + assert.equal("-5", cm.signed_char_to_string(-5)); + assert.equal("-5", cm.short_to_string(-5)); + assert.equal("-5", cm.int_to_string(-5)); + assert.equal("-5", cm.long_to_string(-5)); + + // assumptions: char == signed char == 8 bits + // unsigned char == 8 bits + // short == 16 bits + // int == long == 32 bits + + // all types should have their max positive values. + assert.equal("127", cm.char_to_string(127)); + assert.equal("127", cm.signed_char_to_string(127)); + assert.equal("255", cm.unsigned_char_to_string(255)); + assert.equal("32767", cm.short_to_string(32767)); + assert.equal("65535", cm.unsigned_short_to_string(65535)); + assert.equal("2147483647", cm.int_to_string(2147483647)); + assert.equal("4294967295", cm.unsigned_int_to_string(4294967295)); + assert.equal("2147483647", cm.long_to_string(2147483647)); + assert.equal("4294967295", cm.unsigned_long_to_string(4294967295)); + + // signed types should have their min negative values. + assert.equal("-128", cm.char_to_string(-128)); + assert.equal("-128", cm.signed_char_to_string(-128)); + assert.equal("-32768", cm.short_to_string(-32768)); + assert.equal("-2147483648", cm.int_to_string(-2147483648)); + assert.equal("-2147483648", cm.long_to_string(-2147483648)); + + // passing out of range values should fail. + assert.throws(TypeError, function() { cm.char_to_string(-129); }); + assert.throws(TypeError, function() { cm.char_to_string(128); }); + assert.throws(TypeError, function() { cm.signed_char_to_string(-129); }); + assert.throws(TypeError, function() { cm.signed_char_to_string(128); }); + assert.throws(TypeError, function() { cm.unsigned_char_to_string(-1); }); + assert.throws(TypeError, function() { cm.unsigned_char_to_string(256); }); + assert.throws(TypeError, function() { cm.short_to_string(-32769); }); + assert.throws(TypeError, function() { cm.short_to_string(32768); }); + assert.throws(TypeError, function() { cm.unsigned_short_to_string(-1); }); + assert.throws(TypeError, function() { cm.unsigned_short_to_string(65536); }); + assert.throws(TypeError, function() { cm.int_to_string(-2147483649); }); + assert.throws(TypeError, function() { cm.int_to_string(2147483648); }); + assert.throws(TypeError, function() { cm.unsigned_int_to_string(-1); }); + assert.throws(TypeError, function() { cm.unsigned_int_to_string(4294967296); }); + assert.throws(TypeError, function() { cm.long_to_string(-2147483649); }); + assert.throws(TypeError, function() { cm.long_to_string(2147483648); }); + assert.throws(TypeError, function() { cm.unsigned_long_to_string(-1); }); + assert.throws(TypeError, function() { cm.unsigned_long_to_string(4294967296); }); + }); + + test("access multiple class ctors", function() { + var a = new cm.MultipleCtors(10); + assert.equal(a.WhichCtorCalled(), 1); + var b = new cm.MultipleCtors(20, 20); + assert.equal(b.WhichCtorCalled(), 2); + var c = new cm.MultipleCtors(30, 30, 30); + assert.equal(c.WhichCtorCalled(), 3); + a.delete(); + b.delete(); + c.delete(); + }); + + test("wrong number of constructor arguments throws", function() { + assert.throws(cm.BindingError, function() { new cm.MultipleCtors(); }); + assert.throws(cm.BindingError, function() { new cm.MultipleCtors(1,2,3,4); }); + }); + + test("overloading of free functions", function() { + var a = cm.overloaded_function(10); + assert.equal(a, 1); + var b = cm.overloaded_function(20, 20); + assert.equal(b, 2); + }); + + test("wrong number of arguments to an overloaded free function", function() { + assert.throws(cm.BindingError, function() { cm.overloaded_function(); }); + assert.throws(cm.BindingError, function() { cm.overloaded_function(30, 30, 30); }); + }); + + test("overloading of class member functions", function() { + var foo = new cm.MultipleOverloads(); + assert.equal(foo.Func(10), 1); + assert.equal(foo.WhichFuncCalled(), 1); + assert.equal(foo.Func(20, 20), 2); + assert.equal(foo.WhichFuncCalled(), 2); + foo.delete(); + }); + + test("wrong number of arguments to an overloaded class member function", function() { + var foo = new cm.MultipleOverloads(); + assert.throws(cm.BindingError, function() { foo.Func(); }); + assert.throws(cm.BindingError, function() { foo.Func(30, 30, 30); }); + foo.delete(); + }); + + test("wrong number of arguments to an overloaded class static function", function() { + assert.throws(cm.BindingError, function() { cm.MultipleOverloads.StaticFunc(); }); + assert.throws(cm.BindingError, function() { cm.MultipleOverloads.StaticFunc(30, 30, 30); }); + }); + + test("overloading of derived class member functions", function() { + var foo = new cm.MultipleOverloadsDerived(); + + // NOTE: In C++, default lookup rules will hide overloads from base class if derived class creates them. + // In JS, we make the base class overloads implicitly available. In C++, they would need to be explicitly + // invoked, like foo.MultipleOverloads::Func(10); + assert.equal(foo.Func(10), 1); + assert.equal(foo.WhichFuncCalled(), 1); + assert.equal(foo.Func(20, 20), 2); + assert.equal(foo.WhichFuncCalled(), 2); + + assert.equal(foo.Func(30, 30, 30), 3); + assert.equal(foo.WhichFuncCalled(), 3); + assert.equal(foo.Func(40, 40, 40, 40), 4); + assert.equal(foo.WhichFuncCalled(), 4); + foo.delete(); + }); + + test("overloading of class static functions", function() { + assert.equal(cm.MultipleOverloads.StaticFunc(10), 1); + assert.equal(cm.MultipleOverloads.WhichStaticFuncCalled(), 1); + assert.equal(cm.MultipleOverloads.StaticFunc(20, 20), 2); + assert.equal(cm.MultipleOverloads.WhichStaticFuncCalled(), 2); + }); + + test("overloading of derived class static functions", function() { + assert.equal(cm.MultipleOverloadsDerived.StaticFunc(30, 30, 30), 3); + // TODO: Cannot access static member functions of a Base class via Derived. +// assert.equal(cm.MultipleOverloadsDerived.WhichStaticFuncCalled(), 3); + assert.equal(cm.MultipleOverloads.WhichStaticFuncCalled(), 3); + assert.equal(cm.MultipleOverloadsDerived.StaticFunc(40, 40, 40, 40), 4); + // TODO: Cannot access static member functions of a Base class via Derived. +// assert.equal(cm.MultipleOverloadsDerived.WhichStaticFuncCalled(), 4); + assert.equal(cm.MultipleOverloads.WhichStaticFuncCalled(), 4); + }); +/* + test("can get templated member classes then call its member functions", function() { + var p = new cm.ContainsTemplatedMemberClass(); + var c = p.getTestTemplate(); + var m = c.getMember(1); + assert.equal(87, m); + c.delete(); + p.delete(); + }); +*/ + }); + + BaseFixture.extend("vector", function() { + test("std::vector returns as an native object", function() { + var vec = cm.emval_test_return_vector(); + + assert.equal(3, vec.size()); + assert.equal(10, vec.get(0)); + assert.equal(20, vec.get(1)); + assert.equal(30, vec.get(2)); + vec.delete(); + }); + + test("out of bounds std::vector access returns undefined", function() { + var vec = cm.emval_test_return_vector(); + + assert.throws(TypeError, function() { vec.get(-1); }); + assert.equal(undefined, vec.get(4)); + + vec.delete(); + }); + + test("std::vector<std::shared_ptr<>> can be passed back", function() { + var vec = cm.emval_test_return_shared_ptr_vector(); + + assert.equal(2, vec.size()); + var str0 = vec.get(0); + var str1 = vec.get(1); + + assert.equal('string #1', str0.get()); + assert.equal('string #2', str1.get()); + str0.delete(); + str1.delete(); + + vec.delete(); + }); + + test("objects can be pushed back", function() { + var vectorHolder = new cm.VectorHolder(); + var vec = vectorHolder.get(); + assert.equal(2, vec.size()); + + var str = new cm.StringHolder('abc'); + vec.push_back(str); + str.delete(); + assert.equal(3, vec.size()); + var str = vec.get(2); + assert.equal('abc', str.get()); + + str.delete(); + vec.delete(); + vectorHolder.delete(); + }); + + test("can get elements with array operator", function(){ + var vec = cm.emval_test_return_vector(); + assert.equal(10, vec.get(0)); + vec.delete(); + }); + + test("can set elements with array operator", function() { + var vec = cm.emval_test_return_vector(); + assert.equal(10, vec.get(0)); + vec.set(2, 60); + assert.equal(60, vec.get(2)); + vec.delete(); + }); + + test("can set and get objects", function() { + var vec = cm.emval_test_return_shared_ptr_vector(); + var str = vec.get(0); + assert.equal('string #1', str.get()); + str.delete(); + vec.delete(); + }); + }); + + BaseFixture.extend("map", function() { + test("std::map returns as native object", function() { + var map = cm.embind_test_get_string_int_map(); + + assert.equal(2, map.size()); + assert.equal(1, map.get("one")); + assert.equal(2, map.get("two")); + + map.delete(); + }); + + test("std::map can set keys and values", function() { + var map = cm.embind_test_get_string_int_map(); + + assert.equal(2, map.size()); + + map.set("three", 3); + + assert.equal(3, map.size()); + assert.equal(3, map.get("three")); + + map.set("three", 4); + + assert.equal(3, map.size()); + assert.equal(4, map.get("three")); + + map.delete(); + }); + }); + + BaseFixture.extend("functors", function() { + test("can get and call function ptrs", function() { + var ptr = cm.emval_test_get_function_ptr(); + assert.equal("foobar", ptr.opcall("foobar")); + ptr.delete(); + }); + + test("can pass functor to C++", function() { + var ptr = cm.emval_test_get_function_ptr(); + assert.equal("asdf", cm.emval_test_take_and_call_functor(ptr)); + ptr.delete(); + }); + + test("can clone handles", function() { + var a = cm.emval_test_get_function_ptr(); + assert.equal(1, a.$$.count.value); + var b = a.clone(); + assert.equal(2, a.$$.count.value); + assert.equal(2, b.$$.count.value); + a.delete(); + + assert.throws(cm.BindingError, function() { + a.delete(); + }); + b.delete(); + }); + }); + + BaseFixture.extend("classes", function() { + test("class instance", function() { + var a = {foo: 'bar'}; + assert.equal(0, cm.count_emval_handles()); + var c = new cm.ValHolder(a); + assert.equal(1, cm.count_emval_handles()); + assert.equal('bar', c.getVal().foo); + assert.equal(1, cm.count_emval_handles()); + + c.setVal('1234'); + assert.equal('1234', c.getVal()); + + c.delete(); + assert.equal(0, cm.count_emval_handles()); + }); + + test("class properties can be methods", function() { + var a = {}; + var b = {foo: 'foo'}; + var c = new cm.ValHolder(a); + assert.equal(a, c.val); + c.val = b; + assert.equal(b, c.val); + c.delete(); + }); + + test("class properties can be read-only", function() { + var a = {}; + var h = new cm.ValHolder(a); + assert.equal(a, h.val_readonly); + var e = assert.throws(cm.BindingError, function() { + h.val_readonly = 10; + }); + assert.equal('ValHolder.val_readonly is a read-only property', e.message); + h.delete(); + }); + + test("read-only member field", function() { + var a = new cm.HasReadOnlyProperty(10); + assert.equal(10, a.i); + var e = assert.throws(cm.BindingError, function() { + a.i = 20; + }); + assert.equal('HasReadOnlyProperty.i is a read-only property', e.message); + a.delete(); + }); + + test("class instance $$ property is non-enumerable", function() { + var c = new cm.ValHolder(undefined); + assert.deepEqual([], Object.keys(c)); + var d = c.clone(); + c.delete(); + + assert.deepEqual([], Object.keys(d)); + d.delete(); + }); + + test("class methods", function() { + assert.equal(10, cm.ValHolder.some_class_method(10)); + + var b = cm.ValHolder.makeValHolder("foo"); + assert.equal("foo", b.getVal()); + b.delete(); + }); + + test("can't call methods on deleted class instances", function() { + var c = new cm.ValHolder(undefined); + c.delete(); + assert.throws(cm.BindingError, function() { + c.getVal(); + }); + assert.throws(cm.BindingError, function() { + c.delete(); + }); + }); + + test("calling constructor without new raises BindingError", function() { + var e = assert.throws(cm.BindingError, function() { + cm.ValHolder(undefined); + }); + assert.equal("Use 'new' to construct ValHolder", e.message); + }); + + test("can return class instances by value", function() { + var c = cm.emval_test_return_ValHolder(); + assert.deepEqual({}, c.getVal()); + c.delete(); + }); + + test("can pass class instances to functions by reference", function() { + var a = {a:1}; + var c = new cm.ValHolder(a); + cm.emval_test_set_ValHolder_to_empty_object(c); + assert.deepEqual({}, c.getVal()); + c.delete(); + }); + + test("can pass smart pointer by reference", function() { + var base = cm.embind_test_return_smart_base_ptr(); + var name = cm.embind_test_get_class_name_via_reference_to_smart_base_ptr(base); + assert.equal("Base", name); + base.delete(); + }); + + test("can pass smart pointer by value", function() { + var base = cm.embind_test_return_smart_base_ptr(); + var name = cm.embind_test_get_class_name_via_smart_base_ptr(base); + assert.equal("Base", name); + base.delete(); + }); + + // todo: fix this + // This test does not work because we make no provision for argument values + // having been changed after returning from a C++ routine invocation. In + // this specific case, the original pointee of the smart pointer was + // freed and replaced by a new one, but the ptr in our local handle + // was never updated after returning from the call. + test("can modify smart pointers passed by reference", function() { +// var base = cm.embind_test_return_smart_base_ptr(); +// cm.embind_modify_smart_pointer_passed_by_reference(base); +// assert.equal("Changed", base.getClassName()); +// base.delete(); + }); + + test("can not modify smart pointers passed by value", function() { + var base = cm.embind_test_return_smart_base_ptr(); + cm.embind_attempt_to_modify_smart_pointer_when_passed_by_value(base); + assert.equal("Base", base.getClassName()); + base.delete(); + }); + + test("const return value", function() { + var c = new cm.ValHolder("foo"); + assert.equal("foo", c.getConstVal()); + c.delete(); + }); + + test("return object by const ref", function() { + var c = new cm.ValHolder("foo"); + assert.equal("foo", c.getValConstRef()); + c.delete(); + }); + + test("instanceof", function() { + var c = new cm.ValHolder("foo"); + assert.instanceof(c, cm.ValHolder); + c.delete(); + }); + + test("can access struct fields", function() { + var c = new cm.CustomStruct(); + assert.equal(10, c.field); + assert.equal(10, c.getField()); + c.delete(); + }); + + test("can set struct fields", function() { + var c = new cm.CustomStruct(); + c.field = 15; + assert.equal(15, c.field); + c.delete(); + }); + + test("assignment returns value", function() { + var c = new cm.CustomStruct(); + assert.equal(15, c.field = 15); + c.delete(); + }); + + test("assigning string to integer raises TypeError", function() { + var c = new cm.CustomStruct(); + + var e = assert.throws(TypeError, function() { + c.field = "hi"; + }); + assert.equal('Cannot convert "hi" to int', e.message); + + var e = assert.throws(TypeError, function() { + c.field = {foo:'bar'}; + }); + assert.equal('Cannot convert "[object Object]" to int', e.message); + + c.delete(); + }); + + test("can return tuples by value", function() { + var c = cm.emval_test_return_TupleVector(); + assert.deepEqual([1, 2, 3, 4], c); + }); + + test("tuples can contain tuples", function() { + var c = cm.emval_test_return_TupleVectorTuple(); + assert.deepEqual([[1, 2, 3, 4]], c); + }); + + test("can pass tuples by value", function() { + var c = cm.emval_test_take_and_return_TupleVector([4, 5, 6, 7]); + assert.deepEqual([4, 5, 6, 7], c); + }); + + test("can return structs by value", function() { + var c = cm.emval_test_return_StructVector(); + assert.deepEqual({x: 1, y: 2, z: 3, w: 4}, c); + }); + + test("can pass structs by value", function() { + var c = cm.emval_test_take_and_return_StructVector({x: 4, y: 5, z: 6, w: 7}); + assert.deepEqual({x: 4, y: 5, z: 6, w: 7}, c); + }); + + test("can pass and return tuples in structs", function() { + var d = cm.emval_test_take_and_return_TupleInStruct({field: [1, 2, 3, 4]}); + assert.deepEqual({field: [1, 2, 3, 4]}, d); + }); + + test("can clone handles", function() { + var a = new cm.ValHolder({}); + assert.equal(1, cm.count_emval_handles()); + var b = a.clone(); + a.delete(); + + assert.equal(1, cm.count_emval_handles()); + + assert.throws(cm.BindingError, function() { + a.delete(); + }); + b.delete(); + + assert.equal(0, cm.count_emval_handles()); + }); + + test("A shared pointer set/get point to the same underlying pointer", function() { + var a = new cm.SharedPtrHolder(); + var b = a.get(); + + a.set(b); + var c = a.get(); + + assert.equal(b.$$.ptr, c.$$.ptr); + b.delete(); + c.delete(); + a.delete(); + }); + + test("can return shared ptrs from instance methods", function() { + var a = new cm.SharedPtrHolder(); + + // returns the shared_ptr. + var b = a.get(); + + assert.equal("a string", b.get()); + b.delete(); + a.delete(); + }); + + test("smart ptrs clone correctly", function() { + assert.equal(0, cm.count_emval_handles()); + + var a = cm.emval_test_return_shared_ptr(); + + var b = a.clone(); + a.delete(); + + assert.equal(1, cm.count_emval_handles()); + + assert.throws(cm.BindingError, function() { + a.delete(); + }); + b.delete(); + + assert.equal(0, cm.count_emval_handles()); + }); + + test("can't clone if already deleted", function() { + var a = new cm.ValHolder({}); + a.delete(); + assert.throws(cm.BindingError, function() { + a.clone(); + }); + }); + + test("virtual calls work correctly", function() { + var derived = cm.embind_test_return_raw_polymorphic_derived_ptr_as_base(); + assert.equal("PolyDerived", derived.virtualGetClassName()); + derived.delete(); + }); + + test("virtual calls work correctly on smart ptrs", function() { + var derived = cm.embind_test_return_smart_polymorphic_derived_ptr_as_base(); + assert.equal("PolyDerived", derived.virtualGetClassName()); + derived.delete(); + }); + + test("Empty smart ptr is null", function() { + var a = cm.emval_test_return_empty_shared_ptr(); + assert.equal(null, a); + }); + + test("string cannot be given as smart pointer argument", function() { + assert.throws(cm.BindingError, function() { + cm.emval_test_is_shared_ptr_null("hello world"); + }); + }); + + test("number cannot be given as smart pointer argument", function() { + assert.throws(cm.BindingError, function() { + cm.emval_test_is_shared_ptr_null(105); + }); + }); + + test("raw pointer cannot be given as smart pointer argument", function() { + var p = new cm.ValHolder({}); + assert.throws(cm.BindingError, function() { cm.emval_test_is_shared_ptr_null(p); }); + p.delete(); + }); + + test("null is passed as empty smart pointer", function() { + assert.true(cm.emval_test_is_shared_ptr_null(null)); + }); + + test("Deleting already deleted smart ptrs fails", function() { + var a = cm.emval_test_return_shared_ptr(); + a.delete(); + assert.throws(cm.BindingError, function() { + a.delete(); + }); + }); + + test("StringHolder", function() { + var a = new cm.StringHolder("foobar"); + assert.equal("foobar", a.get()); + + a.set("barfoo"); + assert.equal("barfoo", a.get()); + + assert.equal("barfoo", a.get_const_ref()); + + a.delete(); + }); + + test("can call methods on unique ptr", function() { + var result = cm.emval_test_return_unique_ptr(); + + result.setVal('1234'); + assert.equal('1234', result.getVal()); + result.delete(); + }); + + test("can call methods on shared ptr", function(){ + var result = cm.emval_test_return_shared_ptr(); + result.setVal('1234'); + + assert.equal('1234', result.getVal()); + result.delete(); + }); + + test("Non functors throw exception", function() { + var a = {foo: 'bar'}; + var c = new cm.ValHolder(a); + assert.throws(TypeError, function() { + c(); + }); + c.delete(); + }); + + test("non-member methods", function() { + var a = {foo: 'bar'}; + var c = new cm.ValHolder(a); + c.setEmpty(); // non-member method + assert.deepEqual({}, c.getValNonMember()); + c.delete(); + }); + + test("instantiating class without constructor gives error", function() { + var e = assert.throws(cm.BindingError, function() { + cm.AbstractClass(); + }); + assert.equal("Use 'new' to construct AbstractClass", e.message); + + var e = assert.throws(cm.BindingError, function() { + new cm.AbstractClass(); + }); + assert.equal("AbstractClass has no accessible constructor", e.message); + }); + + test("can construct class with external constructor", function() { + var e = new cm.HasExternalConstructor("foo"); + assert.instanceof(e, cm.HasExternalConstructor); + assert.equal("foo", e.getString()); + e.delete(); + }); + }); + + BaseFixture.extend("const", function() { + test("calling non-const method with const handle is error", function() { + var vh = cm.ValHolder.makeConst({}); + var e = assert.throws(cm.BindingError, function() { + vh.setVal({}); + }); + assert.equal('Cannot convert argument of type ValHolder const* to parameter type ValHolder*', e.message); + vh.delete(); + }); + + test("passing const pointer to non-const pointer is error", function() { + var vh = new cm.ValHolder.makeConst({}); + var e = assert.throws(cm.BindingError, function() { + cm.ValHolder.set_via_raw_pointer(vh, {}); + }); + assert.equal('Cannot convert argument of type ValHolder const* to parameter type ValHolder*', e.message); + vh.delete(); + }); + }); + + BaseFixture.extend("smart pointers", function() { + test("constructor can return smart pointer", function() { + var e = new cm.HeldBySmartPtr(10, "foo"); + assert.instanceof(e, cm.HeldBySmartPtr); + assert.equal(10, e.i); + assert.equal("foo", e.s); + var f = cm.takesHeldBySmartPtr(e); + f.delete(); + e.delete(); + }); + + test("cannot pass incorrect smart pointer type", function() { + var e = cm.emval_test_return_shared_ptr(); + assert.throws(cm.BindingError, function() { + cm.takesHeldBySmartPtr(e); + }); + e.delete(); + }); + + test("smart pointer object has no object keys", function() { + var e = new cm.HeldBySmartPtr(10, "foo"); + assert.deepEqual([], Object.keys(e)); + + var f = e.clone(); + e.delete(); + + assert.deepEqual([], Object.keys(f)); + f.delete(); + }); + + test("smart pointer object has correct constructor name", function() { + var e = new cm.HeldBySmartPtr(10, "foo"); + assert.equal('HeldBySmartPtr', e.constructor.name); + e.delete(); + }); + + test("constructor can return smart pointer", function() { + var e = new cm.HeldBySmartPtr(10, "foo"); + assert.instanceof(e, cm.HeldBySmartPtr); + assert.equal(10, e.i); + assert.equal("foo", e.s); + var f = cm.takesHeldBySmartPtr(e); + assert.instanceof(f, cm.HeldBySmartPtr); + f.delete(); + e.delete(); + }); + + test("custom smart pointer", function() { + var e = new cm.HeldByCustomSmartPtr(20, "bar"); + assert.instanceof(e, cm.HeldByCustomSmartPtr); + assert.equal(20, e.i); + assert.equal("bar", e.s); + e.delete(); + }); + + test("custom smart pointer passed through wiretype", function() { + var e = new cm.HeldByCustomSmartPtr(20, "bar"); + var f = cm.passThroughCustomSmartPtr(e); + e.delete(); + + assert.instanceof(f, cm.HeldByCustomSmartPtr); + assert.equal(20, f.i); + assert.equal("bar", f.s); + + f.delete(); + }); + + test("cannot give null to by-value argument", function() { + var e = assert.throws(cm.BindingError, function() { + cm.takesHeldBySmartPtr(null); + }); + assert.equal('null is not a valid HeldBySmartPtr', e.message); + }); + + test("raw pointer can take and give null", function() { + assert.equal(null, cm.passThroughRawPtr(null)); + }); + + test("custom smart pointer can take and give null", function() { + assert.equal(null, cm.passThroughCustomSmartPtr(null)); + }); + + test("cannot pass shared_ptr to CustomSmartPtr", function() { + var o = cm.HeldByCustomSmartPtr.createSharedPtr(10, "foo"); + var e = assert.throws(cm.BindingError, function() { + cm.passThroughCustomSmartPtr(o); + }); + assert.equal('Cannot convert argument of type NSt3__110shared_ptrI20HeldByCustomSmartPtrEE to parameter type 14CustomSmartPtrI20HeldByCustomSmartPtrE', e.message); + o.delete(); + }); + + test("custom smart pointers can be passed to shared_ptr parameter", function() { + var e = cm.HeldBySmartPtr.newCustomPtr(10, "abc"); + assert.equal(10, e.i); + assert.equal("abc", e.s); + + cm.takesHeldBySmartPtrSharedPtr(e).delete(); + e.delete(); + }); + + test("can call non-member functions as methods", function() { + var e = new cm.HeldBySmartPtr(20, "bar"); + var f = e.returnThis(); + e.delete(); + assert.equal(20, f.i); + assert.equal("bar", f.s); + f.delete(); + }); + }); + + BaseFixture.extend("enumerations", function() { + test("can compare enumeration values", function() { + assert.equal(cm.Enum.ONE, cm.Enum.ONE); + assert.notEqual(cm.Enum.ONE, cm.Enum.TWO); + }); + + if (typeof INVOKED_FROM_EMSCRIPTEN_TEST_RUNNER === "undefined") { // TODO: Enable this to work in Emscripten runner as well! + test("repr includes enum value", function() { + assert.equal('<#Enum_ONE {}>', IMVU.repr(cm.Enum.ONE)); + assert.equal('<#Enum_TWO {}>', IMVU.repr(cm.Enum.TWO)); + }); + } + + test("instanceof", function() { + assert.instanceof(cm.Enum.ONE, cm.Enum); + }); + + test("can pass and return enumeration values to functions", function() { + assert.equal(cm.Enum.TWO, cm.emval_test_take_and_return_Enum(cm.Enum.TWO)); + }); + }); + + BaseFixture.extend("C++11 enum class", function() { + test("can compare enumeration values", function() { + assert.equal(cm.EnumClass.ONE, cm.EnumClass.ONE); + assert.notEqual(cm.EnumClass.ONE, cm.EnumClass.TWO); + }); + + if (typeof INVOKED_FROM_EMSCRIPTEN_TEST_RUNNER === "undefined") { // TODO: Enable this to work in Emscripten runner as well! + test("repr includes enum value", function() { + assert.equal('<#EnumClass_ONE {}>', IMVU.repr(cm.EnumClass.ONE)); + assert.equal('<#EnumClass_TWO {}>', IMVU.repr(cm.EnumClass.TWO)); + }); + } + + test("instanceof", function() { + assert.instanceof(cm.EnumClass.ONE, cm.EnumClass); + }); + + test("can pass and return enumeration values to functions", function() { + assert.equal(cm.EnumClass.TWO, cm.emval_test_take_and_return_EnumClass(cm.EnumClass.TWO)); + }); + }); + + BaseFixture.extend("emval call tests", function() { + test("can call functions from C++", function() { + var called = false; + cm.emval_test_call_function(function(i, f, tv, sv) { + called = true; + assert.equal(10, i); + assert.equal(1.5, f); + assert.deepEqual([1.25, 2.5, 3.75, 4], tv); + assert.deepEqual({x: 1.25, y: 2.5, z: 3.75, w:4}, sv); + }, 10, 1.5, [1.25, 2.5, 3.75, 4], {x: 1.25, y: 2.5, z: 3.75, w:4}); + assert.true(called); + }); + }); + + BaseFixture.extend("extending built-in classes", function() { + // cm.ValHolder.prototype.patched = 10; // this sets instanceCounts.patched inside of Deletable module !?! + + test("can access patched value on new instances", function() { + cm.ValHolder.prototype.patched = 10; + var c = new cm.ValHolder(undefined); + assert.equal(10, c.patched); + c.delete(); + cm.ValHolder.prototype.patched = undefined; + }); + + test("can access patched value on returned instances", function() { + cm.ValHolder.prototype.patched = 10; + var c = cm.emval_test_return_ValHolder(); + assert.equal(10, c.patched); + c.delete(); + cm.ValHolder.prototype.patched = undefined; + }); + }); + + BaseFixture.extend("raw pointers", function() { + test("can pass raw pointers into functions if explicitly allowed", function() { + var vh = new cm.ValHolder({}); + cm.ValHolder.set_via_raw_pointer(vh, 10); + assert.equal(10, cm.ValHolder.get_via_raw_pointer(vh)); + vh.delete(); + }); + + test("can return raw pointers from functions if explicitly allowed", function() { + var p = cm.embind_test_return_raw_base_ptr(); + assert.equal("Base", p.getClassName()); + p.delete(); + }); + + test("can pass multiple raw pointers to functions", function() { + var target = new cm.ValHolder(undefined); + var source = new cm.ValHolder("hi"); + cm.ValHolder.transfer_via_raw_pointer(target, source); + assert.equal("hi", target.getVal()); + target.delete(); + source.delete(); + }); + }); + + BaseFixture.extend("abstract methods", function() { + test("can call abstract methods", function() { + var obj = cm.getAbstractClass(); + assert.equal("from concrete", obj.abstractMethod()); + obj.delete(); + }); + + test("can implement abstract methods in JavaScript", function() { + var expected = "my JS string"; + function MyImplementation() { + this.rv = expected; + } + MyImplementation.prototype.abstractMethod = function() { + return this.rv; + }; + + var impl = cm.AbstractClass.implement(new MyImplementation); + assert.equal(expected, impl.abstractMethod()); + assert.equal(expected, cm.callAbstractMethod(impl)); + impl.delete(); + }); + + test("can implement optional methods in JavaScript", function() { + var expected = "my JS string"; + function MyImplementation() { + this.rv = expected; + } + MyImplementation.prototype.optionalMethod = function() { + return this.rv; + }; + + var impl = cm.AbstractClass.implement(new MyImplementation); + assert.equal(expected, impl.optionalMethod(expected)); + assert.equal(expected, cm.callOptionalMethod(impl, expected)); + impl.delete(); + }); + + test("if not implemented then optional method runs default", function() { + var impl = cm.AbstractClass.implement({}); + assert.equal("optionalfoo", impl.optionalMethod("foo")); + assert.equal("optionalfoo", cm.callOptionalMethod(impl, "foo")); + impl.delete(); + }); + }); + + BaseFixture.extend("registration order", function() { + test("registration of tuple elements out of order leaves them in order", function() { + var ot = cm.getOrderedTuple(); + assert.instanceof(ot[0], cm.FirstElement); + assert.instanceof(ot[1], cm.SecondElement); + ot[0].delete(); + ot[1].delete(); + }); + + test("registration of struct elements out of order", function() { + var os = cm.getOrderedStruct(); + assert.instanceof(os.first, cm.FirstElement); + assert.instanceof(os.second, cm.SecondElement); + os.first.delete(); + os.second.delete(); + }); + }); + + if (typeof INVOKED_FROM_EMSCRIPTEN_TEST_RUNNER === "undefined") { // TODO: Enable this to work in Emscripten runner as well! + + BaseFixture.extend("unbound types", function() { + function assertMessage(fn, message) { + var e = assert.throws(cm.UnboundTypeError, fn); + assert.equal(message, e.message); + } + + test("calling function with unbound types produces error", function() { + assertMessage( + function() { + cm.getUnboundClass(); + }, + 'Cannot call getUnboundClass due to unbound types: 12UnboundClass'); + }); + + test("unbound base class produces error", function() { + assertMessage( + function() { + cm.getHasUnboundBase(); + }, + 'Cannot call getHasUnboundBase due to unbound types: 12UnboundClass'); + }); + + test("construct of class with unbound base", function() { + assertMessage( + function() { + new cm.HasUnboundBase; + }, 'Cannot construct HasUnboundBase due to unbound types: 12UnboundClass'); + }); + + test("unbound constructor argument", function() { + assertMessage( + function() { + new cm.HasConstructorUsingUnboundArgument(1); + }, + 'Cannot construct HasConstructorUsingUnboundArgument due to unbound types: 12UnboundClass'); + }); + + test("unbound constructor argument of class with unbound base", function() { + assertMessage( + function() { + new cm.HasConstructorUsingUnboundArgumentAndUnboundBase; + }, + 'Cannot construct HasConstructorUsingUnboundArgumentAndUnboundBase due to unbound types: 18SecondUnboundClass'); + }); + + test('class function with unbound argument', function() { + var x = new cm.BoundClass; + assertMessage( + function() { + x.method(); + }, 'Cannot call BoundClass.method due to unbound types: 12UnboundClass'); + x.delete(); + }); + + test('class class function with unbound argument', function() { + assertMessage( + function() { + cm.BoundClass.classfunction(); + }, 'Cannot call BoundClass.classfunction due to unbound types: 12UnboundClass'); + }); + + test('class property of unbound type', function() { + var x = new cm.BoundClass; + var y; + assertMessage( + function() { + y = x.property; + }, 'Cannot access BoundClass.property due to unbound types: 12UnboundClass'); + assertMessage( + function() { + x.property = 10; + }, 'Cannot access BoundClass.property due to unbound types: 12UnboundClass'); + x.delete(); + }); + + // todo: tuple elements + // todo: tuple element accessors + // todo: struct fields + }); + + } + + BaseFixture.extend("noncopyable", function() { + test('can call method on noncopyable object', function() { + var x = new cm.Noncopyable; + assert.equal('foo', x.method()); + x.delete(); + }); + }); + + BaseFixture.extend("function names", function() { + assert.equal('ValHolder', cm.ValHolder.name); + assert.equal('ValHolder$setVal', cm.ValHolder.prototype.setVal.name); + assert.equal('ValHolder$makeConst', cm.ValHolder.makeConst.name); + }); + + BaseFixture.extend("constants", function() { + assert.equal(10, cm.INT_CONSTANT); + assert.equal("some string", cm.STRING_CONSTANT); + assert.deepEqual([1, 2, 3, 4], cm.VALUE_TUPLE_CONSTANT); + assert.deepEqual({x:1,y:2,z:3,w:4}, cm.VALUE_STRUCT_CONSTANT); + }); +}); + +/* global run_all_tests */ +// If running as part of the emscripten test runner suite, and not as part of the IMVU suite, +// we launch the test execution from here. IMVU suite uses its own dedicated mechanism instead of this. +if (typeof run_all_tests !== "undefined") + run_all_tests(); diff --git a/tests/embind/embind_benchmark.cpp b/tests/embind/embind_benchmark.cpp new file mode 100644 index 00000000..80abc7e7 --- /dev/null +++ b/tests/embind/embind_benchmark.cpp @@ -0,0 +1,344 @@ +#include <stdio.h> +#include <emscripten.h> +#include <bind.h> +#include <memory> + +int counter = 0; + +extern "C" +{ + +int __attribute__((noinline)) get_counter() +{ + return counter; +} + +void __attribute__((noinline)) increment_counter() +{ + ++counter; +} + +int __attribute__((noinline)) sum_int(int v1, int v2, int v3, int v4, int v5, int v6, int v7, int v8, int v9) +{ + return v1 + v2 + v3 + v4 + v5 + v6 + v7 + v8 + v9; +} + +float __attribute__((noinline)) sum_float(float v1, float v2, float v3, float v4, float v5, float v6, float v7, float v8, float v9) +{ + return v1 + v2 + v3 + v4 + v5 + v6 + v7 + v8 + v9; +} + +int __attribute__((noinline)) returns_input(int i) +{ + return i; +} + +extern void increment_counter_benchmark_js(int N); +extern void returns_input_benchmark_js(); +extern void sum_int_benchmark_js(); +extern void sum_float_benchmark_js(); + +extern void increment_counter_benchmark_embind_js(int N); +extern void returns_input_benchmark_embind_js(); +extern void sum_int_benchmark_embind_js(); +extern void sum_float_benchmark_embind_js(); + +extern void increment_class_counter_benchmark_embind_js(int N); +extern void move_gameobjects_benchmark_embind_js(); +} + +class Vec3 +{ +public: + Vec3():x(0),y(0),z(0) {} + Vec3(float x_, float y_, float z_):x(x_),y(y_),z(z_) {} + float x,y,z; +}; + +Vec3 add(const Vec3 &lhs, const Vec3 &rhs) { return Vec3(lhs.x+rhs.x, lhs.y+rhs.y, lhs.z+rhs.z); } + +class Transform +{ +public: + Transform():scale(1) {} + + Vec3 pos; + Vec3 rot; + float scale; + + Vec3 __attribute__((noinline)) GetPosition() const { return pos; } + Vec3 __attribute__((noinline)) GetRotation() const { return rot; } + float __attribute__((noinline)) GetScale() const { return scale; } + + void __attribute__((noinline)) SetPosition(const Vec3 &pos_) { pos = pos_; } + void __attribute__((noinline)) SetRotation(const Vec3 &rot_) { rot = rot_; } + void __attribute__((noinline)) SetScale(float scale_) { scale = scale_; } +}; +typedef std::shared_ptr<Transform> TransformPtr; + +class GameObject +{ +public: + GameObject() + { + transform = std::make_shared<Transform>(); + } + std::shared_ptr<Transform> transform; + + TransformPtr __attribute__((noinline)) GetTransform() const { return transform; } +}; +typedef std::shared_ptr<GameObject> GameObjectPtr; + +GameObjectPtr create_game_object() +{ + return std::make_shared<GameObject>(); +} + +class Foo +{ +public: + Foo() + :class_counter(0) + { + } + + void __attribute__((noinline)) incr_global_counter() + { + ++counter; + } + + void __attribute__((noinline)) incr_class_counter() + { + ++class_counter; + } + + int class_counter_val() const + { + return class_counter; + } + + int class_counter; +}; + +EMSCRIPTEN_BINDINGS(benchmark) +{ + using namespace emscripten; + + class_<GameObject>("GameObject") + .smart_ptr<GameObjectPtr>() + .function("GetTransform", &GameObject::GetTransform); + + class_<Transform>("Transform") + .smart_ptr<TransformPtr>() + .function("GetPosition", &Transform::GetPosition) + .function("GetRotation", &Transform::GetRotation) + .function("GetScale", &Transform::GetScale) + .function("SetPosition", &Transform::SetPosition) + .function("SetRotation", &Transform::SetRotation) + .function("SetScale", &Transform::SetScale); + + value_tuple<Vec3>("Vec3") + .element(&Vec3::x) + .element(&Vec3::y) + .element(&Vec3::z); + + function("create_game_object", &create_game_object); + function("add", &add); + + function("get_counter", &get_counter); + function("increment_counter", &increment_counter); + function("returns_input", &returns_input); + function("sum_int", &sum_int); + function("sum_float", &sum_float); + + class_<Foo>("Foo") + .constructor<>() + .function("incr_global_counter", &Foo::incr_global_counter) + .function("incr_class_counter", &Foo::incr_class_counter) + .function("class_counter_val", &Foo::class_counter_val); +} + +void __attribute__((noinline)) emscripten_get_now_benchmark(int N) +{ + volatile float t = emscripten_get_now(); + for(int i = 0; i < N; ++i) + { + emscripten_get_now(); + emscripten_get_now(); + emscripten_get_now(); + emscripten_get_now(); + emscripten_get_now(); + emscripten_get_now(); + emscripten_get_now(); + emscripten_get_now(); + emscripten_get_now(); + emscripten_get_now(); + } + volatile float t2 = emscripten_get_now(); + printf("C++ emscripten_get_now %d iters: %f msecs.\n", N, (t2-t)); +} + +void __attribute__((noinline)) increment_counter_benchmark(int N) +{ + volatile float t = emscripten_get_now(); + for(int i = 0; i < N; ++i) + { + increment_counter(); + increment_counter(); + increment_counter(); + increment_counter(); + increment_counter(); + increment_counter(); + increment_counter(); + increment_counter(); + increment_counter(); + increment_counter(); + } + volatile float t2 = emscripten_get_now(); + printf("C++ increment_counter %d iters: %f msecs.\n", N, (t2-t)); +} + +void __attribute__((noinline)) increment_class_counter_benchmark(int N) +{ + Foo foo; + volatile float t = emscripten_get_now(); + for(int i = 0; i < N; ++i) + { + foo.incr_class_counter(); + foo.incr_class_counter(); + foo.incr_class_counter(); + foo.incr_class_counter(); + foo.incr_class_counter(); + foo.incr_class_counter(); + foo.incr_class_counter(); + foo.incr_class_counter(); + foo.incr_class_counter(); + foo.incr_class_counter(); + } + volatile float t2 = emscripten_get_now(); + printf("C++ increment_class_counter %d iters: %f msecs. result: %d\n", N, (t2-t), foo.class_counter); +} + +void __attribute__((noinline)) returns_input_benchmark() +{ + volatile int r = 0; + volatile float t = emscripten_get_now(); + for(int i = 0; i < 100000; ++i) + { + r += returns_input(i); + r += returns_input(i); + r += returns_input(i); + r += returns_input(i); + r += returns_input(i); + r += returns_input(i); + r += returns_input(i); + r += returns_input(i); + r += returns_input(i); + r += returns_input(i); + } + volatile float t2 = emscripten_get_now(); + printf("C++ returns_input 100000 iters: %f msecs.\n", (t2-t)); +} + +void __attribute__((noinline)) sum_int_benchmark() +{ + volatile float t = emscripten_get_now(); + volatile int r = 0; + for(int i = 0; i < 100000; ++i) + { + r += sum_int(i,2,3,4,5,6,7,8,9); + r += sum_int(i,2,3,4,5,6,7,8,9); + r += sum_int(i,2,3,4,5,6,7,8,9); + r += sum_int(i,2,3,4,5,6,7,8,9); + r += sum_int(i,2,3,4,5,6,7,8,9); + r += sum_int(i,2,3,4,5,6,7,8,9); + r += sum_int(i,2,3,4,5,6,7,8,9); + r += sum_int(i,2,3,4,5,6,7,8,9); + r += sum_int(i,2,3,4,5,6,7,8,9); + r += sum_int(i,2,3,4,5,6,7,8,9); + } + volatile float t2 = emscripten_get_now(); + printf("C++ sum_int 100000 iters: %f msecs.\n", (t2-t)); +} + +void __attribute__((noinline)) sum_float_benchmark() +{ + volatile float f = 0.f; + volatile float t = emscripten_get_now(); + for(int i = 0; i < 100000; ++i) + { + f += sum_float((float)i,2.f,3.f,4.f,5.f,6.f,7.f,8.f,9.f); + f += sum_float((float)i,2.f,3.f,4.f,5.f,6.f,7.f,8.f,9.f); + f += sum_float((float)i,2.f,3.f,4.f,5.f,6.f,7.f,8.f,9.f); + f += sum_float((float)i,2.f,3.f,4.f,5.f,6.f,7.f,8.f,9.f); + f += sum_float((float)i,2.f,3.f,4.f,5.f,6.f,7.f,8.f,9.f); + f += sum_float((float)i,2.f,3.f,4.f,5.f,6.f,7.f,8.f,9.f); + f += sum_float((float)i,2.f,3.f,4.f,5.f,6.f,7.f,8.f,9.f); + f += sum_float((float)i,2.f,3.f,4.f,5.f,6.f,7.f,8.f,9.f); + f += sum_float((float)i,2.f,3.f,4.f,5.f,6.f,7.f,8.f,9.f); + f += sum_float((float)i,2.f,3.f,4.f,5.f,6.f,7.f,8.f,9.f); + } + volatile float t2 = emscripten_get_now(); + printf("C++ sum_float 100000 iters: %f msecs.\n", (t2-t)); +} + +void __attribute__((noinline)) move_gameobjects_benchmark() +{ + const int N = 10000; + GameObjectPtr objects[N]; + for(int i = 0; i < N; ++i) + objects[i] = create_game_object(); + + volatile float t = emscripten_get_now(); + for(int i = 0; i < N; ++i) + { + TransformPtr t = objects[i]->GetTransform(); + Vec3 pos = add(t->GetPosition(), Vec3(2.f, 0.f, 1.f)); + Vec3 rot = add(t->GetRotation(), Vec3(0.1f, 0.2f, 0.3f)); + t->SetPosition(pos); + t->SetRotation(rot); + } + volatile float t2 = emscripten_get_now(); + + Vec3 accum; + for(int i = 0; i < N; ++i) + accum = add(add(accum, objects[i]->GetTransform()->GetPosition()), objects[i]->GetTransform()->GetRotation()); + printf("C++ move_gameobjects %d iters: %f msecs. Result: %f\n", N, (t2-t), accum.x+accum.y+accum.z); +} + +int main() +{ + for(int i = 1000; i <= 100000; i *= 10) + emscripten_get_now_benchmark(i); + + printf("\n"); + for(int i = 1000; i <= 100000; i *= 10) + { + increment_counter_benchmark(i); + increment_counter_benchmark_js(i); + increment_counter_benchmark_embind_js(i); + printf("\n"); + } + + for(int i = 1000; i <= 100000; i *= 10) + { + increment_class_counter_benchmark(i); + increment_class_counter_benchmark_embind_js(i); + printf("\n"); + } + + returns_input_benchmark(); + returns_input_benchmark_js(); + returns_input_benchmark_embind_js(); + printf("\n"); + sum_int_benchmark(); + sum_int_benchmark_js(); + sum_int_benchmark_embind_js(); + printf("\n"); + sum_float_benchmark(); + sum_float_benchmark_js(); + sum_float_benchmark_embind_js(); + printf("\n"); + move_gameobjects_benchmark(); + move_gameobjects_benchmark_embind_js(); +} diff --git a/tests/embind/embind_test.cpp b/tests/embind/embind_test.cpp index dc052d1a..f2359955 100644 --- a/tests/embind/embind_test.cpp +++ b/tests/embind/embind_test.cpp @@ -1,5 +1,6 @@ #include <string>
#include <malloc.h>
+#include <functional>
#include <emscripten/bind.h>
using namespace emscripten;
@@ -27,6 +28,10 @@ val emval_test_new_string() { return val("Hello everyone");
}
+std::string emval_test_get_string_from_val(val v) {
+ return v["key"].as<std::string>();
+}
+
val emval_test_new_object() {
val rv(val::object());
rv.set("foo", val("bar"));
@@ -54,22 +59,44 @@ unsigned emval_test_as_unsigned(val v) { }
unsigned emval_test_get_length(val v) {
- return v.get("length").as<unsigned>();
+ return v["length"].as<unsigned>();
}
double emval_test_add(char c, signed char sc, unsigned char uc, signed short ss, unsigned short us, signed int si, unsigned int ui, signed long sl, unsigned long ul, float f, double d) {
return c + sc + uc + ss + us + si + ui + sl + ul + f + d;
}
+float const_ref_adder(const int& i, const float& f) {
+ return i + f;
+}
+
unsigned emval_test_sum(val v) {
- unsigned length = v.get("length").as<unsigned>();
+ unsigned length = v["length"].as<unsigned>();
double rv = 0;
for (unsigned i = 0; i < length; ++i) {
- rv += v.get(i).as<double>();
+ rv += v[i].as<double>();
}
return rv;
}
+std::string get_non_ascii_string() {
+ char c[128 + 1];
+ c[128] = 0;
+ for (int i = 0; i < 128; ++i) {
+ c[i] = 128 + i;
+ }
+ return c;
+}
+
+std::wstring get_non_ascii_wstring() {
+ std::wstring ws(4, 0);
+ ws[0] = 10;
+ ws[1] = 1234;
+ ws[2] = 2345;
+ ws[3] = 65535;
+ return ws;
+}
+
std::string emval_test_take_and_return_const_char_star(const char* str) {
return str;
}
@@ -82,30 +109,74 @@ std::string emval_test_take_and_return_std_string_const_ref(const std::string& s return str;
}
+std::wstring take_and_return_std_wstring(std::wstring str) {
+ return str;
+}
+
+std::function<std::string (std::string)> emval_test_get_function_ptr() {
+ return emval_test_take_and_return_std_string;
+}
+
+std::string emval_test_take_and_call_functor(std::function<std::string(std::string)> func) {
+ return func("asdf");
+}
+
class ValHolder {
public:
ValHolder(val v)
- : v(v)
+ : v_(v)
{}
val getVal() const {
- return v;
+ return v_;
}
- void setVal(val v) {
- this->v = v;
+ val getValNonConst() {
+ return v_;
+ }
+
+ const val getConstVal() const {
+ return v_;
+ }
+
+ const val& getValConstRef() const {
+ return v_;
}
- int returnIntPlusFive( int x ) {
- return x + 5;
+ void setVal(val v) {
+ this->v_ = v;
}
static int some_class_method(int i) {
return i;
}
+ static const ValHolder* makeConst(val v) {
+ return new ValHolder(v);
+ }
+
+ static ValHolder makeValHolder(val v) {
+ return ValHolder(v);
+ }
+
+ static void set_via_raw_pointer(ValHolder* vh, val v) {
+ vh->setVal(v);
+ }
+
+ static val get_via_raw_pointer(const ValHolder* vh) {
+ return vh->getVal();
+ }
+
+ static void transfer_via_raw_pointer(ValHolder* target, const ValHolder* source) {
+ target->setVal(source->getVal());
+ }
+
+ static val getValNonMember(const ValHolder& target) {
+ return target.getVal();
+ }
+
private:
- val v;
+ val v_;
};
ValHolder emval_test_return_ValHolder() {
@@ -119,42 +190,612 @@ void emval_test_set_ValHolder_to_empty_object(ValHolder& vh) { class StringHolder {
public:
StringHolder(const std::string& s)
- : str(s)
+ : str_(s)
{}
void set(const std::string& s) {
- str = s;
+ str_ = s;
}
+
std::string get() const {
- return str;
+ return str_;
+ }
+
+ std::string& get_ref() {
+ return str_;
+ }
+
+ const std::string& get_const_ref() const {
+ return str_;
+ }
+
+private:
+ std::string str_;
+};
+
+class SharedPtrHolder {
+public:
+ SharedPtrHolder()
+ : ptr_(new StringHolder("a string"))
+ {}
+
+ std::shared_ptr<StringHolder> get() const {
+ return ptr_;
+ }
+
+ void set(std::shared_ptr<StringHolder> p) {
+ ptr_ = p;
+ }
+private:
+ std::shared_ptr<StringHolder> ptr_;
+};
+
+class VectorHolder {
+public:
+ VectorHolder() {
+ v_.push_back(StringHolder("string #1"));
+ v_.push_back(StringHolder("string #2"));
+ }
+
+ std::vector<StringHolder> get() const {
+ return v_;
+ }
+
+ void set(std::vector<StringHolder> vec) {
+ v_ = vec;
+ }
+
+private:
+ std::vector<StringHolder> v_;
+};
+
+class SmallClass {
+public:
+ SmallClass(): member(7) {};
+ int member;
+};
+
+class BigClass {
+public:
+ BigClass(): member(11) {};
+ int member;
+ int otherMember;
+ int yetAnotherMember;
+
+ int getMember() {
+ return member;
+ }
+};
+
+class ParentClass {
+public:
+ ParentClass(): bigClass() {};
+
+ BigClass bigClass;
+
+ const BigClass& getBigClass() {
+ return bigClass;
+ };
+};
+
+template<typename T>
+class TemplateClass {
+public:
+ TemplateClass(T a, T b, T c) {
+ members[0] = a;
+ members[1] = b;
+ members[2] = c;
+ };
+
+ const T getMember(int n) {
+ return members[n];
+ }
+
+protected:
+ T members[3];
+};
+
+class ContainsTemplatedMemberClass {
+public:
+ ContainsTemplatedMemberClass(): testTemplate(86, 87, 88) {};
+
+ TemplateClass<int> testTemplate;
+
+ const TemplateClass<int>& getTestTemplate() {
+ return testTemplate;
+ };
+};
+
+// Begin Inheritance Hierarchy Class Definitions
+
+class Base {
+public:
+ Base(): name("Base"),
+ member(0),
+ baseMember(0)
+ {}
+
+ std::string getClassName() const {
+ return name;
+ }
+ std::string getClassNameFromBase() const {
+ return name;
+ }
+ std::string getClassNameNotAvailableInDerivedClasses() {
+ // but wait -- if you act now we will throw in a SECOND base class method ABSOLUTELY FREE!!
+ return name;
+ }
+ void setMember(int value) {
+ member = value;
+ }
+ int getMember() {
+ return member;
+ }
+ void setBaseMember(int value) {
+ baseMember = value;
+ }
+ int getBaseMember() {
+ return baseMember;
+ }
+ std::string name;
+ int member;
+ int baseMember;
+};
+
+class SecondBase {
+public:
+ SecondBase()
+ : name("SecondBase"),
+ member(0),
+ secondBaseMember(0)
+ {}
+
+ std::string getClassName() const {
+ return name;
+ }
+ std::string getClassNameNotAvailableInDerivedClasses() {
+ return name;
+ }
+ std::string getClassNameFromSecondBase() const {
+ return name;
+ }
+ void setMember(int value) {
+ member = value;
+ }
+ int getMember() {
+ return member;
+ }
+ void setSecondBaseMember(int value) {
+ secondBaseMember = value;
+ }
+ int getSecondBaseMember() {
+ return secondBaseMember;
+ }
+ std::string name;
+ int member;
+ int secondBaseMember;
+};
+
+class Derived : public Base{
+public:
+ Derived()
+ : Base()
+ , member(0)
+ , name_("Derived")
+ {}
+
+ std::string getClassName() const {
+ return name_;
+ }
+ void setMember(int value) {
+ member = value;
+ }
+ int getMember() {
+ return member;
+ }
+ int member;
+private:
+ std::string name_;
+};
+
+class DerivedHolder {
+public:
+ DerivedHolder() {
+ derived_.reset();
+ }
+ void newDerived() {
+ deleteDerived();
+ derived_ = std::shared_ptr<Derived>(new Derived());
+ }
+ void deleteDerived() {
+ derived_.reset();
+ }
+ std::shared_ptr<Derived> getDerived() {
+ return derived_;
+ }
+ std::string getDerivedClassName() {
+ return derived_->getClassName();
+ }
+private:
+ std::shared_ptr<Derived> derived_;
+};
+
+class SiblingDerived : public Base {
+public:
+ SiblingDerived()
+ : Base(),
+ name_("SiblingDerived")
+ {}
+
+ std::string getClassName() const {
+ return name_;
+ }
+
+private:
+ std::string name_;
+};
+
+class MultiplyDerived : public Base, public SecondBase {
+public:
+ MultiplyDerived()
+ : Base(), SecondBase(),
+ name_("MultiplyDerived")
+ { instanceCount_ ++; }
+
+ ~MultiplyDerived()
+ { instanceCount_ --; }
+
+ std::string getClassName() const {
+ return name_;
+ }
+
+ static int getInstanceCount() {
+ return instanceCount_;
+ }
+private:
+ std::string name_;
+ static int instanceCount_;
+};
+int MultiplyDerived::instanceCount_ = 0;
+
+class DerivedTwice : public Derived {
+public:
+ DerivedTwice()
+ : Derived(),
+ name_("DerivedTwice")
+ {}
+
+ std::string getClassName() const {
+ return name_;
+ }
+private:
+ std::string name_;
+};
+
+class DerivedTwiceNotBound : public Derived {
+public:
+ DerivedTwiceNotBound()
+ : Derived(),
+ name_("DerivedTwiceNotBound")
+ {}
+
+ std::string getClassName() const {
+ return name_;
+ }
+private:
+ std::string name_;
+};
+
+class DerivedThrice: public DerivedTwiceNotBound {
+public:
+ DerivedThrice()
+ : DerivedTwiceNotBound(),
+ name_("DerivedThrice")
+ {}
+
+ std::string getClassName() const {
+ return name_;
+ }
+private:
+ std::string name_;
+};
+
+class DerivedFourTimesNotBound: public DerivedThrice {
+public:
+ DerivedFourTimesNotBound()
+ : DerivedThrice(),
+ name_("DerivedFourTimesNotBound")
+ {}
+
+ std::string getClassName() const {
+ return name_;
+ }
+private:
+ std::string name_;
+};
+
+class PolyBase {
+public:
+ PolyBase(const std::string& s)
+ : str_(s),
+ name_("PolyBase")
+ {}
+
+ PolyBase(): name_("PolyBase") {}
+
+ virtual ~PolyBase() {}
+
+ virtual std::string virtualGetClassName() const {
+ return name_;
+ }
+
+ std::string getClassName() const {
+ return name_;
+ }
+
+private:
+ std::string str_;
+ std::string name_;
+};
+
+class PolySecondBase {
+public:
+ PolySecondBase(): name_("PolySecondBase")
+ {}
+
+ virtual ~PolySecondBase() {}
+
+ std::string getClassName() const {
+ return name_;
+ }
+private:
+ std::string name_;
+};
+
+class PolyDerived : public PolyBase{
+public:
+ PolyDerived()
+ : PolyBase("PolyDerived"),
+ name_("PolyDerived")
+ {}
+
+ std::string virtualGetClassName() const {
+ return name_;
+ }
+
+ std::string getClassName() const {
+ return name_;
+ }
+
+ static void setPtrDerived() {
+ ptr_ = std::shared_ptr<PolyDerived>(new PolyDerived());
+ }
+
+ static void releasePtr() {
+ ptr_.reset();
+ }
+
+ static std::string getPtrClassName() {
+ return ptr_->getClassName();
+ }
+
+ static std::shared_ptr<PolyBase> getPtr() {
+ return ptr_;
+ }
+
+private:
+ std::string name_;
+ static std::shared_ptr<PolyBase> ptr_;
+};
+std::shared_ptr<PolyBase> PolyDerived::ptr_;
+
+class PolySiblingDerived : public PolyBase {
+public:
+ PolySiblingDerived()
+ : PolyBase(),
+ name_("PolySiblingDerived")
+ {}
+
+ std::string getClassName() const {
+ return name_;
+ }
+private:
+ std::string name_;
+};
+
+class PolyMultiplyDerived : public PolyBase, public PolySecondBase {
+public:
+ PolyMultiplyDerived()
+ : PolyBase(), PolySecondBase(),
+ name_("PolyMultiplyDerived")
+ {}
+
+ std::string getClassName() const {
+ return name_;
+ }
+private:
+ std::string name_;
+};
+
+class PolyDerivedTwiceWithoutSmartPointer: public PolyDerived {
+public:
+ PolyDerivedTwiceWithoutSmartPointer()
+ : PolyDerived(),
+ name_("PolyDerivedTwiceWithoutSmartPointer")
+ {}
+
+ std::string getClassName() const {
+ return name_;
+ }
+private:
+ std::string name_;
+};
+
+class PolyDerivedTwiceNotBound : public PolyDerived {
+public:
+ PolyDerivedTwiceNotBound()
+ : PolyDerived(),
+ name_("PolyDerivedTwiceNotBound")
+ {}
+
+ std::string getClassName() const {
+ return name_;
+ }
+private:
+ std::string name_;
+};
+
+class PolyDerivedThrice: public PolyDerivedTwiceNotBound {
+public:
+ PolyDerivedThrice()
+ : PolyDerivedTwiceNotBound(),
+ name_("PolyDerivedThrice")
+ {}
+
+ std::string getClassName() const {
+ return name_;
+ }
+private:
+ std::string name_;
+};
+
+class PolyDerivedFourTimesNotBound: public PolyDerivedThrice {
+public:
+ PolyDerivedFourTimesNotBound()
+ : PolyDerivedThrice(),
+ name_("PolyDerivedFourTimesNotBound")
+ {}
+
+ std::string getClassName() const {
+ return name_;
}
+private:
+ std::string name_;
+};
+
+class PolyDiamondBase {
+public:
+ PolyDiamondBase():
+ name_("PolyBase")
+ {}
+ ~PolyDiamondBase() {}
+ std::string getClassName() const {
+ return name_;
+ }
private:
- std::string str;
+ std::string name_;
+};
+
+class PolyDiamondDerived: public PolyDiamondBase {
+public:
+ PolyDiamondDerived()
+ : PolyDiamondBase(),
+ name_("PolyDiamondDerived")
+ {}
+
+ std::string getClassName() const {
+ return name_;
+ }
+private:
+ std::string name_;
+};
+
+class PolyDiamondSiblingDerived: public PolyDiamondBase {
+public:
+ PolyDiamondSiblingDerived()
+ : PolyDiamondBase(),
+ name_("PolyDiamondSiblingDerived")
+ {}
+
+ std::string getClassName() const {
+ return name_;
+ }
+private:
+ std::string name_;
+};
+
+class PolyDiamondMultiplyDerived: public PolyDiamondDerived, public PolyDiamondSiblingDerived {
+public:
+ PolyDiamondMultiplyDerived()
+ : PolyDiamondDerived(), PolyDiamondSiblingDerived(),
+ name_("PolyDiamondMultiplyDerived")
+ {}
+
+ std::string getClassName() const {
+ return name_;
+ }
+private:
+ std::string name_;
+};
+
+// End Inheritance Hierarchy Class Definitions
+
+std::map<std::string, int> embind_test_get_string_int_map() {
+ std::map<std::string, int> m;
+
+ m["one"] = 1;
+ m["two"] = 2;
+
+ return m;
+};
+
+struct Vector {
+ Vector() = delete;
+
+ Vector(float x_, float y_, float z_, float w_)
+ : x(x_)
+ , y(y_)
+ , z(z_)
+ , w(w_)
+ {}
+
+ float x, y, z, w;
+
+ float& operator[](int i) {
+ return (&x)[i];
+ }
+
+ const float& operator[](int i) const {
+ return (&x)[i];
+ }
+
+ float getY() const {
+ return y;
+ }
+ void setY(float _y) {
+ y = _y;
+ }
+};
+
+struct DummyDataToTestPointerAdjustment {
+ std::string dummy;
+};
+
+struct TupleVector : DummyDataToTestPointerAdjustment, Vector {
+ TupleVector(): Vector(0, 0, 0, 0) {}
+ TupleVector(float x, float y, float z, float w): Vector(x, y, z, w) {}
};
-struct TupleVector {
- float x, y, z;
+struct StructVector : DummyDataToTestPointerAdjustment, Vector {
+ StructVector(): Vector(0, 0, 0, 0) {}
+ StructVector(float x, float y, float z, float w): Vector(x, y, z, w) {}
};
-float readTupleVectorZ(const TupleVector& v) {
+float readVectorZ(const Vector& v) {
return v.z;
}
-void writeTupleVectorZ(TupleVector& v, float z) {
+void writeVectorZ(Vector& v, float z) {
v.z = z;
}
struct TupleVectorTuple {
- TupleVector v;
+ TupleVector v = TupleVector(0, 0, 0, 0);
};
TupleVector emval_test_return_TupleVector() {
- TupleVector cv;
- cv.x = 1;
- cv.y = 2;
- cv.z = 3;
- return cv;
+ return TupleVector(1, 2, 3, 4);
}
TupleVector emval_test_take_and_return_TupleVector(TupleVector v) {
@@ -167,16 +808,8 @@ TupleVectorTuple emval_test_return_TupleVectorTuple() { return cvt;
}
-struct StructVector {
- float x, y, z;
-};
-
StructVector emval_test_return_StructVector() {
- StructVector v;
- v.x = 1;
- v.y = 2;
- v.z = 3;
- return v;
+ return StructVector(1, 2, 3, 4);
}
StructVector emval_test_take_and_return_StructVector(StructVector v) {
@@ -187,6 +820,11 @@ struct CustomStruct { CustomStruct()
: field(10)
{}
+
+ const int& getField() const {
+ return field;
+ }
+
int field;
};
@@ -210,34 +848,645 @@ EnumClass emval_test_take_and_return_EnumClass(EnumClass e) { return e;
}
-class Interface {
+void emval_test_call_function(val v, int i, float f, TupleVector tv, StructVector sv) {
+ v(i, f, tv, sv);
+}
+
+std::unique_ptr<ValHolder> emval_test_return_unique_ptr() {
+ return std::unique_ptr<ValHolder>(new ValHolder(val::object()));
+}
+
+std::shared_ptr<ValHolder> emval_test_return_shared_ptr() {
+ return std::shared_ptr<ValHolder>(new ValHolder(val::object()));
+}
+
+std::shared_ptr<ValHolder> emval_test_return_empty_shared_ptr() {
+ return std::shared_ptr<ValHolder>();
+}
+
+bool emval_test_is_shared_ptr_null(std::shared_ptr<ValHolder> p) {
+ return !p;
+}
+
+static SmallClass smallClass;
+static BigClass bigClass;
+
+SmallClass embind_test_return_small_class_instance() {
+ return smallClass;
+}
+
+BigClass embind_test_return_big_class_instance() {
+ return bigClass;
+}
+
+int embind_test_accept_small_class_instance(SmallClass c) {
+ return c.member;
+}
+
+int embind_test_accept_big_class_instance(BigClass c) {
+ return c.member;
+}
+
+// Begin Inheritance Hierarchy Test Wrappers
+
+Base* embind_test_return_raw_base_ptr() {
+ return new Base();
+}
+
+Base* embind_test_return_raw_derived_ptr_as_base() {
+ return new Derived();
+}
+
+Base* embind_test_return_raw_sibling_derived_ptr_as_base() {
+ return new SiblingDerived();
+}
+
+PolyBase* embind_test_return_raw_polymorphic_derived_ptr_as_base() {
+ return new PolyDerived();
+}
+
+PolyBase* embind_test_return_raw_polymorphic_sibling_derived_ptr_as_base() {
+ return new PolySiblingDerived();
+}
+
+PolyBase* embind_test_return_raw_polymorphic_multiply_derived_ptr_as_base() {
+ return new PolyMultiplyDerived();
+}
+
+PolySecondBase* embind_test_return_raw_polymorphic_multiply_derived_ptr_as_second_base() {
+ return new PolyMultiplyDerived();
+}
+
+PolyBase* embind_test_return_raw_polymorphic_derived_four_times_not_bound_as_base() {
+ return new PolyDerivedFourTimesNotBound();
+}
+
+std::shared_ptr<Base> embind_test_return_smart_base_ptr() {
+ return std::shared_ptr<Base>(new Base());
+}
+
+std::shared_ptr<PolyBase> embind_test_return_smart_polymorphic_base_ptr() {
+ return std::shared_ptr<PolyBase>(new PolyBase("PolyBase"));
+}
+
+std::shared_ptr<Derived> embind_test_return_smart_derived_ptr() {
+ return std::shared_ptr<Derived>(new Derived());
+}
+
+std::shared_ptr<SiblingDerived> embind_test_return_smart_sibling_derived_ptr() {
+ return std::shared_ptr<SiblingDerived>(new SiblingDerived());
+}
+
+std::shared_ptr<MultiplyDerived> embind_test_return_smart_multiply_derived_ptr() {
+ return std::shared_ptr<MultiplyDerived>(new MultiplyDerived());
+}
+
+std::shared_ptr<DerivedThrice> embind_test_return_smart_derived_thrice_ptr() {
+ return std::shared_ptr<DerivedThrice>(new DerivedThrice());
+}
+
+std::shared_ptr<PolyDerived> embind_test_return_smart_polymorphic_derived_ptr() {
+ return std::shared_ptr<PolyDerived>(new PolyDerived());
+}
+
+std::shared_ptr<PolySiblingDerived> embind_test_return_smart_polymorphic_sibling_derived_ptr() {
+ return std::shared_ptr<PolySiblingDerived>(new PolySiblingDerived());
+}
+
+std::shared_ptr<PolyMultiplyDerived> embind_test_return_smart_polymorphic_multiply_derived_ptr() {
+ return std::shared_ptr<PolyMultiplyDerived>(new PolyMultiplyDerived());
+}
+
+std::shared_ptr<PolyBase> embind_test_return_poly_derived_twice_without_smart_pointer_as_poly_base() {
+ return std::shared_ptr<PolyBase>(new PolyDerivedTwiceWithoutSmartPointer());
+}
+
+std::shared_ptr<PolyDerivedThrice> embind_test_return_smart_poly_derived_thrice_ptr() {
+ return std::shared_ptr<PolyDerivedThrice>(new PolyDerivedThrice());
+}
+
+std::shared_ptr<PolyBase> embind_test_return_smart_derived_ptr_as_base() {
+ return std::shared_ptr<PolyBase>(new PolyDerived());
+}
+
+val embind_test_return_smart_derived_ptr_as_val() {
+ return val(std::shared_ptr<PolyBase>(new PolyDerived()));
+}
+
+std::shared_ptr<PolyBase> embind_test_return_smart_polymorphic_derived_ptr_as_base() {
+ return std::shared_ptr<PolyBase>(new PolyDerived());
+}
+
+std::shared_ptr<PolyBase> embind_test_return_smart_polymorphic_sibling_derived_ptr_as_base() {
+ return std::shared_ptr<PolyBase>(new PolySiblingDerived());
+}
+
+std::string embind_test_get_class_name_via_base_ptr(Base *p) {
+ return p->getClassName();
+}
+
+std::string embind_test_get_class_name_via_second_base_ptr(SecondBase *p) {
+ return p->getClassName();
+}
+
+std::string embind_test_get_class_name_via_polymorphic_base_ptr(PolyBase *p) {
+ return p->getClassName();
+}
+
+std::string embind_test_get_class_name_via_polymorphic_second_base_ptr(PolySecondBase *p) {
+ return p->getClassName();
+}
+
+std::string embind_test_get_class_name_via_smart_base_ptr(std::shared_ptr<Base> p) {
+ return p->getClassName();
+}
+
+std::string embind_test_get_class_name_via_reference_to_smart_base_ptr(std::shared_ptr<Base>& p) {
+ return p->getClassName();
+}
+
+std::string embind_test_get_class_name_via_smart_second_base_ptr(std::shared_ptr<SecondBase> p) {
+ return p->getClassName();
+}
+
+std::string embind_test_get_class_name_via_smart_polymorphic_base_ptr(std::shared_ptr<PolyBase> p) {
+ return p->getClassName();
+}
+
+std::string embind_test_get_virtual_class_name_via_smart_polymorphic_base_ptr(std::shared_ptr<PolyBase> p) {
+ return p->virtualGetClassName();
+}
+
+std::string embind_test_get_class_name_via_smart_polymorphic_second_base_ptr(std::shared_ptr<PolySecondBase> p) {
+ return p->getClassName();
+}
+
+void embind_modify_smart_pointer_passed_by_reference(std::shared_ptr<Base>& p) {
+ p = std::shared_ptr<Base>(new Base());
+ p->name = "Changed";
+}
+
+void embind_attempt_to_modify_smart_pointer_when_passed_by_value(std::shared_ptr<Base> p) {
+ p = std::shared_ptr<Base>(new Base());
+ p->name = "Changed";
+}
+
+static std::shared_ptr<Base> savedBasePointer;
+
+void embind_save_smart_base_pointer(std::shared_ptr<Base> p) {
+ savedBasePointer = p;
+}
+
+// End Inheritance Hierarchy Test Wrappers
+
+std::vector<int> emval_test_return_vector() {
+ int myints[] = { 10, 20, 30 };
+ return std::vector<int>(myints, myints + sizeof(myints) / sizeof(int));
+}
+
+std::vector<std::vector<int> > emval_test_return_vector_of_vectors() {
+ int myints1[] = { 10, 20, 30 };
+ int myints2[] = { 40, 50, 60 };
+ std::vector<int> vec1(myints1, myints1 + sizeof(myints1) / sizeof(int));
+ std::vector<int> vec2(myints2, myints2 + sizeof(myints2) / sizeof(int));
+ std::vector<std::vector<int>>vec3;
+ vec3.emplace_back(vec1);
+ vec3.emplace_back(vec2);
+ return vec3;
+}
+
+std::vector<std::shared_ptr<StringHolder>> emval_test_return_shared_ptr_vector() {
+ std::vector<std::shared_ptr<StringHolder>> sharedStrVector;
+ sharedStrVector.push_back(std::shared_ptr<StringHolder>(new StringHolder("string #1")));
+ sharedStrVector.push_back(std::shared_ptr<StringHolder>(new StringHolder("string #2")));
+
+ return sharedStrVector;
+}
+
+void test_string_with_vec(const std::string& p1, std::vector<std::string>& v1) {
+ // THIS DOES NOT WORK -- need to get as val and then call vecFromJSArray
+ printf("%s\n", p1.c_str());
+}
+
+val embind_test_new_Object() {
+ return val::global("Object").new_();
+}
+
+val embind_test_new_factory(val factory, val argument) {
+ return factory.new_(10, std::string("hello"), argument);
+}
+
+class AbstractClass {
public:
- virtual int method() = 0;
- virtual TupleInStruct method2(const TupleInStruct& arg1, float arg2) = 0;
- virtual void method3() = 0;
+ virtual ~AbstractClass() {}
+ virtual std::string abstractMethod() const = 0;
+ virtual std::string optionalMethod(std::string s) const {
+ return "optional" + s;
+ }
+};
+
+EMSCRIPTEN_SYMBOL(optionalMethod);
+
+class AbstractClassWrapper : public wrapper<AbstractClass> {
+public:
+ EMSCRIPTEN_WRAPPER(AbstractClassWrapper);
+
+ std::string abstractMethod() const {
+ return call<std::string>("abstractMethod");
+ }
+ std::string optionalMethod(std::string s) const {
+ return optional_call<std::string>(optionalMethod_symbol, [&] {
+ return AbstractClass::optionalMethod(s);
+ }, s);
+ }
+};
+
+class ConcreteClass : public AbstractClass {
+ std::string abstractMethod() const {
+ return "from concrete";
+ }
};
-int emval_test_call_method(Interface& i) {
- return i.method();
+std::shared_ptr<AbstractClass> getAbstractClass() {
+ return std::make_shared<ConcreteClass>();
}
-TupleInStruct emval_test_call_method2(Interface& i, const TupleInStruct& arg1, float arg2) {
- return i.method2(arg1, arg2);
+std::string callAbstractMethod(AbstractClass& ac) {
+ return ac.abstractMethod();
}
-void emval_test_call_method3(Interface& i) {
- i.method3();
+std::string callOptionalMethod(AbstractClass& ac, std::string s) {
+ return ac.optionalMethod(s);
}
-void emval_test_call_function(val v, int i, float f, TupleVector tv, StructVector sv) {
- v(i, f, tv, sv);
+class HasExternalConstructor {
+public:
+ HasExternalConstructor(const std::string& str)
+ : m(str)
+ {}
+
+ std::string getString() const {
+ return m;
+ }
+
+ std::string m;
+};
+
+HasExternalConstructor* createHasExternalConstructor(const std::string& str) {
+ return new HasExternalConstructor(str);
}
-EMSCRIPTEN_BINDINGS(([]() {
- function("mallinfo", &emval_test_mallinfo);
+template<typename T>
+class CustomSmartPtr {
+public:
+ CustomSmartPtr()
+ : CustomSmartPtr(nullptr)
+ {
+ std::fill(d_, d_ + N_, Valid);
+ }
+
+ explicit CustomSmartPtr(T* t)
+ : ptr_(t)
+ {
+ std::fill(d_, d_ + N_, Valid);
+ }
+
+ CustomSmartPtr(const CustomSmartPtr& other)
+ : ptr_(other.ptr_)
+ {
+ other.verify();
+ std::fill(d_, d_ + N_, Valid);
+ if (ptr_) {
+ ++(ptr_->refcount);
+ }
+ }
+
+ ~CustomSmartPtr() {
+ verify();
+ std::fill(d_, d_ + N_, Deleted);
+
+ if (ptr_ && --ptr_->refcount == 0) {
+ delete ptr_;
+ }
+ }
+
+ T* get_raw() const {
+ return ptr_;
+ }
+
+private:
+ void verify() const {
+ for (size_t i = 0; i < N_; ++i) {
+ if (d_[i] != Valid) {
+ abort();
+ }
+ }
+ }
+
+ enum {
+ Valid = 255,
+ Deleted = 127,
+ };
+ static constexpr size_t N_ = 1000000;
+ unsigned char d_[N_];
+ T* ptr_;
+
+ CustomSmartPtr& operator=(const CustomSmartPtr&) = delete;
+};
+
+class HeldBySmartPtr {
+public:
+ HeldBySmartPtr(int i, const std::string& s)
+ : i(i)
+ , s(s)
+ {}
+
+ static CustomSmartPtr<HeldBySmartPtr> newCustomPtr(int i, const std::string& s) {
+ return CustomSmartPtr<HeldBySmartPtr>(new HeldBySmartPtr(i, s));
+ }
+
+ int refcount = 1;
+ int i;
+ std::string s;
+};
+
+HeldBySmartPtr takesHeldBySmartPtr(HeldBySmartPtr p) {
+ return p;
+}
+std::shared_ptr<HeldBySmartPtr> takesHeldBySmartPtrSharedPtr(std::shared_ptr<HeldBySmartPtr> p) {
+ return p;
+}
+
+namespace emscripten {
+ template<typename T>
+ struct smart_ptr_trait<CustomSmartPtr<T>> {
+ typedef T element_type;
+
+ static sharing_policy get_sharing_policy() {
+ return sharing_policy::NONE;
+ }
+ static T* get(const CustomSmartPtr<T>& p) {
+ return p.get_raw();
+ }
+
+ static CustomSmartPtr<T> share(const CustomSmartPtr<T>& r, T* ptr) {
+ ++ptr->refcount; // implement an adopt API?
+ return CustomSmartPtr<T>(ptr);
+ }
+ };
+}
+
+typedef CustomSmartPtr<class HeldByCustomSmartPtr> HeldByCustomSmartPtrPtr;
+
+class HeldByCustomSmartPtr {
+public:
+ HeldByCustomSmartPtr(int i, const std::string& s)
+ : i(i)
+ , s(s)
+ {}
+
+ static HeldByCustomSmartPtrPtr create(int i, const std::string& s) {
+ return HeldByCustomSmartPtrPtr(new HeldByCustomSmartPtr(i, s));
+ }
+
+ static std::shared_ptr<HeldByCustomSmartPtr> createSharedPtr(int i, const std::string& s) {
+ return std::make_shared<HeldByCustomSmartPtr>(i, s);
+ };
+
+ int refcount = 1;
+ int i;
+ std::string s;
+};
+
+HeldByCustomSmartPtr* passThroughRawPtr(HeldByCustomSmartPtr* p) {
+ return p;
+}
+HeldByCustomSmartPtrPtr passThroughCustomSmartPtr(HeldByCustomSmartPtrPtr p) {
+ return p;
+}
+
+struct Base1 {
+public:
+ Base1(): field1("Base1") {}
+ std::string field1;
+
+ std::string getField() const {
+ return field1;
+ }
+};
+
+struct Base2 {
+public:
+ Base2(): field2("Base2") {}
+ std::string field2;
+
+ std::string getField() const {
+ return field2;
+ }
+};
+
+struct HasTwoBases : public Base1, public Base2 {
+};
+
+val get_module_property(const std::string& s) {
+ return val::module_property(s.c_str());
+}
+
+std::string char_to_string(char ch) {
+ char str[256];
+ sprintf(str, "%d", (int)ch);
+ return str;
+}
+
+std::string signed_char_to_string(signed char ch) {
+ char str[256];
+ sprintf(str, "%hhd", ch);
+ return str;
+}
+
+std::string unsigned_char_to_string(unsigned char ch) {
+ char str[256];
+ sprintf(str, "%hhu", ch);
+ return str;
+}
+
+std::string short_to_string(short val) {
+ char str[256];
+ sprintf(str, "%hd", val);
+ return str;
+}
+
+std::string unsigned_short_to_string(unsigned short val) {
+ char str[256];
+ sprintf(str, "%hu", val);
+ return str;
+}
+
+std::string int_to_string(int val) {
+ char str[256];
+ sprintf(str, "%d", val);
+ return str;
+}
+
+std::string unsigned_int_to_string(unsigned int val) {
+ char str[256];
+ sprintf(str, "%u", val);
+ return str;
+}
+
+std::string long_to_string(long val) {
+ char str[256];
+ sprintf(str, "%ld", val);
+ return str;
+}
+
+std::string unsigned_long_to_string(unsigned long val) {
+ char str[256];
+ sprintf(str, "%lu", val);
+ return str;
+}
+
+class MultipleCtors {
+public:
+ int value;
+
+ MultipleCtors(int i) {
+ value = 1;
+ assert(i == 10);
+ }
+ MultipleCtors(int i, int j) {
+ value = 2;
+ assert(i == 20);
+ assert(j == 20);
+ }
+ MultipleCtors(int i, int j, int k) {
+ value = 3;
+ assert(i == 30);
+ assert(j == 30);
+ assert(k == 30);
+ }
+
+ int WhichCtorCalled() const {
+ return value;
+ }
+};
+
+class MultipleOverloads {
+public:
+ MultipleOverloads() {}
+
+ int value;
+ static int staticValue;
+
+ int Func(int i) {
+ assert(i == 10);
+ value = 1;
+ return 1;
+ }
+ int Func(int i, int j) {
+ assert(i == 20);
+ assert(j == 20);
+ value = 2;
+ return 2;
+ }
+
+ int WhichFuncCalled() const {
+ return value;
+ }
+
+ static int StaticFunc(int i) {
+ assert(i == 10);
+ staticValue = 1;
+ return 1;
+ }
+ static int StaticFunc(int i, int j) {
+ assert(i == 20);
+ assert(j == 20);
+ staticValue = 2;
+ return 2;
+ }
+
+ static int WhichStaticFuncCalled() {
+ return staticValue;
+ }
+};
+
+class MultipleOverloadsDerived : public MultipleOverloads {
+public:
+ MultipleOverloadsDerived() {}
+
+ int Func(int i, int j, int k) {
+ assert(i == 30);
+ assert(j == 30);
+ assert(k == 30);
+ value = 3;
+ return 3;
+ }
+ int Func(int i, int j, int k, int l) {
+ assert(i == 40);
+ assert(j == 40);
+ assert(k == 40);
+ assert(l == 40);
+ value = 4;
+ return 4;
+ }
+
+ static int StaticFunc(int i, int j, int k) {
+ assert(i == 30);
+ assert(j == 30);
+ assert(k == 30);
+ staticValue = 3;
+ return 3;
+ }
+ static int StaticFunc(int i, int j, int k, int l) {
+ assert(i == 40);
+ assert(j == 40);
+ assert(k == 40);
+ assert(l == 40);
+ staticValue = 4;
+ return 4;
+ }
+};
+
+int overloaded_function(int i)
+{
+ assert(i == 10);
+ return 1;
+}
+
+int overloaded_function(int i, int j)
+{
+ assert(i == 20);
+ assert(j == 20);
+ return 2;
+}
+
+EMSCRIPTEN_BINDINGS(constants) {
+ constant("INT_CONSTANT", 10);
+ constant("STRING_CONSTANT", std::string("some string"));
+
+ TupleVector tv(1, 2, 3, 4);
+ constant("VALUE_TUPLE_CONSTANT", tv);
+
+ StructVector sv(1, 2, 3, 4);
+ constant("VALUE_STRUCT_CONSTANT", sv);
+}
+
+EMSCRIPTEN_BINDINGS(tests) {
+ register_vector<int>("IntegerVector");
+ register_vector<char>("CharVector");
+ register_vector<unsigned>("VectorUnsigned");
+ register_vector<unsigned char>("VectorUnsignedChar");
+ register_vector<std::string>("StringVector");
+ register_vector<emscripten::val>("EmValVector");
+ register_vector<float>("FloatVector");
+ register_vector<std::vector<int>>("IntegerVectorVector");
+
+ function("mallinfo", &emval_test_mallinfo);
function("emval_test_new_integer", &emval_test_new_integer);
function("emval_test_new_string", &emval_test_new_string);
+ function("emval_test_get_string_from_val", &emval_test_get_string_from_val);
function("emval_test_new_object", &emval_test_new_object);
function("emval_test_passthrough_unsigned", &emval_test_passthrough_unsigned);
function("emval_test_passthrough", &emval_test_passthrough);
@@ -247,19 +1496,23 @@ EMSCRIPTEN_BINDINGS(([]() { function("emval_test_as_unsigned", &emval_test_as_unsigned);
function("emval_test_get_length", &emval_test_get_length);
function("emval_test_add", &emval_test_add);
+ function("const_ref_adder", &const_ref_adder);
function("emval_test_sum", &emval_test_sum);
+ function("get_non_ascii_string", &get_non_ascii_string);
+ function("get_non_ascii_wstring", &get_non_ascii_wstring);
//function("emval_test_take_and_return_const_char_star", &emval_test_take_and_return_const_char_star);
function("emval_test_take_and_return_std_string", &emval_test_take_and_return_std_string);
function("emval_test_take_and_return_std_string_const_ref", &emval_test_take_and_return_std_string_const_ref);
+ function("take_and_return_std_wstring", &take_and_return_std_wstring);
//function("emval_test_take_and_return_CustomStruct", &emval_test_take_and_return_CustomStruct);
value_tuple<TupleVector>("TupleVector")
.element(&TupleVector::x)
- .element(&TupleVector::y)
- //.element(&TupleVector::z)
- .element(&readTupleVectorZ, &writeTupleVectorZ)
+ .element(&Vector::getY, &Vector::setY)
+ .element(&readVectorZ, &writeVectorZ)
+ .element(index<3>())
;
function("emval_test_return_TupleVector", &emval_test_return_TupleVector);
@@ -273,8 +1526,9 @@ EMSCRIPTEN_BINDINGS(([]() { value_struct<StructVector>("StructVector")
.field("x", &StructVector::x)
- .field("y", &StructVector::y)
- .field("z", &StructVector::z)
+ .field("y", &Vector::getY, &Vector::setY)
+ .field("z", &readVectorZ, &writeVectorZ)
+ .field("w", index<3>())
;
function("emval_test_return_StructVector", &emval_test_return_StructVector);
@@ -287,24 +1541,296 @@ EMSCRIPTEN_BINDINGS(([]() { function("emval_test_take_and_return_TupleInStruct", &emval_test_take_and_return_TupleInStruct);
class_<ValHolder>("ValHolder")
+ .smart_ptr<std::shared_ptr<ValHolder>>()
.constructor<val>()
- .method("getVal", &ValHolder::getVal)
- .method("setVal", &ValHolder::setVal)
- .method("returnIntPlusFive", &ValHolder::returnIntPlusFive)
- .classmethod("some_class_method", &ValHolder::some_class_method)
+ .function("getVal", &ValHolder::getVal)
+ .function("getValNonConst", &ValHolder::getValNonConst)
+ .function("getConstVal", &ValHolder::getConstVal)
+ .function("getValConstRef", &ValHolder::getValConstRef)
+ .function("setVal", &ValHolder::setVal)
+ .property("val", &ValHolder::getVal, &ValHolder::setVal)
+ .property("val_readonly", &ValHolder::getVal)
+ .class_function("makeConst", &ValHolder::makeConst, allow_raw_pointer<ret_val>())
+ .class_function("makeValHolder", &ValHolder::makeValHolder)
+ .class_function("some_class_method", &ValHolder::some_class_method)
+ .class_function("set_via_raw_pointer",
+ &ValHolder::set_via_raw_pointer,
+ allow_raw_pointer<arg<0>>())
+ .class_function("get_via_raw_pointer",
+ &ValHolder::get_via_raw_pointer,
+ allow_raw_pointer<arg<0>>())
+ .class_function("transfer_via_raw_pointer",
+ &ValHolder::transfer_via_raw_pointer,
+ allow_raw_pointers())
+
+ // non-member method
+ .function("setEmpty", &emval_test_set_ValHolder_to_empty_object)
+ .function("getValNonMember", &ValHolder::getValNonMember)
;
+
function("emval_test_return_ValHolder", &emval_test_return_ValHolder);
function("emval_test_set_ValHolder_to_empty_object", &emval_test_set_ValHolder_to_empty_object);
+ class_<std::function<std::string(std::string)>>("StringFunctorString")
+ .constructor<>()
+ .function("opcall", &std::function<std::string(std::string)>::operator())
+ ;
+
+ function("emval_test_get_function_ptr", &emval_test_get_function_ptr);
+ function("emval_test_take_and_call_functor", &emval_test_take_and_call_functor);
+
class_<StringHolder>("StringHolder")
+ .smart_ptr<std::shared_ptr<StringHolder>>()
.constructor<std::string>()
- .method("set", &StringHolder::set)
- .method("get", &StringHolder::get)
+ .function("set", &StringHolder::set)
+ .function("get", &StringHolder::get)
+ .function("get_const_ref", &StringHolder::get_const_ref)
+ ;
+
+ class_<SharedPtrHolder>("SharedPtrHolder")
+ .constructor<>()
+ .function("get", &SharedPtrHolder::get)
+ .function("set", &SharedPtrHolder::set)
+ ;
+
+ class_<SmallClass>("SmallClass")
+ .constructor<>()
+ .property("member", &SmallClass::member)
+ ;
+
+ class_<BigClass>("BigClass")
+ .constructor<>()
+ .property("member", &BigClass::member)
+ .property("otherMember", &BigClass::otherMember)
+ .property("yetAnotherMember", &BigClass::yetAnotherMember)
+ .function("getMember", &BigClass::getMember)
+ ;
+
+ class_<ParentClass>("ParentClass")
+ .constructor<>()
+ .function("getBigClass", &ParentClass::getBigClass)
+ ;
+
+ class_<TemplateClass<int>>("IntTemplateClass")
+ .constructor<int, int, int>()
+ .function("getMember", &TemplateClass<int>::getMember)
+ ;
+
+ class_<ContainsTemplatedMemberClass>("ContainsTemplatedMemberClass")
+ .constructor<>()
+ .function("getTestTemplate", &ContainsTemplatedMemberClass::getTestTemplate)
+ ;
+
+ // register Derived before Base as a test that it's possible to
+ // register base classes afterwards
+ class_<Derived, base<Base>>("Derived")
+ .smart_ptr<std::shared_ptr<Derived>>()
+ .constructor<>()
+ .function("getClassName", &Derived::getClassName)
+ .function("getMember", &Derived::getMember)
+ .function("setMember", &Derived::setMember)
+ .property("member", &Derived::member)
+ ;
+
+ class_<Base>("Base")
+ .smart_ptr<std::shared_ptr<Base>>()
+ .constructor<>()
+ .function("getClassName", &Base::getClassName)
+ .function("getClassNameFromBase", &Base::getClassNameFromBase)
+ .function("getClassNameNotAvailableInDerivedClasses", &Base::getClassNameNotAvailableInDerivedClasses)
+ .function("getMember", &Base::getMember)
+ .function("setMember", &Base::setMember)
+ .function("getBaseMember", &Base::getBaseMember)
+ .function("setBaseMember", &Base::setBaseMember)
+ .property("member", &Base::member)
+ .property("baseMember", &Base::baseMember)
+ ;
+
+ class_<SecondBase>("SecondBase")
+ .smart_ptr<std::shared_ptr<SecondBase>>()
+ .constructor<>()
+ .function("getClassName", &SecondBase::getClassName)
+ .function("getClassNameFromSecondBase", &SecondBase::getClassNameFromSecondBase)
+ .function("getClassNameNotAvailableInDerivedClasses", &SecondBase::getClassNameNotAvailableInDerivedClasses)
+ .function("getMember", &SecondBase::getMember)
+ .function("setMember", &SecondBase::setMember)
+ .function("getSecondBaseMember", &SecondBase::getSecondBaseMember)
+ .function("setSecondBaseMember", &SecondBase::setSecondBaseMember)
+ .property("member", &SecondBase::member)
+ .property("secondBaseMember", &SecondBase::secondBaseMember)
+ ;
+
+
+ class_<DerivedHolder>("DerivedHolder")
+ .constructor<>()
+ .function("newDerived", &DerivedHolder::newDerived)
+ .function("deleteDerived", &DerivedHolder::deleteDerived)
+ .function("getDerived", &DerivedHolder::getDerived)
+ .function("getDerivedClassName", &DerivedHolder::getDerivedClassName)
+ ;
+
+ class_<SiblingDerived>("SiblingDerived")
+ .smart_ptr<std::shared_ptr<SiblingDerived>>()
+ .constructor<>()
+ .function("getClassName", &SiblingDerived::getClassName)
+ ;
+
+ class_<MultiplyDerived, base<Base>>("MultiplyDerived")
+ .smart_ptr<std::shared_ptr<MultiplyDerived>>()
+ .constructor<>()
+ .function("getClassName", &MultiplyDerived::getClassName)
+ .class_function("getInstanceCount", &MultiplyDerived::getInstanceCount)
+ ;
+
+ class_<DerivedTwice, base<Derived> >("DerivedTwice")
+ .constructor<>()
+ .function("getClassName", &DerivedTwice::getClassName)
+ ;
+
+ class_<DerivedThrice, base<Derived> >("DerivedThrice")
+ .smart_ptr<std::shared_ptr<DerivedThrice>>()
+ .constructor<>()
+ .function("getClassName", &DerivedThrice::getClassName)
+ ;
+
+ class_<PolyBase>("PolyBase")
+ .smart_ptr<std::shared_ptr<PolyBase>>()
+ .constructor<>()
+ .function("virtualGetClassName", &PolyBase::virtualGetClassName)
+ .function("getClassName", &PolyBase::getClassName)
+ ;
+
+ class_<PolySecondBase>("PolySecondBase")
+ .smart_ptr<std::shared_ptr<PolySecondBase>>()
+ .constructor<>()
+ .function("getClassName", &PolySecondBase::getClassName)
+ ;
+
+ class_<PolyDerived, base<PolyBase>>("PolyDerived")
+ .smart_ptr<std::shared_ptr<PolyDerived>>()
+ .constructor<>()
+ .function("virtualGetClassName", &PolyDerived::virtualGetClassName)
+ .function("getClassName", &PolyDerived::getClassName)
+ .class_function("setPtrDerived", &PolyDerived::setPtrDerived)
+ .class_function("releasePtr", &PolyDerived::releasePtr)
+ .class_function("getPtrClassName", &PolyDerived::getPtrClassName)
+ .class_function("getPtr", &PolyDerived::getPtr)
+ ;
+// static void setPtrDerived() {
+// ptr = std::shared_ptr<PolyDerived>(new PolyDerived());
+// }
+//
+// static std::string getPtrClassName() {
+// return ptr->getClassName();
+// }
+//
+// static std::shared_ptr<PolyBase> getPtr() {
+// return ptr;
+// }
+
+ class_<PolySiblingDerived, base<PolyBase>>("PolySiblingDerived")
+ .smart_ptr<std::shared_ptr<PolySiblingDerived>>()
+ .constructor<>()
+ .function("getClassName", &PolySiblingDerived::getClassName)
+ ;
+
+ class_<PolyMultiplyDerived, base<PolyBase>>("PolyMultiplyDerived")
+ .smart_ptr<std::shared_ptr<PolyMultiplyDerived>>()
+ .constructor<>()
+ .function("getClassName", &PolyMultiplyDerived::getClassName)
+ ;
+
+ class_<PolyDerivedThrice, base<PolyDerived>>("PolyDerivedThrice")
+ .smart_ptr<std::shared_ptr<PolyDerivedThrice>>()
+ .constructor<>()
+ .function("getClassName", &PolyDerivedThrice::getClassName)
+ ;
+
+ class_<PolyDiamondBase>("PolyDiamondBase")
+ .smart_ptr<std::shared_ptr<PolyDiamondBase>>()
+ .constructor<>()
+ .function("getClassName", &PolyDiamondBase::getClassName)
+ ;
+
+ class_<PolyDiamondDerived>("PolyDiamondDerived")
+ .smart_ptr<std::shared_ptr<PolyDiamondDerived>>()
+ .constructor<>()
+ .function("getClassName", &PolyDiamondDerived::getClassName)
+ ;
+
+ class_<PolyDiamondSiblingDerived>("PolyDiamondSiblingDerived")
+ .smart_ptr<std::shared_ptr<PolyDiamondSiblingDerived>>()
+ .constructor<>()
+ .function("getClassName", &PolyDiamondSiblingDerived::getClassName)
+ ;
+
+ class_<PolyDiamondMultiplyDerived>("PolyDiamondMultiplyDerived")
+ .smart_ptr<std::shared_ptr<PolyDiamondMultiplyDerived>>()
+ .constructor<>()
+ .function("getClassName", &PolyDiamondMultiplyDerived::getClassName)
+ ;
+
+ function("embind_test_return_small_class_instance", &embind_test_return_small_class_instance);
+ function("embind_test_return_big_class_instance", &embind_test_return_big_class_instance);
+ function("embind_test_accept_small_class_instance", &embind_test_accept_small_class_instance);
+ function("embind_test_accept_big_class_instance", &embind_test_accept_big_class_instance);
+
+ function("embind_test_return_raw_base_ptr", embind_test_return_raw_base_ptr, allow_raw_pointer<ret_val>());
+ function("embind_test_return_raw_derived_ptr_as_base", embind_test_return_raw_derived_ptr_as_base, allow_raw_pointer<ret_val>());
+ function("embind_test_return_raw_sibling_derived_ptr_as_base", embind_test_return_raw_sibling_derived_ptr_as_base, allow_raw_pointer<ret_val>());
+ function("embind_test_return_raw_polymorphic_derived_ptr_as_base", embind_test_return_raw_polymorphic_derived_ptr_as_base, allow_raw_pointer<ret_val>());
+ function("embind_test_return_raw_polymorphic_sibling_derived_ptr_as_base", embind_test_return_raw_polymorphic_sibling_derived_ptr_as_base, allow_raw_pointer<ret_val>());
+ function("embind_test_return_raw_polymorphic_multiply_derived_ptr_as_base", embind_test_return_raw_polymorphic_multiply_derived_ptr_as_base, allow_raw_pointer<ret_val>());
+ function("embind_test_return_raw_polymorphic_multiply_derived_ptr_as_second_base", embind_test_return_raw_polymorphic_multiply_derived_ptr_as_second_base, allow_raw_pointer<ret_val>());
+ function("embind_test_return_raw_polymorphic_derived_four_times_not_bound_as_base", embind_test_return_raw_polymorphic_derived_four_times_not_bound_as_base, allow_raw_pointer<ret_val>());
+ function("embind_test_return_smart_derived_ptr", embind_test_return_smart_derived_ptr);
+ function("embind_test_return_smart_sibling_derived_ptr", embind_test_return_smart_sibling_derived_ptr);
+ function("embind_test_return_smart_multiply_derived_ptr", embind_test_return_smart_multiply_derived_ptr);
+ function("embind_test_return_smart_derived_thrice_ptr", embind_test_return_smart_derived_thrice_ptr);
+ function("embind_test_return_smart_base_ptr", embind_test_return_smart_base_ptr);
+ function("embind_test_return_smart_polymorphic_base_ptr", embind_test_return_smart_polymorphic_base_ptr);
+ function("embind_test_return_smart_polymorphic_derived_ptr", embind_test_return_smart_polymorphic_derived_ptr);
+ function("embind_test_return_smart_polymorphic_sibling_derived_ptr", embind_test_return_smart_polymorphic_sibling_derived_ptr);
+ function("embind_test_return_smart_polymorphic_multiply_derived_ptr", embind_test_return_smart_polymorphic_multiply_derived_ptr);
+ function("embind_test_return_poly_derived_twice_without_smart_pointer_as_poly_base", embind_test_return_poly_derived_twice_without_smart_pointer_as_poly_base);
+ function("embind_test_return_smart_poly_derived_thrice_ptr", embind_test_return_smart_poly_derived_thrice_ptr);
+ function("embind_test_return_smart_derived_ptr_as_base", embind_test_return_smart_derived_ptr_as_base);
+ function("embind_test_return_smart_derived_ptr_as_val", embind_test_return_smart_derived_ptr_as_val);
+ function("embind_test_return_smart_polymorphic_derived_ptr_as_base", embind_test_return_smart_polymorphic_derived_ptr_as_base);
+ function("embind_test_return_smart_polymorphic_sibling_derived_ptr_as_base", embind_test_return_smart_polymorphic_sibling_derived_ptr_as_base);
+ function("embind_test_get_class_name_via_base_ptr", embind_test_get_class_name_via_base_ptr, allow_raw_pointer<arg<0>>());
+ function("embind_test_get_class_name_via_second_base_ptr", embind_test_get_class_name_via_second_base_ptr, allow_raw_pointer<arg<0>>());
+ function("embind_test_get_class_name_via_polymorphic_base_ptr", embind_test_get_class_name_via_polymorphic_base_ptr, allow_raw_pointer<arg<0>>());
+ function("embind_test_get_class_name_via_polymorphic_second_base_ptr", embind_test_get_class_name_via_polymorphic_second_base_ptr, allow_raw_pointer<arg<0>>());
+ // todo: allow_raw_pointer should fail earlier if argument is not a pointer
+ function("embind_test_get_class_name_via_smart_base_ptr", embind_test_get_class_name_via_smart_base_ptr);
+ function("embind_test_get_class_name_via_reference_to_smart_base_ptr", embind_test_get_class_name_via_reference_to_smart_base_ptr);
+ function("embind_test_get_class_name_via_smart_second_base_ptr", embind_test_get_class_name_via_smart_second_base_ptr);
+ function("embind_test_get_class_name_via_smart_polymorphic_base_ptr", embind_test_get_class_name_via_smart_polymorphic_base_ptr);
+ function("embind_test_get_virtual_class_name_via_smart_polymorphic_base_ptr", embind_test_get_virtual_class_name_via_smart_polymorphic_base_ptr);
+ function("embind_test_get_class_name_via_smart_polymorphic_second_base_ptr", embind_test_get_class_name_via_smart_polymorphic_second_base_ptr);
+ function("embind_modify_smart_pointer_passed_by_reference", embind_modify_smart_pointer_passed_by_reference);
+ function("embind_attempt_to_modify_smart_pointer_when_passed_by_value", embind_attempt_to_modify_smart_pointer_when_passed_by_value);
+ function("embind_save_smart_base_pointer", embind_save_smart_base_pointer);
+
+ class_<Base1>("Base1")
+ .constructor()
+ .function("getField", &Base1::getField)
+ ;
+
+ class_<Base2>("Base2")
+ .function("getField", &Base2::getField)
+ .property("field", &Base2::field2)
+ ;
+
+ class_<HasTwoBases, base<Base2>>("HasTwoBases")
+ .constructor()
;
class_<CustomStruct>("CustomStruct")
.constructor<>()
- .field("field", &CustomStruct::field)
+ .property("field", &CustomStruct::field)
+ .function("getField", &CustomStruct::getField)
;
enum_<Enum>("Enum")
@@ -319,22 +1845,270 @@ EMSCRIPTEN_BINDINGS(([]() { ;
function("emval_test_take_and_return_EnumClass", &emval_test_take_and_return_EnumClass);
- class InterfaceWrapper : public wrapper<Interface> {
- int method() {
- return call<int>("method");
- }
- TupleInStruct method2(const TupleInStruct& arg1, float arg2) {
- return call<TupleInStruct>("method2", arg1, arg2);
- }
- void method3() {
- return call<void>("method3");
- }
- };
- interface<InterfaceWrapper>("Interface")
+ function("emval_test_call_function", &emval_test_call_function);
+
+ function("emval_test_return_unique_ptr", &emval_test_return_unique_ptr);
+
+ function("emval_test_return_shared_ptr", &emval_test_return_shared_ptr);
+ function("emval_test_return_empty_shared_ptr", &emval_test_return_empty_shared_ptr);
+ function("emval_test_is_shared_ptr_null", &emval_test_is_shared_ptr_null);
+
+ function("emval_test_return_vector", &emval_test_return_vector);
+ function("emval_test_return_vector_of_vectors", &emval_test_return_vector_of_vectors);
+
+ register_vector<std::shared_ptr<StringHolder>>("SharedPtrVector");
+ function("emval_test_return_shared_ptr_vector", &emval_test_return_shared_ptr_vector);
+
+ function("get_module_property", &get_module_property);
+
+ register_vector<StringHolder>("StringHolderVector");
+ class_<VectorHolder>("VectorHolder")
+ .constructor<>()
+ .function("get", &VectorHolder::get)
+ .function("set", &VectorHolder::set)
;
- function("emval_test_call_method", &emval_test_call_method);
- function("emval_test_call_method2", &emval_test_call_method2);
- function("emval_test_call_method3", &emval_test_call_method3);
- function("emval_test_call_function", &emval_test_call_function);
-}));
+ function("test_string_with_vec", &test_string_with_vec);
+
+ register_map<std::string, int>("StringIntMap");
+ function("embind_test_get_string_int_map", embind_test_get_string_int_map);
+
+ function("embind_test_new_Object", &embind_test_new_Object);
+ function("embind_test_new_factory", &embind_test_new_factory);
+
+ class_<AbstractClass>("AbstractClass")
+ .smart_ptr<std::shared_ptr<AbstractClass>>()
+ .allow_subclass<AbstractClassWrapper>()
+ .function("abstractMethod", &AbstractClass::abstractMethod)
+ .function("optionalMethod", &AbstractClass::optionalMethod)
+ ;
+
+ function("getAbstractClass", &getAbstractClass);
+ function("callAbstractMethod", &callAbstractMethod);
+ function("callOptionalMethod", &callOptionalMethod);
+
+ class_<HasExternalConstructor>("HasExternalConstructor")
+ .constructor(&createHasExternalConstructor)
+ .function("getString", &HasExternalConstructor::getString)
+ ;
+
+ auto HeldBySmartPtr_class = class_<HeldBySmartPtr>("HeldBySmartPtr");
+ HeldBySmartPtr_class
+ .smart_ptr<CustomSmartPtr<HeldBySmartPtr>>()
+ .smart_ptr_constructor(&std::make_shared<HeldBySmartPtr, int, std::string>)
+ .class_function("newCustomPtr", HeldBySmartPtr::newCustomPtr)
+ .function("returnThis", &takesHeldBySmartPtrSharedPtr)
+ .property("i", &HeldBySmartPtr::i)
+ .property("s", &HeldBySmartPtr::s)
+ ;
+ function("takesHeldBySmartPtr", &takesHeldBySmartPtr);
+ function("takesHeldBySmartPtrSharedPtr", &takesHeldBySmartPtrSharedPtr);
+
+ class_<HeldByCustomSmartPtr>("HeldByCustomSmartPtr")
+ .smart_ptr<std::shared_ptr<HeldByCustomSmartPtr>>()
+ .smart_ptr_constructor(&HeldByCustomSmartPtr::create)
+ .class_function("createSharedPtr", &HeldByCustomSmartPtr::createSharedPtr)
+ .property("i", &HeldByCustomSmartPtr::i)
+ .property("s", &HeldByCustomSmartPtr::s)
+ ;
+
+ function("passThroughRawPtr", &passThroughRawPtr, allow_raw_pointers());
+ function("passThroughCustomSmartPtr", &passThroughCustomSmartPtr);
+
+ function("char_to_string", &char_to_string);
+ function("signed_char_to_string", &signed_char_to_string);
+ function("unsigned_char_to_string", &unsigned_char_to_string);
+ function("short_to_string", &short_to_string);
+ function("unsigned_short_to_string", &unsigned_short_to_string);
+ function("int_to_string", &int_to_string);
+ function("unsigned_int_to_string", &unsigned_int_to_string);
+ function("long_to_string", &long_to_string);
+ function("unsigned_long_to_string", &unsigned_long_to_string);
+
+ function("overloaded_function", select_overload<int(int)>(&overloaded_function));
+ function("overloaded_function", select_overload<int(int, int)>(&overloaded_function));
+
+ class_<MultipleCtors>("MultipleCtors")
+ .constructor<int>()
+ .constructor<int, int>()
+ .constructor<int, int, int>()
+ .function("WhichCtorCalled", &MultipleCtors::WhichCtorCalled);
+
+ class_<MultipleOverloads>("MultipleOverloads")
+ .constructor<>()
+ .function("Func", select_overload<int(int)>(&MultipleOverloads::Func))
+ .function("Func", select_overload<int(int, int)>(&MultipleOverloads::Func))
+ .function("WhichFuncCalled", &MultipleOverloads::WhichFuncCalled)
+ .class_function("StaticFunc", select_overload<int(int)>(&MultipleOverloads::StaticFunc))
+ .class_function("StaticFunc", select_overload<int(int,int)>(&MultipleOverloads::StaticFunc))
+ .class_function("WhichStaticFuncCalled", &MultipleOverloads::WhichStaticFuncCalled)
+ ;
+
+ class_<MultipleOverloadsDerived, base<MultipleOverloads> >("MultipleOverloadsDerived")
+ .constructor<>()
+ .function("Func", select_overload<int(int,int,int)>(&MultipleOverloadsDerived::Func))
+ .function("Func", select_overload<int(int,int,int,int)>(&MultipleOverloadsDerived::Func))
+ .class_function("StaticFunc", select_overload<int(int,int,int)>(&MultipleOverloadsDerived::StaticFunc))
+ .class_function("StaticFunc", select_overload<int(int,int,int,int)>(&MultipleOverloadsDerived::StaticFunc))
+ ;
+}
+
+// tests for out-of-order registration
+
+class SecondElement {
+};
+
+class FirstElement {
+};
+
+struct OrderedTuple {
+ FirstElement first;
+ SecondElement second;
+};
+
+struct OrderedStruct {
+ FirstElement first;
+ SecondElement second;
+};
+
+OrderedTuple getOrderedTuple() {
+ return OrderedTuple();
+}
+
+OrderedStruct getOrderedStruct() {
+ return OrderedStruct();
+}
+
+EMSCRIPTEN_BINDINGS(order) {
+ value_tuple<OrderedTuple>("OrderedTuple")
+ .element(&OrderedTuple::first)
+ .element(&OrderedTuple::second)
+ ;
+
+ value_struct<OrderedStruct>("OrderedStruct")
+ .field("first", &OrderedStruct::first)
+ .field("second", &OrderedStruct::second)
+ ;
+
+ class_<SecondElement>("SecondElement")
+ ;
+
+ class_<FirstElement>("FirstElement")
+ ;
+
+ function("getOrderedTuple", &getOrderedTuple);
+ function("getOrderedStruct", &getOrderedStruct);
+}
+
+// tests for unbound types
+
+template<typename T>
+T passThrough(T t) {
+ return t;
+}
+
+struct UnboundClass {
+};
+
+struct HasUnboundBase : public UnboundClass {
+ static void noop() {
+ }
+};
+
+HasUnboundBase getHasUnboundBase(HasUnboundBase f) {
+ return f;
+}
+
+struct HasConstructorUsingUnboundArgument {
+ HasConstructorUsingUnboundArgument(UnboundClass) {
+ }
+};
+
+struct SecondUnboundClass {
+};
+
+struct HasConstructorUsingUnboundArgumentAndUnboundBase : public SecondUnboundClass {
+ HasConstructorUsingUnboundArgumentAndUnboundBase(UnboundClass) {
+ }
+};
+
+struct BoundClass {
+ UnboundClass method(UnboundClass t) {
+ return t;
+ }
+
+ static UnboundClass classfunction(UnboundClass t) {
+ return t;
+ }
+
+ UnboundClass property;
+};
+
+EMSCRIPTEN_BINDINGS(incomplete) {
+ function("getUnboundClass", &passThrough<UnboundClass>);
+
+ class_<HasUnboundBase, base<UnboundClass>>("HasUnboundBase")
+ .class_function("noop", &HasUnboundBase::noop)
+ ;
+ function("getHasUnboundBase", &passThrough<HasUnboundBase>);
+
+ class_<HasConstructorUsingUnboundArgument>("HasConstructorUsingUnboundArgument")
+ .constructor<UnboundClass>()
+ ;
+
+ class_<HasConstructorUsingUnboundArgumentAndUnboundBase, base<SecondUnboundClass>>("HasConstructorUsingUnboundArgumentAndUnboundBase")
+ .constructor<UnboundClass>()
+ ;
+
+ class_<BoundClass>("BoundClass")
+ .constructor<>()
+ .function("method", &BoundClass::method)
+ .class_function("classfunction", &BoundClass::classfunction)
+ .property("property", &BoundClass::property)
+ ;
+}
+
+class Noncopyable {
+ Noncopyable(const Noncopyable&) = delete;
+ Noncopyable& operator=(const Noncopyable&) = delete;
+
+public:
+ Noncopyable() {}
+ Noncopyable(Noncopyable&& other) {
+ other.valid = false;
+ }
+
+ std::string method() const {
+ return "foo";
+ }
+
+ bool valid = true;
+};
+
+Noncopyable getNoncopyable() {
+ return Noncopyable();
+}
+
+EMSCRIPTEN_BINDINGS(noncopyable) {
+ class_<Noncopyable>("Noncopyable")
+ .constructor<>()
+ .function("method", &Noncopyable::method)
+ ;
+
+ function("getNoncopyable", &getNoncopyable);
+}
+
+struct HasReadOnlyProperty {
+ HasReadOnlyProperty(int i)
+ : i(i)
+ {}
+
+ const int i;
+};
+
+EMSCRIPTEN_BINDINGS(read_only_properties) {
+ class_<HasReadOnlyProperty>("HasReadOnlyProperty")
+ .constructor<int>()
+ .property("i", &HasReadOnlyProperty::i)
+ ;
+}
diff --git a/tests/embind/embind_test.js b/tests/embind/embind_test.js deleted file mode 100644 index 8c61553b..00000000 --- a/tests/embind/embind_test.js +++ /dev/null @@ -1,393 +0,0 @@ -//=== testing glue - -function module(ignore, func) { - func({ Emscripten: Module }); -} - -function fixture(name, info) { - Module.print('fixture: ' + name); - for (var test in info) { - var f = info[test]; - if (typeof f != 'function') continue; - Module.print('--test: ' + test); - // TODO: Base fixture! - f(); - } -} - -assert.true = assert; - -assert.equal = function(x, y) { - assert(x == y); -} - -assert.notEqual = function(x, y) { - assert(x != y); -} - -assert.throws = function(exc, func) { - var ret; - try { - func(); - } catch(e) { - ret = e; - } - assert(ret); // TODO: check exc vs e - return ret; -} - -assert.instanceof = function(inst, clazz) { - assert(inst instanceof clazz); -} - -assert.deepEqual = function(x, y) { - assert(JSON.stringify(x) == JSON.stringify(y)); -} - -//=== - -module({ - Emscripten: '../build/Emscripten.js' -}, function(imports) { - var cm = imports.Emscripten; - - var checkForLeaks = { - setUp: function() { - this.originalBlockCount = cm.mallinfo().uordblks; - }, - tearDown: function() { - assert.equal(this.originalBlockCount, cm.mallinfo().uordblks); - }, - }; - - fixture("embind", { - baseFixture: checkForLeaks, - - "test value creation": function() { - assert.equal(15, cm.emval_test_new_integer()); - assert.equal("Hello everyone", cm.emval_test_new_string()); - - var object = cm.emval_test_new_object(); - assert.equal('bar', object.foo); - assert.equal(1, object.baz); - }, - - "test passthrough": function() { - var a = {foo: 'bar'}; - var b = cm.emval_test_passthrough(a); - a.bar = 'baz'; - assert.equal('baz', b.bar); - - assert.equal(0, cm.count_emval_handles()); - }, - - "test void return converts to undefined": function() { - assert.equal(undefined, cm.emval_test_return_void()); - }, - - "test booleans can be marshalled": function() { - assert.equal(false, cm.emval_test_not(true)); - assert.equal(true, cm.emval_test_not(false)); - }, - - "test convert double to unsigned": function() { - var rv = cm.emval_test_as_unsigned(1.5); - assert.equal('number', typeof rv); - assert.equal(1, rv); - assert.equal(0, cm.count_emval_handles()); - }, - - "test get length of array": function() { - assert.equal(10, cm.emval_test_get_length([0, 1, 2, 3, 4, 5, 'a', 'b', 'c', 'd'])); - assert.equal(0, cm.count_emval_handles()); - }, - - "test add a bunch of things": function() { - assert.equal(66.0, cm.emval_test_add(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)); - assert.equal(0, cm.count_emval_handles()); - }, - - "test sum array": function() { - assert.equal(66, cm.emval_test_sum([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])); - assert.equal(0, cm.count_emval_handles()); - }, - - "test strings": function() { - assert.equal("foobar", "foo" + "bar"); - assert.equal("foobar", cm.emval_test_take_and_return_std_string("foobar")); - - assert.equal("foobar", cm.emval_test_take_and_return_std_string_const_ref("foobar")); - }, - - "test no memory leak when passing strings in by const reference": function() { - var original = cm.mallinfo().uordblks; - cm.emval_test_take_and_return_std_string_const_ref("foobar"); - assert.equal(original, cm.mallinfo().uordblks); - }, - }); - - fixture("classes", { - baseFixture: checkForLeaks, - - "test class instance": function() { - var a = {foo: 'bar'}; - var c = new cm.ValHolder(a); - assert.equal('bar', c.getVal().foo); - - c.setVal('1234'); - assert.equal('1234', c.getVal()); - - assert.equal(1239, c.returnIntPlusFive(1234)); - - c.delete(); - assert.equal(0, cm.count_emval_handles()); - }, - - "test class methods": function() { - assert.equal(10, cm.ValHolder.some_class_method(10)); - }, - - "test can't call methods on deleted class instances": function() { - var c = new cm.ValHolder(undefined); - c.delete(); - assert.throws(cm.BindingError, function() { - c.getVal(); - }); - assert.throws(cm.BindingError, function() { - c.delete(); - }); - }, - - "test isinstance": function() { - var c = new cm.ValHolder(undefined); - assert.instanceof(c, cm.ValHolder); - c.delete(); - }, - - "test can return class instances by value": function() { - var c = cm.emval_test_return_ValHolder(); - assert.deepEqual({}, c.getVal()); - c.delete(); - }, - - "test can pass class instances to functions by reference": function() { - var a = {a:1}; - var c = new cm.ValHolder(a); - cm.emval_test_set_ValHolder_to_empty_object(c); - assert.deepEqual({}, c.getVal()); - c.delete(); - }, - - "test can access struct fields": function() { - var c = new cm.CustomStruct(); - assert.equal(10, c.field); - c.delete(); - }, - - "test can set struct fields": function() { - var c = new cm.CustomStruct(); - c.field = 15; - assert.equal(15, c.field); - c.delete(); - }, - - "test assignment returns value": function() { - var c = new cm.CustomStruct(); - assert.equal(15, c.field = 15); - c.delete(); - }, - - "test assigning string to integer raises TypeError": function() { - var c = new cm.CustomStruct(); - - var e = assert.throws(TypeError, function() { - c.field = "hi"; - }); - assert.equal('Cannot convert "hi" to int', e.message); - - var e = assert.throws(TypeError, function() { - c.field = {foo:'bar'}; - }); - assert.equal('Cannot convert "[object Object]" to int', e.message); - - c.delete(); - }, - - "test can return tuples by value": function() { - var c = cm.emval_test_return_TupleVector(); - assert.deepEqual([1, 2, 3], c); - }, - - "test tuples can contain tuples": function() { - var c = cm.emval_test_return_TupleVectorTuple(); - assert.deepEqual([[1, 2, 3]], c); - }, - - "test can pass tuples by value": function() { - var c = cm.emval_test_take_and_return_TupleVector([4, 5, 6]); - assert.deepEqual([4, 5, 6], c); - }, - - "test can return structs by value": function() { - var c = cm.emval_test_return_StructVector(); - assert.deepEqual({x: 1, y: 2, z: 3}, c); - }, - - "test can pass structs by value": function() { - var c = cm.emval_test_take_and_return_StructVector({x: 4, y: 5, z: 6}); - assert.deepEqual({x: 4, y: 5, z: 6}, c); - }, - - "test can pass and return tuples in structs": function() { - var d = cm.emval_test_take_and_return_TupleInStruct({field: [1, 2, 3]}); - assert.deepEqual({field: [1, 2, 3]}, d); - }, - - "test can clone handles": function() { - assert.equal(0, cm.count_emval_handles()); - - var a = new cm.ValHolder({}); - var b = a.clone(); - a.delete(); - - assert.equal(1, cm.count_emval_handles()); - - assert.throws(cm.BindingError, function() { - a.delete(); - }); - b.delete(); - - assert.equal(0, cm.count_emval_handles()); - }, - - "test can't clone if already deleted": function() { - var a = new cm.ValHolder({}); - a.delete(); - assert.throws(cm.BindingError, function() { - a.clone(); - }); - }, - - "test moving handles is a clone+delete": function() { - var a = new cm.ValHolder({}); - var b = a.move(); - assert.throws(cm.BindingError, function() { - a.delete(); - }); - assert.equal(1, cm.count_emval_handles()); - b.delete(); - assert.equal(0, cm.count_emval_handles()); - }, - - "test StringHolder": function() { - var a = new cm.StringHolder("foobar"); - assert.equal("foobar", a.get()); - - a.set("barfoo"); - assert.equal("barfoo", a.get()); - a.delete(); - }, - }); - - fixture("embind enumerations", { - baseFixture: checkForLeaks, - - "test can compare enumeration values": function() { - assert.equal(cm.Enum.ONE, cm.Enum.ONE); - assert.notEqual(cm.Enum.ONE, cm.Enum.TWO); - }, - - "test repr includes enum value": function() { - return; // XXX IMVU? - assert.equal('<#Enum_ONE {}>', IMVU.repr(cm.Enum.ONE)); - assert.equal('<#Enum_TWO {}>', IMVU.repr(cm.Enum.TWO)); - }, - - "test instanceof": function() { - assert.instanceof(cm.Enum.ONE, cm.Enum); - }, - - "test can pass and return enumeration values to functions": function() { - assert.equal(cm.Enum.TWO, cm.emval_test_take_and_return_Enum(cm.Enum.TWO)); - }, - }); - - fixture("C++11 enum class", { - baseFixture: checkForLeaks, - - "test can compare enumeration values": function() { - assert.equal(cm.EnumClass.ONE, cm.EnumClass.ONE); - assert.notEqual(cm.EnumClass.ONE, cm.EnumClass.TWO); - }, - - "test repr includes enum value": function() { - return; // XXX IMVU? - assert.equal('<#EnumClass_ONE {}>', IMVU.repr(cm.EnumClass.ONE)); - assert.equal('<#EnumClass_TWO {}>', IMVU.repr(cm.EnumClass.TWO)); - }, - - "test instanceof": function() { - assert.instanceof(cm.EnumClass.ONE, cm.EnumClass); - }, - - "test can pass and return enumeration values to functions": function() { - assert.equal(cm.EnumClass.TWO, cm.emval_test_take_and_return_EnumClass(cm.EnumClass.TWO)); - }, - }); - - fixture("emval call tests", { - "test can call functions from C++": function() { - var called = false; - cm.emval_test_call_function(function(i, f, tv, sv) { - called = true; - assert.equal(10, i); - assert.equal(1.5, f); - assert.deepEqual([1.25, 2.5, 3.75], tv); - assert.deepEqual({x: 1.25, y: 2.5, z: 3.75}, sv); - }, 10, 1.5, [1.25, 2.5, 3.75], {x: 1.25, y: 2.5, z: 3.75}); - assert.true(called); - }, - }); - - fixture("interfaces", { - baseFixture: checkForLeaks, - - "test can wrap JS object in native interface": function() { - var foo = { - calls: [], - method: function() { - this.calls.push('called'); - return 10; - } - }; - - assert.equal(10, cm.emval_test_call_method(foo)); - assert.deepEqual(['called'], foo.calls); - }, - - "test can pass arguments and return complicated values": function() { - var calls = []; - var foo = { - method2: function(arg1, arg2) { - calls.push([arg1, arg2]); - return arg1; - } - }; - - var result = cm.emval_test_call_method2(foo, {field: [1, 2, 3]}, 7); - assert.deepEqual({field: [1, 2, 3]}, result); - assert.deepEqual([[{field: [1, 2, 3]}, 7]], calls); - }, - - "test can call interface methods that return nothing": function() { - var calls = []; - var foo = { - method3: function() { - calls.push('called'); - } - }; - cm.emval_test_call_method3(foo); - assert.deepEqual(['called'], calls); - }, - }); -}); diff --git a/tests/embind/imvu_test_adapter.js b/tests/embind/imvu_test_adapter.js new file mode 100755 index 00000000..421e86c8 --- /dev/null +++ b/tests/embind/imvu_test_adapter.js @@ -0,0 +1,616 @@ +/* The embind test suite (embind.test.js) is configured to be runnable in two different testing engines:
+ - The Emscripten python test runner (open-source in emscripten repository) and
+ - The IMVU test runner (closed-source in IMVU repository)
+
+ Embind (and its tests) were originally developed in IMVU repository, which is the reason for two testing architectures.
+ This adapter file is used when the embind tests are run as part of the Emscripten test runner, to provide the necessary glue code to adapt the tests to Emscripten runner.
+
+ To run the Embind tests using the Emscripten test runner, invoke 'python tests/runner.py other.test_embind' in the Emscripten root directory.
+*/
+
+/* global Module, console, global, process */
+
+//=== testing glue
+
+function module(ignore, func) {
+ func({ Emscripten: Module });
+}
+
+/*global IMVU:true, TEST_MAX_OUTPUT_SIZE*/
+//(function() {
+// "use strict";
+
+ // { beforeTest: function,
+ // afterTest: function }
+ var superFixtures = [];
+
+ function registerSuperFixture(superFixture) {
+ superFixtures.push(superFixture);
+ }
+
+ // { fixture: Fixture instance,
+ // name: string,
+ // body: function() }
+ var allTests = [];
+
+ function test(name, fn) {
+ if (arguments.length !== 2) {
+ throw new TypeError("test requires 2 arguments");
+ }
+
+ if (undefined !== activeFixture && activeFixture.abstract) {
+ activeFixture.abstractTests.push({
+ name: name,
+ body: fn });
+ } else {
+ var fixtureName = (undefined !== activeFixture)? activeFixture.name + ': ' : '';
+ allTests.push({
+ name: fixtureName + name,
+ body: fn,
+ fixture: activeFixture });
+ }
+ }
+
+ function runTest(test, continuation) {
+ try {
+ var afterTests = [];
+
+ for (var i = 0; i < superFixtures.length; ++i) {
+ var superFixture = superFixtures[i];
+
+ var superScope = {};
+ superFixture.beforeTest.call(superScope);
+ afterTests.push(superFixture.afterTest.bind(superScope));
+ }
+
+ var testScope = test.fixture ?
+ Object.create(test.fixture.scope) :
+ {};
+
+ var runSetUp = function(fixtureObject) {
+ if (undefined === fixtureObject) {
+ return;
+ }
+ runSetUp(fixtureObject.parent);
+ fixtureObject.setUp.call(testScope);
+ afterTests.push(fixtureObject.tearDown.bind(testScope));
+ };
+ runSetUp(test.fixture);
+
+ test.body.call(testScope);
+ while (afterTests.length) {
+ afterTests.pop()();
+ }
+ return false;
+ } catch (e) {
+ console.error('error:', e);
+ return {stack: e.stack, e: e};
+ }
+ }
+
+ function run_all(reporter) {
+ for (var i = 0; i < allTests.length; ++i) {
+ var test = allTests[i];
+ reporter({
+ type: 'test-start',
+ name: test.name
+ });
+
+ var failed = runTest(test);
+ if (failed) {
+ reporter({
+ type: 'test-complete',
+ name: test.name,
+ verdict: 'FAIL',
+ stack: failed.stack,
+ e: failed.e
+ });
+ return false;
+ } else {
+ reporter({
+ type: 'test-complete',
+ name: test.name,
+ verdict: 'PASS'
+ });
+ }
+ }
+
+ reporter({
+ type: 'all-tests-complete'
+ });
+
+ allTests = [];
+ return true;
+ }
+
+ var activeFixture;
+
+ function Fixture(parent, name, definition, abstract_) {
+ if (!(definition instanceof Function)) {
+ throw new TypeError("fixture's 2nd argument must be a function");
+ }
+
+ this.name = name;
+ this.parent = parent;
+ this.abstract = abstract_;
+ if (this.abstract) {
+ // { name: string,
+ // body: function }
+ this.abstractTests = [];
+ }
+
+ if (this.parent !== undefined) {
+ this.parent.addAbstractTests(this);
+ }
+
+ this.scope = (this.parent === undefined ? {} : Object.create(this.parent.scope));
+ this.scope.setUp = function(setUp) {
+ this.setUp = setUp;
+ }.bind(this);
+ this.scope.tearDown = function(tearDown) {
+ this.tearDown = tearDown;
+ }.bind(this);
+
+ if (undefined !== activeFixture) {
+ throw new TypeError("Cannot define a fixture within another fixture");
+ }
+
+ activeFixture = this;
+ try {
+ definition.call(this.scope);
+ }
+ finally {
+ activeFixture = undefined;
+ }
+ }
+ Fixture.prototype.setUp = function defaultSetUp() {
+ };
+ Fixture.prototype.tearDown = function defaultTearDown() {
+ };
+ Fixture.prototype.addAbstractTests = function(concreteFixture) {
+ if (this.abstract) {
+ for (var i = 0; i < this.abstractTests.length; ++i) {
+ var test = this.abstractTests[i];
+ allTests.push({
+ name: concreteFixture.name + ': ' + test.name,
+ body: test.body,
+ fixture: concreteFixture});
+ }
+ }
+ if (this.parent) {
+ this.parent.addAbstractTests(concreteFixture);
+ }
+ };
+
+ Fixture.prototype.extend = function(fixtureName, definition) {
+ return new Fixture(this, fixtureName, definition, false);
+ };
+
+ function fixture(fixtureName, definition) {
+ return new Fixture(undefined, fixtureName, definition, false);
+ }
+ fixture.abstract = function(fixtureName, definition) {
+ return new Fixture(undefined, fixtureName, definition, true);
+ };
+
+ var AssertionError = Error;
+
+ function fail(exception, info) {
+ exception.info = info;
+ throw exception;
+ }
+
+ var formatTestValue = function(v) {
+ return v.toString();
+ /*
+ var s = IMVU.repr(v, TEST_MAX_OUTPUT_SIZE + 1);
+ if (s.length <= TEST_MAX_OUTPUT_SIZE) {
+ return s;
+ }
+ return s.substring(0, TEST_MAX_OUTPUT_SIZE) + '...';
+ */
+ };
+
+// var assert = {
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // GENERAL STATUS
+
+ assert.fail = function(info) {
+ info = info || "assert.fail()";
+ fail(new AssertionError(info));
+ },
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // BOOLEAN TESTS
+
+ assert['true'] = function(value) {
+ if (!value) {
+ fail(new AssertionError("expected truthy, actual " + formatTestValue(value)),
+ {Value: value});
+ }
+ },
+
+ assert['false'] = function(value) {
+ if (value) {
+ fail(new AssertionError("expected falsy, actual " + formatTestValue(value)),
+ {Value: value});
+ }
+ },
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // SCALAR COMPARISON
+
+ assert.equal = function(expected, actual) {
+ if (expected !== actual) {
+ fail(new AssertionError('expected: ' + formatTestValue(expected) + ', actual: ' + formatTestValue(actual)),
+ {Expected: expected, Actual: actual});
+ }
+ },
+
+ assert.notEqual = function(expected, actual) {
+ if (expected === actual) {
+ fail(new AssertionError('actual was equal to: ' + formatTestValue(expected)));
+ }
+ },
+
+ assert.greater = function(lhs, rhs) {
+ if (lhs <= rhs) {
+ fail(new AssertionError(formatTestValue(lhs) + ' not greater than ' + formatTestValue(rhs)));
+ }
+ },
+
+ assert.less = function(lhs, rhs) {
+ if (lhs >= rhs) {
+ fail(new AssertionError(formatTestValue(lhs) + ' not less than ' + formatTestValue(rhs)));
+ }
+ },
+
+ assert.greaterOrEqual = function(lhs, rhs) {
+ if (lhs < rhs) {
+ fail(new AssertionError(formatTestValue(lhs) + ' not greater than or equal to ' + formatTestValue(rhs)));
+ }
+ },
+
+ assert.lessOrEqual = function(lhs, rhs) {
+ if (lhs > rhs) {
+ fail(new AssertionError(formatTestValue(lhs) + ' not less than or equal to ' + formatTestValue(rhs)));
+ }
+ },
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // DEEP COMPARISON
+
+ assert.deepEqual = function(expected, actual) {
+ if (!_.isEqual(expected, actual)) {
+ fail(new AssertionError('expected: ' + formatTestValue(expected) + ', actual: ' + formatTestValue(actual)),
+ {Expected: expected, Actual: actual});
+ }
+ },
+
+ assert.notDeepEqual = function(expected, actual) {
+ if (_.isEqual(expected, actual)) {
+ fail(new AssertionError('expected: ' + formatTestValue(expected) + ' and actual: ' + formatTestValue(actual) + ' were equal'));
+ }
+ },
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // FLOATING POINT
+
+ assert.nearEqual = function( expected, actual, tolerance ) {
+ if( tolerance === undefined ) {
+ tolerance = 0.0;
+ }
+ if( expected instanceof Array && actual instanceof Array ) {
+ assert.equal(expected.length, actual.length);
+ for( var i=0; i<expected.length; ++i ) {
+ assert.nearEqual(expected[i], actual[i], tolerance);
+ }
+ return;
+ }
+ if( Math.abs(expected - actual) > tolerance ) {
+ fail( new AssertionError('expected: ' + formatTestValue(expected) + ', actual: ' + formatTestValue(actual) +
+ ', tolerance: ' + formatTestValue(tolerance) + ', diff: ' + formatTestValue(actual-expected) ),
+ { Expected:expected, Actual:actual, Tolerance:tolerance } );
+ }
+ },
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // STRING
+
+ assert.inString = function(expected, string){
+ if (-1 === string.indexOf(expected)){
+ fail(new AssertionError('expected: ' + formatTestValue(expected) + ' not in string: ' + formatTestValue(string)),
+ {Expected: expected, 'String': string});
+ }
+ },
+
+ assert.notInString = function(expected, string){
+ if (-1 !== string.indexOf(expected)){
+ fail(new AssertionError('unexpected: ' + formatTestValue(expected) + ' in string: ' + formatTestValue(string)),
+ {Expected: expected, 'String': string});
+ }
+ },
+
+ assert.matches = function(re, string) {
+ if (!re.test(string)) {
+ fail(new AssertionError('regexp ' + re + ' does not match: ' + string));
+ }
+ },
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // ARRAY
+
+ assert.inArray = function(expected, array) {
+ var found = false;
+ _.each(array, function(element){
+ if (_.isEqual(expected, element)){
+ found = true;
+ }
+ });
+ if (!found){
+ fail(new AssertionError('expected: ' + formatTestValue(expected) + ' not found in array: ' + formatTestValue(array)),
+ {Expected: expected, 'Array': array});
+ }
+ },
+
+ assert.notInArray = function(expected, array) {
+ var found = false;
+ _.each(array, function(element){
+ if (_.isEqual(expected, element)){
+ found = true;
+ }
+ });
+ if (found){
+ fail(new AssertionError('unexpected: ' + formatTestValue(expected) + ' found in array: ' + formatTestValue(array)),
+ {Expected: expected, 'Array': array});
+ }
+ },
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // OBJECTS
+
+ assert.hasKey = function (key, object) {
+ if (!(key in object)) {
+ fail(new AssertionError('Key ' + formatTestValue(key) + ' is not in object: ' + formatTestValue(object)));
+ }
+ },
+
+ assert.notHasKey = function (key, object) {
+ if (key in object) {
+ fail(new AssertionError('Unexpected key ' + formatTestValue(key) + ' is found in object: ' + formatTestValue(object)));
+ }
+ },
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // EXCEPTIONS
+
+ assert.throws = function(exception, fn) {
+ try {
+ fn();
+ } catch (e) {
+ if (e instanceof exception) {
+ return e;
+ }
+ fail(new AssertionError('Expected to throw "' + exception.name + '", actually threw: ' + formatTestValue(e) + ': ' + e.message),
+ {Expected: exception, Actual: e});
+ }
+ throw new AssertionError('did not throw');
+ },
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // TYPE
+
+ assert['instanceof'] = function(actual, type) {
+ if(!(actual instanceof type)) {
+ fail(new AssertionError(formatTestValue(actual) + ' not instance of ' + formatTestValue(type)),
+ {Type: type, Actual: actual});
+ }
+ },
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // DOM ASSERTIONS
+
+ // TODO: lift into separate file?
+ assert.dom = {
+ present: function(domElement){
+ if (!$(domElement).length) {
+ fail(new AssertionError(decipherDomElement(domElement) + ' should be present'));
+ }
+ },
+
+ notPresent: function(selector){
+ assert.equal(0, $(selector).length);
+ },
+
+ hasTag: function(tag, domElement) {
+ var elementTag = $(domElement)[0].tagName.toLowerCase();
+ if (elementTag !== tag.toLowerCase()) {
+ fail(new AssertionError(decipherDomElement(domElement) + ' expected to have tag name ' + formatTestValue(tag) + ', was ' + formatTestValue(elementTag) + ' instead'));
+ }
+ },
+
+ hasClass: function(className, domElement) {
+ if (!$(domElement).hasClass(className)){
+ fail(new AssertionError(decipherDomElement(domElement) + ' expected to have class '+ formatTestValue(className) + ', has ' + formatTestValue($(domElement).attr('class')) + ' instead'));
+ }
+ },
+
+ notHasClass: function(className, domElement) {
+ assert.dom.present(domElement); // if domElement is empty, .hasClass will always return false
+ if ($(domElement).hasClass(className)){
+ fail(new AssertionError(decipherDomElement(domElement) + ' expected NOT to have class '+ formatTestValue(className)));
+ }
+ },
+
+ hasAttribute: function(attributeName, selector) {
+ assert['true']($(selector).is('[' + attributeName + ']'));
+ },
+
+ notHasAttribute: function(attributeName, selector) {
+ assert.dom.present(selector);
+ assert['false']($(selector).is('[' + attributeName + ']'));
+ },
+
+ attr: function(value, attributeName, selector) {
+ assert.equal(value, $(selector).attr(attributeName));
+ },
+
+ attributeValues: function (values, selector) {
+ var $el = $(selector);
+ _(values).each(function (val, key) {
+ assert.equal(val, $el.attr(key));
+ });
+ },
+
+ text: function(expected, selector) {
+ assert.equal(expected, $(selector).text());
+ },
+
+ value: function(expected, selector) {
+ assert.equal(expected, $(selector).val());
+ },
+
+ count: function(elementCount, selector) {
+ assert.equal(elementCount, $(selector).length);
+ },
+
+ visible: function(domElement) {
+ if (!$(domElement).is(':visible')) {
+ fail(new AssertionError(decipherDomElement(domElement) + ' expected to be visible'));
+ }
+ },
+
+ notVisible: function(domElement) {
+ assert.dom.present(domElement);
+ if ($(domElement).is(':visible')) {
+ fail(new AssertionError(decipherDomElement(domElement) + ' expected to be NOT visible'));
+ }
+ },
+
+ disabled: function(domElement) {
+ if (!$(domElement).is(':disabled')) {
+ fail(new AssertionError(decipherDomElement(domElement) + ' expected to be disabled'));
+ }
+ },
+
+ enabled: function(domElement) {
+ if (!$(domElement).is(':enabled')) {
+ fail(new AssertionError(decipherDomElement(domElement) + ' expected to be enabled'));
+ }
+ },
+
+ focused: function(selector) {
+ var expected = $(selector)[0];
+ var actual = document.activeElement;
+ if (expected !== actual) {
+ throw new AssertionError(actual.outerHTML + ' has focus. expected: ' + expected.outerHTML);
+ }
+ },
+
+ notFocused: function(selector) {
+ var expected = $(selector)[0];
+ var actual = document.activeElement;
+ if (expected === actual) {
+ throw new AssertionError(expected.outerHTML + ' expected not to have focus.');
+ }
+ },
+
+ html: function(expected, selector) {
+ assert.equal(expected, $(selector).html());
+ },
+
+ css: function(expected, propertyName, selector) {
+ assert.equal(expected, $(selector).css(propertyName));
+ },
+
+ empty: function(selectorOrJQueryObject) {
+ var el = selectorOrJQueryObject;
+ assert.dom.present(el);
+ if (!$(el).is(':empty')) {
+ fail(new AssertionError(decipherDomElement(el) + ' expected to be empty'));
+ }
+ },
+
+ notEmpty: function(selectorOrJQueryObject) {
+ var el = selectorOrJQueryObject;
+ assert.dom.present(el);
+ if ($(el).is(':empty')) {
+ fail(new AssertionError(decipherDomElement(el) + ' expected NOT to be empty'));
+ }
+ }
+ };
+// };
+
+ function decipherDomElement(selectorOrJQueryObject) {
+ if (typeof selectorOrJQueryObject === 'string') {
+ return 'Selector ' + formatTestValue(selectorOrJQueryObject);
+ } else if (typeof selectorOrJQueryObject === 'object') {
+ return "'" + selectorOrJQueryObject[0] + "'";
+ }
+ }
+
+ var g = 'undefined' === typeof window ? global : window;
+
+ // synonyms
+ assert.equals = assert.equal;
+ assert.notEquals = assert.notEqual;
+ assert['null'] = assert.equal.bind(null, null);
+ assert.notNull = assert.notEqual.bind(null, null);
+ assert['undefined'] = assert.equal.bind(null, undefined);
+ assert.notUndefined = assert.notEqual.bind(null, undefined);
+
+ // ES3 synonyms
+ assert.false_ = assert['false'];
+ assert.true_ = assert['true'];
+
+ g.registerSuperFixture = registerSuperFixture;
+ g.test = test;
+ g.run_all = run_all;
+ g.fixture = fixture;
+// g.repr = IMVU.repr;
+ g.AssertionError = AssertionError;
+ g.assert = assert;
+ g.test = test;
+ g.TEST_MAX_OUTPUT_SIZE = 1024;
+
+ g.setTimeout = function(fn, time) {
+ if (time === 1 || time === 0){
+ fn();
+ return 0;
+ }
+ throw new AssertionError("Don't call setTimeout in tests. Use fakes.");
+ };
+
+ g.setInterval = function() {
+ throw new AssertionError("Don't call setInterval in tests. Use fakes.");
+ };
+
+ if (typeof process !== 'undefined') {
+ process.nextTick = function() {
+ throw new AssertionError("Don't call process.nextTick in tests. Use fakes.");
+ };
+ }
+
+ Math.random = function() {
+ throw new AssertionError("Don't call Math.random in tests. Use fakes.");
+ };
+
+ g.requestAnimationFrame = function() {
+ throw new AssertionError("Don't call requestAnimationFrame in tests. Use fakes.");
+ };
+//})();
+
+// Emscripten runner starts all tests from this function.
+// IMVU runner uses a separate runner & reporting mechanism.
+function run_all_tests() {
+ function report_to_stdout(msg) {
+ if (msg.type === "test-complete")
+ console.log(msg.name + ": " + msg.verdict);
+ }
+ run_all(report_to_stdout);
+}
+
+// Signal the embind test suite that it is being run from the Emscripten python test runner and not the
+// IMVU test runner.
+var INVOKED_FROM_EMSCRIPTEN_TEST_RUNNER = 1;
diff --git a/tests/embind/shell.html b/tests/embind/shell.html new file mode 100644 index 00000000..6664ec78 --- /dev/null +++ b/tests/embind/shell.html @@ -0,0 +1,94 @@ +<!doctype html> +<html lang="en-us"> + <head> + <meta charset="utf-8"> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> + <title>Emscripten-Generated Code</title> + <style> + .emscripten { padding-right: 0; margin-left: auto; margin-right: auto; display: block; } + textarea.emscripten { font-family: monospace; width: 80%; } + div.emscripten { text-align: center; } + div.emscripten_border { border: 1px solid black; } + /* the canvas *must not* have any border or padding, or mouse coords will be wrong */ + canvas.emscripten { border: 0px none; } + </style> + </head> + <body> + <div class="emscripten" id="status">Downloading...</div> + <div class="emscripten"> + <progress value="0" max="100" id="progress" hidden=1></progress> + </div> + <div class="emscripten_border" style="display:none;"> + <canvas class="emscripten" id="canvas" oncontextmenu="event.preventDefault()"></canvas> + </div> + <div class="emscripten" style="display:none;"> + <input type="checkbox" id="resize">Resize canvas + <input type="checkbox" id="pointerLock" checked>Lock/hide mouse pointer + + <input type="button" value="Fullscreen" onclick="Module.requestFullScreen(document.getElementById('pointerLock').checked, + document.getElementById('resize').checked)"> + </div> + + <hr/> + <textarea class="emscripten" id="output" rows="45"></textarea> + <hr> + <script type='text/javascript'> + // connect to canvas + var Module = { + preRun: [], + postRun: [], + print: (function() { + var element = document.getElementById('output'); + element.value = ''; // clear browser cache + return function(text) { + text = Array.prototype.slice.call(arguments).join(' '); + // These replacements are necessary if you render to raw HTML + //text = text.replace(/&/g, "&"); + //text = text.replace(/</g, "<"); + //text = text.replace(/>/g, ">"); + //text = text.replace('\n', '<br>', 'g'); + element.value += text + "\n"; + element.scrollTop = 99999; // focus on bottom + }; + })(), + printErr: function(text) { + text = Array.prototype.slice.call(arguments).join(' '); + if (0) { // XXX disabled for safety typeof dump == 'function') { + dump(text + '\n'); // fast, straight to the real console + } else { + console.log(text); + } + }, + canvas: document.getElementById('canvas'), + setStatus: function(text) { + if (Module.setStatus.interval) clearInterval(Module.setStatus.interval); + var m = text.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/); + var statusElement = document.getElementById('status'); + var progressElement = document.getElementById('progress'); + if (m) { + text = m[1]; + progressElement.value = parseInt(m[2])*100; + progressElement.max = parseInt(m[4])*100; + progressElement.hidden = false; + } else { + progressElement.value = null; + progressElement.max = null; + progressElement.hidden = true; + } + statusElement.innerHTML = text; + }, + totalDependencies: 0, + monitorRunDependencies: function(left) { + this.totalDependencies = Math.max(this.totalDependencies, left); + Module.setStatus(left ? 'Preparing... (' + (this.totalDependencies-left) + '/' + this.totalDependencies + ')' : 'All downloads complete.'); + } + }; + Module.setStatus('Downloading...'); + </script> + <script type='text/javascript'> + + {{{ SCRIPT_CODE }}} + + </script> + </body> +</html> diff --git a/tests/embind/underscore-1.4.2.js b/tests/embind/underscore-1.4.2.js new file mode 100755 index 00000000..dd274095 --- /dev/null +++ b/tests/embind/underscore-1.4.2.js @@ -0,0 +1,1200 @@ +// Underscore.js 1.4.2 +// http://underscorejs.org +// (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc. +// Underscore may be freely distributed under the MIT license. + +//(function() { + + // Baseline setup + // -------------- + + // Establish the root object, `window` in the browser, or `global` on the server. + var root = this; + + // Save the previous value of the `_` variable. + var previousUnderscore = root._; + + // Establish the object that gets returned to break out of a loop iteration. + var breaker = {}; + + // Save bytes in the minified (but not gzipped) version: + var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype; + + // Create quick reference variables for speed access to core prototypes. + var push = ArrayProto.push, + slice = ArrayProto.slice, + concat = ArrayProto.concat, + unshift = ArrayProto.unshift, + toString = ObjProto.toString, + hasOwnProperty = ObjProto.hasOwnProperty; + + // All **ECMAScript 5** native function implementations that we hope to use + // are declared here. + var + nativeForEach = ArrayProto.forEach, + nativeMap = ArrayProto.map, + nativeReduce = ArrayProto.reduce, + nativeReduceRight = ArrayProto.reduceRight, + nativeFilter = ArrayProto.filter, + nativeEvery = ArrayProto.every, + nativeSome = ArrayProto.some, + nativeIndexOf = ArrayProto.indexOf, + nativeLastIndexOf = ArrayProto.lastIndexOf, + nativeIsArray = Array.isArray, + nativeKeys = Object.keys, + nativeBind = FuncProto.bind; + + // Create a safe reference to the Underscore object for use below. + var _ = function(obj) { + if (obj instanceof _) return obj; + if (!(this instanceof _)) return new _(obj); + this._wrapped = obj; + }; + + // Export the Underscore object for **Node.js**, with + // backwards-compatibility for the old `require()` API. If we're in + // the browser, add `_` as a global object via a string identifier, + // for Closure Compiler "advanced" mode. + if (typeof exports !== 'undefined') { + if (typeof module !== 'undefined' && module.exports) { + exports = module.exports = _; + } + exports._ = _; + } else { + root['_'] = _; + } + + // Current version. + _.VERSION = '1.4.2'; + + // Collection Functions + // -------------------- + + // The cornerstone, an `each` implementation, aka `forEach`. + // Handles objects with the built-in `forEach`, arrays, and raw objects. + // Delegates to **ECMAScript 5**'s native `forEach` if available. + var each = _.each = _.forEach = function(obj, iterator, context) { + if (obj == null) return; + if (nativeForEach && obj.forEach === nativeForEach) { + obj.forEach(iterator, context); + } else if (obj.length === +obj.length) { + for (var i = 0, l = obj.length; i < l; i++) { + if (iterator.call(context, obj[i], i, obj) === breaker) return; + } + } else { + for (var key in obj) { + if (_.has(obj, key)) { + if (iterator.call(context, obj[key], key, obj) === breaker) return; + } + } + } + }; + + // Return the results of applying the iterator to each element. + // Delegates to **ECMAScript 5**'s native `map` if available. + _.map = _.collect = function(obj, iterator, context) { + var results = []; + if (obj == null) return results; + if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context); + each(obj, function(value, index, list) { + results[results.length] = iterator.call(context, value, index, list); + }); + return results; + }; + + // **Reduce** builds up a single result from a list of values, aka `inject`, + // or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available. + _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) { + var initial = arguments.length > 2; + if (obj == null) obj = []; + if (nativeReduce && obj.reduce === nativeReduce) { + if (context) iterator = _.bind(iterator, context); + return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator); + } + each(obj, function(value, index, list) { + if (!initial) { + memo = value; + initial = true; + } else { + memo = iterator.call(context, memo, value, index, list); + } + }); + if (!initial) throw new TypeError('Reduce of empty array with no initial value'); + return memo; + }; + + // The right-associative version of reduce, also known as `foldr`. + // Delegates to **ECMAScript 5**'s native `reduceRight` if available. + _.reduceRight = _.foldr = function(obj, iterator, memo, context) { + var initial = arguments.length > 2; + if (obj == null) obj = []; + if (nativeReduceRight && obj.reduceRight === nativeReduceRight) { + if (context) iterator = _.bind(iterator, context); + return arguments.length > 2 ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator); + } + var length = obj.length; + if (length !== +length) { + var keys = _.keys(obj); + length = keys.length; + } + each(obj, function(value, index, list) { + index = keys ? keys[--length] : --length; + if (!initial) { + memo = obj[index]; + initial = true; + } else { + memo = iterator.call(context, memo, obj[index], index, list); + } + }); + if (!initial) throw new TypeError('Reduce of empty array with no initial value'); + return memo; + }; + + // Return the first value which passes a truth test. Aliased as `detect`. + _.find = _.detect = function(obj, iterator, context) { + var result; + any(obj, function(value, index, list) { + if (iterator.call(context, value, index, list)) { + result = value; + return true; + } + }); + return result; + }; + + // Return all the elements that pass a truth test. + // Delegates to **ECMAScript 5**'s native `filter` if available. + // Aliased as `select`. + _.filter = _.select = function(obj, iterator, context) { + var results = []; + if (obj == null) return results; + if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context); + each(obj, function(value, index, list) { + if (iterator.call(context, value, index, list)) results[results.length] = value; + }); + return results; + }; + + // Return all the elements for which a truth test fails. + _.reject = function(obj, iterator, context) { + var results = []; + if (obj == null) return results; + each(obj, function(value, index, list) { + if (!iterator.call(context, value, index, list)) results[results.length] = value; + }); + return results; + }; + + // Determine whether all of the elements match a truth test. + // Delegates to **ECMAScript 5**'s native `every` if available. + // Aliased as `all`. + _.every = _.all = function(obj, iterator, context) { + iterator || (iterator = _.identity); + var result = true; + if (obj == null) return result; + if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context); + each(obj, function(value, index, list) { + if (!(result = result && iterator.call(context, value, index, list))) return breaker; + }); + return !!result; + }; + + // Determine if at least one element in the object matches a truth test. + // Delegates to **ECMAScript 5**'s native `some` if available. + // Aliased as `any`. + var any = _.some = _.any = function(obj, iterator, context) { + iterator || (iterator = _.identity); + var result = false; + if (obj == null) return result; + if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context); + each(obj, function(value, index, list) { + if (result || (result = iterator.call(context, value, index, list))) return breaker; + }); + return !!result; + }; + + // Determine if the array or object contains a given value (using `===`). + // Aliased as `include`. + _.contains = _.include = function(obj, target) { + var found = false; + if (obj == null) return found; + if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1; + found = any(obj, function(value) { + return value === target; + }); + return found; + }; + + // Invoke a method (with arguments) on every item in a collection. + _.invoke = function(obj, method) { + var args = slice.call(arguments, 2); + return _.map(obj, function(value) { + return (_.isFunction(method) ? method : value[method]).apply(value, args); + }); + }; + + // Convenience version of a common use case of `map`: fetching a property. + _.pluck = function(obj, key) { + return _.map(obj, function(value){ return value[key]; }); + }; + + // Convenience version of a common use case of `filter`: selecting only objects + // with specific `key:value` pairs. + _.where = function(obj, attrs) { + if (_.isEmpty(attrs)) return []; + return _.filter(obj, function(value) { + for (var key in attrs) { + if (attrs[key] !== value[key]) return false; + } + return true; + }); + }; + + // Return the maximum element or (element-based computation). + // Can't optimize arrays of integers longer than 65,535 elements. + // See: https://bugs.webkit.org/show_bug.cgi?id=80797 + _.max = function(obj, iterator, context) { + if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) { + return Math.max.apply(Math, obj); + } + if (!iterator && _.isEmpty(obj)) return -Infinity; + var result = {computed : -Infinity}; + each(obj, function(value, index, list) { + var computed = iterator ? iterator.call(context, value, index, list) : value; + computed >= result.computed && (result = {value : value, computed : computed}); + }); + return result.value; + }; + + // Return the minimum element (or element-based computation). + _.min = function(obj, iterator, context) { + if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) { + return Math.min.apply(Math, obj); + } + if (!iterator && _.isEmpty(obj)) return Infinity; + var result = {computed : Infinity}; + each(obj, function(value, index, list) { + var computed = iterator ? iterator.call(context, value, index, list) : value; + computed < result.computed && (result = {value : value, computed : computed}); + }); + return result.value; + }; + + // Shuffle an array. + _.shuffle = function(obj) { + var rand; + var index = 0; + var shuffled = []; + each(obj, function(value) { + rand = _.random(index++); + shuffled[index - 1] = shuffled[rand]; + shuffled[rand] = value; + }); + return shuffled; + }; + + // An internal function to generate lookup iterators. + var lookupIterator = function(value) { + return _.isFunction(value) ? value : function(obj){ return obj[value]; }; + }; + + // Sort the object's values by a criterion produced by an iterator. + _.sortBy = function(obj, value, context) { + var iterator = lookupIterator(value); + return _.pluck(_.map(obj, function(value, index, list) { + return { + value : value, + index : index, + criteria : iterator.call(context, value, index, list) + }; + }).sort(function(left, right) { + var a = left.criteria; + var b = right.criteria; + if (a !== b) { + if (a > b || a === void 0) return 1; + if (a < b || b === void 0) return -1; + } + return left.index < right.index ? -1 : 1; + }), 'value'); + }; + + // An internal function used for aggregate "group by" operations. + var group = function(obj, value, context, behavior) { + var result = {}; + var iterator = lookupIterator(value); + each(obj, function(value, index) { + var key = iterator.call(context, value, index, obj); + behavior(result, key, value); + }); + return result; + }; + + // Groups the object's values by a criterion. Pass either a string attribute + // to group by, or a function that returns the criterion. + _.groupBy = function(obj, value, context) { + return group(obj, value, context, function(result, key, value) { + (_.has(result, key) ? result[key] : (result[key] = [])).push(value); + }); + }; + + // Counts instances of an object that group by a certain criterion. Pass + // either a string attribute to count by, or a function that returns the + // criterion. + _.countBy = function(obj, value, context) { + return group(obj, value, context, function(result, key, value) { + if (!_.has(result, key)) result[key] = 0; + result[key]++; + }); + }; + + // Use a comparator function to figure out the smallest index at which + // an object should be inserted so as to maintain order. Uses binary search. + _.sortedIndex = function(array, obj, iterator, context) { + iterator = iterator == null ? _.identity : lookupIterator(iterator); + var value = iterator.call(context, obj); + var low = 0, high = array.length; + while (low < high) { + var mid = (low + high) >>> 1; + iterator.call(context, array[mid]) < value ? low = mid + 1 : high = mid; + } + return low; + }; + + // Safely convert anything iterable into a real, live array. + _.toArray = function(obj) { + if (!obj) return []; + if (obj.length === +obj.length) return slice.call(obj); + return _.values(obj); + }; + + // Return the number of elements in an object. + _.size = function(obj) { + return (obj.length === +obj.length) ? obj.length : _.keys(obj).length; + }; + + // Array Functions + // --------------- + + // Get the first element of an array. Passing **n** will return the first N + // values in the array. Aliased as `head` and `take`. The **guard** check + // allows it to work with `_.map`. + _.first = _.head = _.take = function(array, n, guard) { + return (n != null) && !guard ? slice.call(array, 0, n) : array[0]; + }; + + // Returns everything but the last entry of the array. Especially useful on + // the arguments object. Passing **n** will return all the values in + // the array, excluding the last N. The **guard** check allows it to work with + // `_.map`. + _.initial = function(array, n, guard) { + return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n)); + }; + + // Get the last element of an array. Passing **n** will return the last N + // values in the array. The **guard** check allows it to work with `_.map`. + _.last = function(array, n, guard) { + if ((n != null) && !guard) { + return slice.call(array, Math.max(array.length - n, 0)); + } else { + return array[array.length - 1]; + } + }; + + // Returns everything but the first entry of the array. Aliased as `tail` and `drop`. + // Especially useful on the arguments object. Passing an **n** will return + // the rest N values in the array. The **guard** + // check allows it to work with `_.map`. + _.rest = _.tail = _.drop = function(array, n, guard) { + return slice.call(array, (n == null) || guard ? 1 : n); + }; + + // Trim out all falsy values from an array. + _.compact = function(array) { + return _.filter(array, function(value){ return !!value; }); + }; + + // Internal implementation of a recursive `flatten` function. + var flatten = function(input, shallow, output) { + each(input, function(value) { + if (_.isArray(value)) { + shallow ? push.apply(output, value) : flatten(value, shallow, output); + } else { + output.push(value); + } + }); + return output; + }; + + // Return a completely flattened version of an array. + _.flatten = function(array, shallow) { + return flatten(array, shallow, []); + }; + + // Return a version of the array that does not contain the specified value(s). + _.without = function(array) { + return _.difference(array, slice.call(arguments, 1)); + }; + + // Produce a duplicate-free version of the array. If the array has already + // been sorted, you have the option of using a faster algorithm. + // Aliased as `unique`. + _.uniq = _.unique = function(array, isSorted, iterator, context) { + var initial = iterator ? _.map(array, iterator, context) : array; + var results = []; + var seen = []; + each(initial, function(value, index) { + if (isSorted ? (!index || seen[seen.length - 1] !== value) : !_.contains(seen, value)) { + seen.push(value); + results.push(array[index]); + } + }); + return results; + }; + + // Produce an array that contains the union: each distinct element from all of + // the passed-in arrays. + _.union = function() { + return _.uniq(concat.apply(ArrayProto, arguments)); + }; + + // Produce an array that contains every item shared between all the + // passed-in arrays. + _.intersection = function(array) { + var rest = slice.call(arguments, 1); + return _.filter(_.uniq(array), function(item) { + return _.every(rest, function(other) { + return _.indexOf(other, item) >= 0; + }); + }); + }; + + // Take the difference between one array and a number of other arrays. + // Only the elements present in just the first array will remain. + _.difference = function(array) { + var rest = concat.apply(ArrayProto, slice.call(arguments, 1)); + return _.filter(array, function(value){ return !_.contains(rest, value); }); + }; + + // Zip together multiple lists into a single array -- elements that share + // an index go together. + _.zip = function() { + var args = slice.call(arguments); + var length = _.max(_.pluck(args, 'length')); + var results = new Array(length); + for (var i = 0; i < length; i++) { + results[i] = _.pluck(args, "" + i); + } + return results; + }; + + // Converts lists into objects. Pass either a single array of `[key, value]` + // pairs, or two parallel arrays of the same length -- one of keys, and one of + // the corresponding values. + _.object = function(list, values) { + var result = {}; + for (var i = 0, l = list.length; i < l; i++) { + if (values) { + result[list[i]] = values[i]; + } else { + result[list[i][0]] = list[i][1]; + } + } + return result; + }; + + // If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**), + // we need this function. Return the position of the first occurrence of an + // item in an array, or -1 if the item is not included in the array. + // Delegates to **ECMAScript 5**'s native `indexOf` if available. + // If the array is large and already in sort order, pass `true` + // for **isSorted** to use binary search. + _.indexOf = function(array, item, isSorted) { + if (array == null) return -1; + var i = 0, l = array.length; + if (isSorted) { + if (typeof isSorted == 'number') { + i = (isSorted < 0 ? Math.max(0, l + isSorted) : isSorted); + } else { + i = _.sortedIndex(array, item); + return array[i] === item ? i : -1; + } + } + if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item, isSorted); + for (; i < l; i++) if (array[i] === item) return i; + return -1; + }; + + // Delegates to **ECMAScript 5**'s native `lastIndexOf` if available. + _.lastIndexOf = function(array, item, from) { + if (array == null) return -1; + var hasIndex = from != null; + if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) { + return hasIndex ? array.lastIndexOf(item, from) : array.lastIndexOf(item); + } + var i = (hasIndex ? from : array.length); + while (i--) if (array[i] === item) return i; + return -1; + }; + + // Generate an integer Array containing an arithmetic progression. A port of + // the native Python `range()` function. See + // [the Python documentation](http://docs.python.org/library/functions.html#range). + _.range = function(start, stop, step) { + if (arguments.length <= 1) { + stop = start || 0; + start = 0; + } + step = arguments[2] || 1; + + var len = Math.max(Math.ceil((stop - start) / step), 0); + var idx = 0; + var range = new Array(len); + + while(idx < len) { + range[idx++] = start; + start += step; + } + + return range; + }; + + // Function (ahem) Functions + // ------------------ + + // Reusable constructor function for prototype setting. + var ctor = function(){}; + + // Create a function bound to a given object (assigning `this`, and arguments, + // optionally). Binding with arguments is also known as `curry`. + // Delegates to **ECMAScript 5**'s native `Function.bind` if available. + // We check for `func.bind` first, to fail fast when `func` is undefined. + _.bind = function bind(func, context) { + var bound, args; + if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1)); + if (!_.isFunction(func)) throw new TypeError; + args = slice.call(arguments, 2); + return bound = function() { + if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments))); + ctor.prototype = func.prototype; + var self = new ctor; + var result = func.apply(self, args.concat(slice.call(arguments))); + if (Object(result) === result) return result; + return self; + }; + }; + + // Bind all of an object's methods to that object. Useful for ensuring that + // all callbacks defined on an object belong to it. + _.bindAll = function(obj) { + var funcs = slice.call(arguments, 1); + if (funcs.length == 0) funcs = _.functions(obj); + each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); }); + return obj; + }; + + // Memoize an expensive function by storing its results. + _.memoize = function(func, hasher) { + var memo = {}; + hasher || (hasher = _.identity); + return function() { + var key = hasher.apply(this, arguments); + return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments)); + }; + }; + + // Delays a function for the given number of milliseconds, and then calls + // it with the arguments supplied. + _.delay = function(func, wait) { + var args = slice.call(arguments, 2); + return setTimeout(function(){ return func.apply(null, args); }, wait); + }; + + // Defers a function, scheduling it to run after the current call stack has + // cleared. + _.defer = function(func) { + return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1))); + }; + + // Returns a function, that, when invoked, will only be triggered at most once + // during a given window of time. + _.throttle = function(func, wait) { + var context, args, timeout, throttling, more, result; + var whenDone = _.debounce(function(){ more = throttling = false; }, wait); + return function() { + context = this; args = arguments; + var later = function() { + timeout = null; + if (more) { + result = func.apply(context, args); + } + whenDone(); + }; + if (!timeout) timeout = setTimeout(later, wait); + if (throttling) { + more = true; + } else { + throttling = true; + result = func.apply(context, args); + } + whenDone(); + return result; + }; + }; + + // Returns a function, that, as long as it continues to be invoked, will not + // be triggered. The function will be called after it stops being called for + // N milliseconds. If `immediate` is passed, trigger the function on the + // leading edge, instead of the trailing. + _.debounce = function(func, wait, immediate) { + var timeout, result; + return function() { + var context = this, args = arguments; + var later = function() { + timeout = null; + if (!immediate) result = func.apply(context, args); + }; + var callNow = immediate && !timeout; + clearTimeout(timeout); + timeout = setTimeout(later, wait); + if (callNow) result = func.apply(context, args); + return result; + }; + }; + + // Returns a function that will be executed at most one time, no matter how + // often you call it. Useful for lazy initialization. + _.once = function(func) { + var ran = false, memo; + return function() { + if (ran) return memo; + ran = true; + memo = func.apply(this, arguments); + func = null; + return memo; + }; + }; + + // Returns the first function passed as an argument to the second, + // allowing you to adjust arguments, run code before and after, and + // conditionally execute the original function. + _.wrap = function(func, wrapper) { + return function() { + var args = [func]; + push.apply(args, arguments); + return wrapper.apply(this, args); + }; + }; + + // Returns a function that is the composition of a list of functions, each + // consuming the return value of the function that follows. + _.compose = function() { + var funcs = arguments; + return function() { + var args = arguments; + for (var i = funcs.length - 1; i >= 0; i--) { + args = [funcs[i].apply(this, args)]; + } + return args[0]; + }; + }; + + // Returns a function that will only be executed after being called N times. + _.after = function(times, func) { + if (times <= 0) return func(); + return function() { + if (--times < 1) { + return func.apply(this, arguments); + } + }; + }; + + // Object Functions + // ---------------- + + // Retrieve the names of an object's properties. + // Delegates to **ECMAScript 5**'s native `Object.keys` + _.keys = nativeKeys || function(obj) { + if (obj !== Object(obj)) throw new TypeError('Invalid object'); + var keys = []; + for (var key in obj) if (_.has(obj, key)) keys[keys.length] = key; + return keys; + }; + + // Retrieve the values of an object's properties. + _.values = function(obj) { + var values = []; + for (var key in obj) if (_.has(obj, key)) values.push(obj[key]); + return values; + }; + + // Convert an object into a list of `[key, value]` pairs. + _.pairs = function(obj) { + var pairs = []; + for (var key in obj) if (_.has(obj, key)) pairs.push([key, obj[key]]); + return pairs; + }; + + // Invert the keys and values of an object. The values must be serializable. + _.invert = function(obj) { + var result = {}; + for (var key in obj) if (_.has(obj, key)) result[obj[key]] = key; + return result; + }; + + // Return a sorted list of the function names available on the object. + // Aliased as `methods` + _.functions = _.methods = function(obj) { + var names = []; + for (var key in obj) { + if (_.isFunction(obj[key])) names.push(key); + } + return names.sort(); + }; + + // Extend a given object with all the properties in passed-in object(s). + _.extend = function(obj) { + each(slice.call(arguments, 1), function(source) { + for (var prop in source) { + obj[prop] = source[prop]; + } + }); + return obj; + }; + + // Return a copy of the object only containing the whitelisted properties. + _.pick = function(obj) { + var copy = {}; + var keys = concat.apply(ArrayProto, slice.call(arguments, 1)); + each(keys, function(key) { + if (key in obj) copy[key] = obj[key]; + }); + return copy; + }; + + // Return a copy of the object without the blacklisted properties. + _.omit = function(obj) { + var copy = {}; + var keys = concat.apply(ArrayProto, slice.call(arguments, 1)); + for (var key in obj) { + if (!_.contains(keys, key)) copy[key] = obj[key]; + } + return copy; + }; + + // Fill in a given object with default properties. + _.defaults = function(obj) { + each(slice.call(arguments, 1), function(source) { + for (var prop in source) { + if (obj[prop] == null) obj[prop] = source[prop]; + } + }); + return obj; + }; + + // Create a (shallow-cloned) duplicate of an object. + _.clone = function(obj) { + if (!_.isObject(obj)) return obj; + return _.isArray(obj) ? obj.slice() : _.extend({}, obj); + }; + + // Invokes interceptor with the obj, and then returns obj. + // The primary purpose of this method is to "tap into" a method chain, in + // order to perform operations on intermediate results within the chain. + _.tap = function(obj, interceptor) { + interceptor(obj); + return obj; + }; + + // Internal recursive comparison function for `isEqual`. + var eq = function(a, b, aStack, bStack) { + // Identical objects are equal. `0 === -0`, but they aren't identical. + // See the Harmony `egal` proposal: http://wiki.ecmascript.org/doku.php?id=harmony:egal. + if (a === b) return a !== 0 || 1 / a == 1 / b; + // A strict comparison is necessary because `null == undefined`. + if (a == null || b == null) return a === b; + // Unwrap any wrapped objects. + if (a instanceof _) a = a._wrapped; + if (b instanceof _) b = b._wrapped; + // Compare `[[Class]]` names. + var className = toString.call(a); + if (className != toString.call(b)) return false; + switch (className) { + // Strings, numbers, dates, and booleans are compared by value. + case '[object String]': + // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is + // equivalent to `new String("5")`. + return a == String(b); + case '[object Number]': + // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for + // other numeric values. + return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b); + case '[object Date]': + case '[object Boolean]': + // Coerce dates and booleans to numeric primitive values. Dates are compared by their + // millisecond representations. Note that invalid dates with millisecond representations + // of `NaN` are not equivalent. + return +a == +b; + // RegExps are compared by their source patterns and flags. + case '[object RegExp]': + return a.source == b.source && + a.global == b.global && + a.multiline == b.multiline && + a.ignoreCase == b.ignoreCase; + } + if (typeof a != 'object' || typeof b != 'object') return false; + // Assume equality for cyclic structures. The algorithm for detecting cyclic + // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`. + var length = aStack.length; + while (length--) { + // Linear search. Performance is inversely proportional to the number of + // unique nested structures. + if (aStack[length] == a) return bStack[length] == b; + } + // Add the first object to the stack of traversed objects. + aStack.push(a); + bStack.push(b); + var size = 0, result = true; + // Recursively compare objects and arrays. + if (className == '[object Array]') { + // Compare array lengths to determine if a deep comparison is necessary. + size = a.length; + result = size == b.length; + if (result) { + // Deep compare the contents, ignoring non-numeric properties. + while (size--) { + if (!(result = eq(a[size], b[size], aStack, bStack))) break; + } + } + } else { + // Objects with different constructors are not equivalent, but `Object`s + // from different frames are. + var aCtor = a.constructor, bCtor = b.constructor; + if (aCtor !== bCtor && !(_.isFunction(aCtor) && (aCtor instanceof aCtor) && + _.isFunction(bCtor) && (bCtor instanceof bCtor))) { + return false; + } + // Deep compare objects. + for (var key in a) { + if (_.has(a, key)) { + // Count the expected number of properties. + size++; + // Deep compare each member. + if (!(result = _.has(b, key) && eq(a[key], b[key], aStack, bStack))) break; + } + } + // Ensure that both objects contain the same number of properties. + if (result) { + for (key in b) { + if (_.has(b, key) && !(size--)) break; + } + result = !size; + } + } + // Remove the first object from the stack of traversed objects. + aStack.pop(); + bStack.pop(); + return result; + }; + + // Perform a deep comparison to check if two objects are equal. + _.isEqual = function(a, b) { + return eq(a, b, [], []); + }; + + // Is a given array, string, or object empty? + // An "empty" object has no enumerable own-properties. + _.isEmpty = function(obj) { + if (obj == null) return true; + if (_.isArray(obj) || _.isString(obj)) return obj.length === 0; + for (var key in obj) if (_.has(obj, key)) return false; + return true; + }; + + // Is a given value a DOM element? + _.isElement = function(obj) { + return !!(obj && obj.nodeType === 1); + }; + + // Is a given value an array? + // Delegates to ECMA5's native Array.isArray + _.isArray = nativeIsArray || function(obj) { + return toString.call(obj) == '[object Array]'; + }; + + // Is a given variable an object? + _.isObject = function(obj) { + return obj === Object(obj); + }; + + // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp. + each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function(name) { + _['is' + name] = function(obj) { + return toString.call(obj) == '[object ' + name + ']'; + }; + }); + + // Define a fallback version of the method in browsers (ahem, IE), where + // there isn't any inspectable "Arguments" type. + if (!_.isArguments(arguments)) { + _.isArguments = function(obj) { + return !!(obj && _.has(obj, 'callee')); + }; + } + + // Optimize `isFunction` if appropriate. + if (typeof (/./) !== 'function') { + _.isFunction = function(obj) { + return typeof obj === 'function'; + }; + } + + // Is a given object a finite number? + _.isFinite = function(obj) { + return _.isNumber(obj) && isFinite(obj); + }; + + // Is the given value `NaN`? (NaN is the only number which does not equal itself). + _.isNaN = function(obj) { + return _.isNumber(obj) && obj != +obj; + }; + + // Is a given value a boolean? + _.isBoolean = function(obj) { + return obj === true || obj === false || toString.call(obj) == '[object Boolean]'; + }; + + // Is a given value equal to null? + _.isNull = function(obj) { + return obj === null; + }; + + // Is a given variable undefined? + _.isUndefined = function(obj) { + return obj === void 0; + }; + + // Shortcut function for checking if an object has a given property directly + // on itself (in other words, not on a prototype). + _.has = function(obj, key) { + return hasOwnProperty.call(obj, key); + }; + + // Utility Functions + // ----------------- + + // Run Underscore.js in *noConflict* mode, returning the `_` variable to its + // previous owner. Returns a reference to the Underscore object. + _.noConflict = function() { + root._ = previousUnderscore; + return this; + }; + + // Keep the identity function around for default iterators. + _.identity = function(value) { + return value; + }; + + // Run a function **n** times. + _.times = function(n, iterator, context) { + for (var i = 0; i < n; i++) iterator.call(context, i); + }; + + // Return a random integer between min and max (inclusive). + _.random = function(min, max) { + if (max == null) { + max = min; + min = 0; + } + return min + (0 | Math.random() * (max - min + 1)); + }; + + // List of HTML entities for escaping. + var entityMap = { + escape: { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''', + '/': '/' + } + }; + entityMap.unescape = _.invert(entityMap.escape); + + // Regexes containing the keys and values listed immediately above. + var entityRegexes = { + escape: new RegExp('[' + _.keys(entityMap.escape).join('') + ']', 'g'), + unescape: new RegExp('(' + _.keys(entityMap.unescape).join('|') + ')', 'g') + }; + + // Functions for escaping and unescaping strings to/from HTML interpolation. + _.each(['escape', 'unescape'], function(method) { + _[method] = function(string) { + if (string == null) return ''; + return ('' + string).replace(entityRegexes[method], function(match) { + return entityMap[method][match]; + }); + }; + }); + + // If the value of the named property is a function then invoke it; + // otherwise, return it. + _.result = function(object, property) { + if (object == null) return null; + var value = object[property]; + return _.isFunction(value) ? value.call(object) : value; + }; + + // Add your own custom functions to the Underscore object. + _.mixin = function(obj) { + each(_.functions(obj), function(name){ + var func = _[name] = obj[name]; + _.prototype[name] = function() { + var args = [this._wrapped]; + push.apply(args, arguments); + return result.call(this, func.apply(_, args)); + }; + }); + }; + + // Generate a unique integer id (unique within the entire client session). + // Useful for temporary DOM ids. + var idCounter = 0; + _.uniqueId = function(prefix) { + var id = idCounter++; + return prefix ? prefix + id : id; + }; + + // By default, Underscore uses ERB-style template delimiters, change the + // following template settings to use alternative delimiters. + _.templateSettings = { + evaluate : /<%([\s\S]+?)%>/g, + interpolate : /<%=([\s\S]+?)%>/g, + escape : /<%-([\s\S]+?)%>/g + }; + + // When customizing `templateSettings`, if you don't want to define an + // interpolation, evaluation or escaping regex, we need one that is + // guaranteed not to match. + var noMatch = /(.)^/; + + // Certain characters need to be escaped so that they can be put into a + // string literal. + var escapes = { + "'": "'", + '\\': '\\', + '\r': 'r', + '\n': 'n', + '\t': 't', + '\u2028': 'u2028', + '\u2029': 'u2029' + }; + + var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g; + + // JavaScript micro-templating, similar to John Resig's implementation. + // Underscore templating handles arbitrary delimiters, preserves whitespace, + // and correctly escapes quotes within interpolated code. + _.template = function(text, data, settings) { + settings = _.defaults({}, settings, _.templateSettings); + + // Combine delimiters into one regular expression via alternation. + var matcher = new RegExp([ + (settings.escape || noMatch).source, + (settings.interpolate || noMatch).source, + (settings.evaluate || noMatch).source + ].join('|') + '|$', 'g'); + + // Compile the template source, escaping string literals appropriately. + var index = 0; + var source = "__p+='"; + text.replace(matcher, function(match, escape, interpolate, evaluate, offset) { + source += text.slice(index, offset) + .replace(escaper, function(match) { return '\\' + escapes[match]; }); + source += + escape ? "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'" : + interpolate ? "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'" : + evaluate ? "';\n" + evaluate + "\n__p+='" : ''; + index = offset + match.length; + }); + source += "';\n"; + + // If a variable is not specified, place data values in local scope. + if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n'; + + source = "var __t,__p='',__j=Array.prototype.join," + + "print=function(){__p+=__j.call(arguments,'');};\n" + + source + "return __p;\n"; + + try { + var render = new Function(settings.variable || 'obj', '_', source); + } catch (e) { + e.source = source; + throw e; + } + + if (data) return render(data, _); + var template = function(data) { + return render.call(this, data, _); + }; + + // Provide the compiled function source as a convenience for precompilation. + template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}'; + + return template; + }; + + // Add a "chain" function, which will delegate to the wrapper. + _.chain = function(obj) { + return _(obj).chain(); + }; + + // OOP + // --------------- + // If Underscore is called as a function, it returns a wrapped object that + // can be used OO-style. This wrapper holds altered versions of all the + // underscore functions. Wrapped objects may be chained. + + // Helper function to continue chaining intermediate results. + var result = function(obj) { + return this._chain ? _(obj).chain() : obj; + }; + + // Add all of the Underscore functions to the wrapper object. + _.mixin(_); + + // Add all mutator Array functions to the wrapper. + each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) { + var method = ArrayProto[name]; + _.prototype[name] = function() { + var obj = this._wrapped; + method.apply(obj, arguments); + if ((name == 'shift' || name == 'splice') && obj.length === 0) delete obj[0]; + return result.call(this, obj); + }; + }); + + // Add all accessor Array functions to the wrapper. + each(['concat', 'join', 'slice'], function(name) { + var method = ArrayProto[name]; + _.prototype[name] = function() { + return result.call(this, method.apply(this._wrapped, arguments)); + }; + }); + + _.extend(_.prototype, { + + // Start chaining a wrapped Underscore object. + chain: function() { + this._chain = true; + return this; + }, + + // Extracts the result from a wrapped and chained object. + value: function() { + return this._wrapped; + } + + }); + +//}).call(this); diff --git a/tests/gl_stride.c b/tests/gl_stride.c new file mode 100644 index 00000000..c254ad5a --- /dev/null +++ b/tests/gl_stride.c @@ -0,0 +1,152 @@ +/******************************************************************* + * * + * Using SDL With OpenGL * + * * + * Tutorial by Kyle Foley (sdw) * + * * + * http://gpwiki.org/index.php/SDL:Tutorials:Using_SDL_with_OpenGL * + * * + *******************************************************************/ + +/* +THIS WORK, INCLUDING THE SOURCE CODE, DOCUMENTATION +AND RELATED MEDIA AND DATA, IS PLACED INTO THE PUBLIC DOMAIN. + +THE ORIGINAL AUTHOR IS KYLE FOLEY. + +THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY +OF ANY KIND, NOT EVEN THE IMPLIED WARRANTY OF +MERCHANTABILITY. THE AUTHOR OF THIS SOFTWARE, +ASSUMES _NO_ RESPONSIBILITY FOR ANY CONSEQUENCE +RESULTING FROM THE USE, MODIFICATION, OR +REDISTRIBUTION OF THIS SOFTWARE. +*/ + +#if !EMSCRIPTEN +#define USE_GLEW 0 +#endif + +#if USE_GLEW +#include "GL/glew.h" +#endif + +#include "SDL/SDL.h" +#include "SDL/SDL_image.h" +#if !USE_GLEW +#include "SDL/SDL_opengl.h" +#endif + +#include <stdio.h> +#include <string.h> +#include <assert.h> + +int main(int argc, char *argv[]) +{ + SDL_Surface *screen; + + // Slightly different SDL initialization + if ( SDL_Init(SDL_INIT_VIDEO) != 0 ) { + printf("Unable to initialize SDL: %s\n", SDL_GetError()); + return 1; + } + + SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 ); // *new* + + screen = SDL_SetVideoMode( 640, 480, 16, SDL_OPENGL ); // *changed* + if ( !screen ) { + printf("Unable to set video mode: %s\n", SDL_GetError()); + return 1; + } + + // Set the OpenGL state after creating the context with SDL_SetVideoMode + + glClearColor( 0, 0, 0, 0 ); + +#if !EMSCRIPTEN + glEnable( GL_TEXTURE_2D ); // Need this to display a texture XXX unnecessary in OpenGL ES 2.0/WebGL +#endif + + glViewport( 0, 0, 640, 480 ); + + glMatrixMode( GL_MODELVIEW ); + glLoadIdentity(); + + // Clear the screen before drawing + glClear( GL_COLOR_BUFFER_BIT ); + + typedef struct Vertex { + GLfloat x; + GLfloat y; + } Vertex; + + typedef struct Color { + GLubyte r; + GLubyte g; + GLubyte b; + GLubyte a; + } Color; + + Vertex vertices[3] = { + {-1.0, 0.0}, + { 0.0, 1.0}, + { 1.0, 0.0} + }; + + Color colors[3] = { + {0xFF, 0x00, 0x00, 0xFF}, + {0x00, 0xFF, 0x00, 0xFF}, + {0x00, 0x00, 0xFF, 0xFF} + }; + + Vertex vertices2[3] = { + {-1.0, 0.0}, + { 1.0, 0.0}, + { 0.0, -1.0} + }; + + Color colors2[3] = { + {0xFF, 0x00, 0x00, 0xFF}, + {0x00, 0x00, 0xFF, 0xFF}, + {0x00, 0xFF, 0x00, 0xFF} + }; + + // DRAW + + // Clear the screen before drawing + glClear( GL_COLOR_BUFFER_BIT ); + + // This test ensures that we can use two separate arrays in memory for different + // attributes, and that they each can have different stride. + // The first test shows implicit striding (the zero indicates tightly packed) + // The second test shows explicit striding where the stride is passed in + // even though it also is tightly packed + + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); + + // TEST 1 + + glVertexPointer(2, GL_FLOAT, 0, vertices); + glColorPointer(4, GL_UNSIGNED_BYTE, 0, colors); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 3); + + // TEST 2 + + glVertexPointer(2, GL_FLOAT, 8, vertices2); + glColorPointer(4, GL_UNSIGNED_BYTE, 4, colors2); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 3); + + glDisableClientState(GL_COLOR_ARRAY); + glDisableClientState(GL_VERTEX_ARRAY); + + SDL_GL_SwapBuffers(); + +#if !EMSCRIPTEN + // Wait for 3 seconds to give us a chance to see the image + SDL_Delay(3000); +#endif + + SDL_Quit(); + + return 0; +} diff --git a/tests/gl_stride.png b/tests/gl_stride.png Binary files differnew file mode 100644 index 00000000..db1bc751 --- /dev/null +++ b/tests/gl_stride.png diff --git a/tests/runner.py b/tests/runner.py index 890d42e7..868ef955 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -266,11 +266,11 @@ process(sys.argv[1]) if output_processor is not None: output_processor(open(filename + '.o.js').read()) - if self.emcc_args is not None: + if self.emcc_args is not None and 'ASM_JS=1' in self.emcc_args: if '--memory-init-file' in self.emcc_args: memory_init_file = int(self.emcc_args[self.emcc_args.index('--memory-init-file')+1]) else: - memory_init_file = 1 + memory_init_file = 0 if memory_init_file: assert '/* memory initializer */' not in open(filename + '.o.js').read() else: @@ -455,7 +455,7 @@ if 'benchmark' not in str(sys.argv) and 'sanity' not in str(sys.argv) and 'brows if len(sys.argv) == 2 and 'ALL.' in sys.argv[1]: ignore, test = sys.argv[1].split('.') print 'Running all test modes on test "%s"' % test - sys.argv = [sys.argv[0], 'default.'+test, 'o1.'+test, 'o2.'+test, 'asm2.'+test, 'asm2g.'+test, 's_0_0.'+test, 's_0_1.'+test, 's_1_0.'+test, 's_1_1.'+test] + sys.argv = [sys.argv[0], 'default.'+test, 'o1.'+test, 'o2.'+test, 'asm1.'+test, 'asm2.'+test, 'asm2g.'+test, 's_0_0.'+test, 's_0_1.'+test, 's_1_0.'+test, 's_1_1.'+test] class T(RunnerCore): # Short name, to make it more fun to use manually on the commandline ## Does a complete test - builds, runs, checks output, etc. @@ -735,7 +735,8 @@ if 'benchmark' not in str(sys.argv) and 'sanity' not in str(sys.argv) and 'brows uint64_t a = 5; double b = 6.8; uint64_t c = a * b; - printf("*prod:%llu*\n*%d,%d,%d*\n", c, (int)&a, (int)&b, (int)&c); // printing addresses prevents optimizations + if (truthy()) printf("*%d,%d,%d*\n", (int)&a, (int)&b, (int)&c); // printing addresses prevents optimizations + printf("*prod:%llu*\n", c); } // Basic (rounded, for now) math. Just check compilation. @@ -745,6 +746,14 @@ if 'benchmark' not in str(sys.argv) and 'sanity' not in str(sys.argv) and 'brows b++; if (truthy()) b--; // confuse optimizer printf("*%Ld,%Ld,%Ld,%Ld*\n", (a+b)/5000, (a-b)/5000, (a*3)/5000, (a/5)/5000); + a -= 17; if (truthy()) a += 5; // confuse optimizer + b -= 17; if (truthy()) b += 121; // confuse optimizer + printf("*%Lx,%Lx,%Lx,%Lx*\n", b - a, b - a/2, b/2 - a, b - 20); + + if (truthy()) a += 5/b; // confuse optimizer + if (truthy()) b += 121*(3+a/b); // confuse optimizer + printf("*%Lx,%Lx,%Lx,%Lx*\n", a - b, a - b/2, a/2 - b, a - 20); + return 0; } ''' @@ -764,7 +773,10 @@ if 'benchmark' not in str(sys.argv) and 'sanity' not in str(sys.argv) and 'brows '*-1,34359738367,4294967295,1073741823*\n' + '*-1,-1,-1,-1*\n' + '*-1,34359738367,4294967295,1073741823*\n' + - '*prod:34*') + '*prod:34*\n' + + '*524718382041609,49025451137,787151111239120,52476740749274*\n' + + '*ffff210edd000002,91990876ea283be,f6e5210edcdd7c45,1234000000450765*\n' + + '*def122fffffe,91adef1232283bb,f6e66f78915d7c42,1234def123450763*\n') src = r''' #include <stdio.h> @@ -2298,8 +2310,6 @@ cat |umber one top notchfi FI FO FUM WHEN WHERE WHY HOW WHO|''', ['wowie', 'too' self.do_run(src, 'Assertion failed: 1 == false') def test_longjmp(self): - if Settings.ASM_JS: return self.skip('asm does not support longjmp') - src = r''' #include <stdio.h> #include <setjmp.h> @@ -2331,8 +2341,6 @@ cat |umber one top notchfi FI FO FUM WHEN WHERE WHY HOW WHO|''', ['wowie', 'too' self.do_run(src, 'second\nmain: 1\n') def test_longjmp2(self): - if Settings.ASM_JS: return self.skip('asm does not support longjmp') - src = r''' #include <setjmp.h> #include <stdio.h> @@ -2379,8 +2387,6 @@ Exiting stack_manipulate_func, level: 0 ''') def test_longjmp3(self): - if Settings.ASM_JS: return self.skip('asm does not support longjmp') - src = r''' #include <setjmp.h> #include <stdio.h> @@ -2433,8 +2439,6 @@ Exiting setjmp function, level: 0 ''') def test_longjmp4(self): - if Settings.ASM_JS: return self.skip('asm does not support longjmp') - src = r''' #include <setjmp.h> #include <stdio.h> @@ -3529,7 +3533,7 @@ def process(filename): double get() { double ret = 0; - __asm __volatile__("12/3.3":"=a"(ret)); + __asm __volatile__("12/3.3":"=r"(ret)); return ret; } @@ -3697,6 +3701,7 @@ def process(filename): def test_bigswitch(self): if Settings.RELOOP: return self.skip('TODO: switch in relooper, issue #781') + if Settings.ASM_JS: return self.skip('TODO: switch too large for asm') src = open(path_from_root('tests', 'bigswitch.cpp')).read() self.do_run(src, '''34962: GL_ARRAY_BUFFER (0x8892) @@ -7389,13 +7394,15 @@ void*:16 extra_emscripten_args=['-H', 'libc/fcntl.h,libc/sys/unistd.h,poll.h,libc/math.h,libc/langinfo.h,libc/time.h']) def get_freetype(self): + Settings.DEAD_FUNCTIONS += ['_inflateEnd', '_inflate', '_inflateReset', '_inflateInit2_'] + return self.get_library('freetype', os.path.join('objs', '.libs', 'libfreetype.a')) def test_freetype(self): if self.emcc_args is None: return self.skip('requires emcc') if Settings.QUANTUM_SIZE == 1: return self.skip('TODO: Figure out and try to fix') - if Settings.ASM_JS: return self.skip('asm does not support longjmp') + if Settings.ASM_JS and '-O2' not in self.emcc_args: return self.skip('mozilla bug 863867') if Settings.CORRECT_SIGNS == 0: Settings.CORRECT_SIGNS = 1 # Not sure why, but needed @@ -8065,19 +8072,21 @@ def process(filename): self.do_run(src, output) shutil.move(self.in_dir('src.cpp.o.js'), self.in_dir('normal.js')) - self.emcc_args = map(lambda x: 'ASM_JS=0' if x == 'ASM_JS=1' else x, self.emcc_args) + self.emcc_args.append('-s') + self.emcc_args.append('ASM_JS=0') Settings.PGO = 1 self.do_run(src, output) Settings.PGO = 0 - self.emcc_args = map(lambda x: 'ASM_JS=1' if x == 'ASM_JS=0' else x, self.emcc_args) + self.emcc_args.append('-s') + self.emcc_args.append('ASM_JS=1') shutil.move(self.in_dir('src.cpp.o.js'), self.in_dir('pgo.js')) pgo_output = run_js(self.in_dir('pgo.js')).split('\n')[1] - open('pgo_data', 'w').write(pgo_output) + open('pgo_data.rsp', 'w').write(pgo_output) # with response file - self.emcc_args += ['@pgo_data'] + self.emcc_args += ['@pgo_data.rsp'] self.do_run(src, output) self.emcc_args.pop() shutil.move(self.in_dir('src.cpp.o.js'), self.in_dir('pgoed.js')) @@ -8983,14 +8992,15 @@ TT = %s exec('default = make_run("default", compiler=CLANG, emcc_args=[])') # Make one run with -O1, with safe heap - exec('o1 = make_run("o1", compiler=CLANG, emcc_args=["-O1", "-s", "SAFE_HEAP=1"])') + exec('o1 = make_run("o1", compiler=CLANG, emcc_args=["-O1", "-s", "ASM_JS=0", "-s", "SAFE_HEAP=1"])') # Make one run with -O2, but without closure (we enable closure in specific tests, otherwise on everything it is too slow) - exec('o2 = make_run("o2", compiler=CLANG, emcc_args=["-O2", "-s", "JS_CHUNK_SIZE=1024"])') + exec('o2 = make_run("o2", compiler=CLANG, emcc_args=["-O2", "-s", "ASM_JS=0", "-s", "JS_CHUNK_SIZE=1024"])') # asm.js - exec('asm2 = make_run("asm2", compiler=CLANG, emcc_args=["-O2", "-s", "ASM_JS=1"])') - exec('asm2g = make_run("asm2g", compiler=CLANG, emcc_args=["-O2", "-s", "ASM_JS=1", "-g", "-s", "ASSERTIONS=1", "--memory-init-file", "0"])') + exec('asm1 = make_run("asm1", compiler=CLANG, emcc_args=["-O1"])') + exec('asm2 = make_run("asm2", compiler=CLANG, emcc_args=["-O2"])') + exec('asm2g = make_run("asm2g", compiler=CLANG, emcc_args=["-O2", "-g", "-s", "ASSERTIONS=1", "--memory-init-file", "1"])') # Make custom runs with various options for compiler, quantum, embetter, typed_arrays, llvm_opts in [ @@ -9127,19 +9137,19 @@ Options that are modified or new in %s include: (['-o', 'something.js'], 0, None, 0, 1), (['-o', 'something.js', '-O0'], 0, None, 0, 0), (['-o', 'something.js', '-O1'], 1, None, 0, 0), - (['-o', 'something.js', '-O1', '--closure', '1'], 1, None, 1, 0), + (['-o', 'something.js', '-O1', '-g'], 1, None, 0, 0), + (['-o', 'something.js', '-O1', '--closure', '1'], 1, None, 0, 0), # no closure when asm.js is on + (['-o', 'something.js', '-O1', '--closure', '1', '-s', 'ASM_JS=0'], 1, None, 1, 0), (['-o', 'something.js', '-O2'], 2, None, 0, 1), - (['-o', 'something.js', '-O2', '--closure', '0'], 2, None, 0, 0), (['-o', 'something.js', '-O2', '-g'], 2, None, 0, 0), (['-o', 'something.js', '-Os'], 2, None, 0, 1), - (['-o', 'something.js', '-O3'], 3, None, 1, 1), - (['-o', 'something.js', '-O3', '--closure', '0'], 3, None, 0, 0), + (['-o', 'something.js', '-O3', '-s', 'ASM_JS=0'], 3, None, 1, 1), # and, test compiling to bitcode first (['-o', 'something.bc'], 0, [], 0, 0), (['-o', 'something.bc'], 0, ['-O0'], 0, 0), (['-o', 'something.bc'], 1, ['-O1'], 0, 0), (['-o', 'something.bc'], 2, ['-O2'], 0, 0), - (['-o', 'something.bc'], 3, ['-O3'], 1, 0), + (['-o', 'something.bc'], 3, ['-O3', '-s', 'ASM_JS=0'], 1, 0), (['-O1', '-o', 'something.bc'], 0, [], 0, 0), # -Ox is ignored and warned about ]: print params, opt_level, bc_params, closure, has_malloc @@ -9170,23 +9180,23 @@ Options that are modified or new in %s include: # closure has not been run, we can do some additional checks. TODO: figure out how to do these even with closure assert 'Module._main = ' not in generated, 'closure compiler should not have been run' if keep_debug: - assert ('(label)' in generated) == (opt_level <= 1), 'relooping should be in opt >= 2' + assert ('(label)' in generated or '(label | 0)' in generated) == (opt_level <= 1), 'relooping should be in opt >= 2' assert ('assert(STACKTOP < STACK_MAX' in generated) == (opt_level == 0), 'assertions should be in opt == 0' - assert 'var $i;' in generated or 'var $i_0' in generated or 'var $storemerge3;' in generated or 'var $storemerge4;' in generated or 'var $i_04;' in generated, 'micro opts should always be on' - if opt_level >= 2: + assert 'var $i;' in generated or 'var $i_0' in generated or 'var $storemerge3;' in generated or 'var $storemerge4;' in generated or 'var $i_04;' in generated or 'var $original = 0' in generated, 'micro opts should always be on' + if opt_level >= 2 and '-g' in params: assert re.search('HEAP8\[\$?\w+ ?\+ ?\(+\$?\w+ ?', generated) or re.search('HEAP8\[HEAP32\[', generated), 'eliminator should create compound expressions, and fewer one-time vars' # also in -O1, but easier to test in -O2 assert ('_puts(' in generated) == (opt_level >= 1), 'with opt >= 1, llvm opts are run and they should optimize printf to puts' if opt_level <= 1 or '-g' in params: assert 'function _main() {' in generated, 'Should be unminified, including whitespace' - elif opt_level >= 2: assert 'function _main(){' in generated, 'Should be whitespace-minified' + elif opt_level >= 2: assert ('function _main(){' in generated or '"use asm";var a=' in generated), 'Should be whitespace-minified' # emcc -s RELOOP=1 src.cpp ==> should pass -s to emscripten.py. --typed-arrays is a convenient alias for -s USE_TYPED_ARRAYS for params, test, text in [ - (['-s', 'ASM_JS=1', '-O2'], lambda generated: 'var b=0' in generated and not 'function _main' in generated, 'registerize/minify is run by default in -O2'), - (['-s', 'ASM_JS=1', '-O2', '--minify', '0'], lambda generated: 'var b = 0' in generated and not 'function _main' in generated, 'minify is cancelled, but not registerize'), - (['-s', 'ASM_JS=1', '-O2', '-g'], lambda generated: 'var b=0' not in generated and 'var b = 0' not in generated and 'function _main' in generated, 'registerize/minify is cancelled by -g'), + (['-O2'], lambda generated: 'var b=0' in generated and not 'function _main' in generated, 'registerize/minify is run by default in -O2'), + (['-O2', '--minify', '0'], lambda generated: 'var b = 0' in generated and not 'function _main' in generated, 'minify is cancelled, but not registerize'), + (['-O2', '-g'], lambda generated: 'var b=0' not in generated and 'var b = 0' not in generated and 'function _main' in generated, 'registerize/minify is cancelled by -g'), (['-s', 'INLINING_LIMIT=0'], lambda generated: 'function _dump' in generated, 'no inlining without opts'), (['-O3', '-s', 'INLINING_LIMIT=0', '--closure', '0'], lambda generated: 'function _dump' not in generated, 'lto/inlining'), - (['-Os', '--llvm-lto', '1'], lambda generated: 'function _dump' in generated, '-Os disables inlining'), + (['-Os', '--llvm-lto', '1', '-s', 'ASM_JS=0'], lambda generated: 'function _dump' in generated, '-Os disables inlining'), (['-s', 'USE_TYPED_ARRAYS=0'], lambda generated: 'new Int32Array' not in generated, 'disable typed arrays'), (['-s', 'USE_TYPED_ARRAYS=1'], lambda generated: 'IHEAPU = ' in generated, 'typed arrays 1 selected'), ([], lambda generated: 'Module["_dump"]' not in generated, 'dump is not exported by default'), @@ -9196,6 +9206,7 @@ Options that are modified or new in %s include: (['--typed-arrays', '2'], lambda generated: 'new Uint16Array' in generated and 'new Uint32Array' in generated, 'typed arrays 2 selected'), (['--llvm-opts', '1'], lambda generated: '_puts(' in generated, 'llvm opts requested'), ]: + print params, text self.clear() output = Popen([PYTHON, compiler, path_from_root('tests', 'hello_world_loop.cpp'), '-o', 'a.out.js'] + params, stdout=PIPE, stderr=PIPE).communicate() assert len(output[0]) == 0, output[0] @@ -10229,64 +10240,17 @@ f.close() for args, fail in [ ([], True), # without --bind, we fail (['--bind'], False), - (['--bind', '-O1'], False) - # XXX TODO (['--bind', '-O2'], False) + (['--bind', '-O1'], False), + (['--bind', '-O2'], False) ]: print args, fail self.clear() try_delete(self.in_dir('a.out.js')) - Popen([PYTHON, EMCC, path_from_root('tests', 'embind', 'embind_test.cpp'), '--post-js', path_from_root('tests', 'embind', 'embind_test.js')] + args, stderr=PIPE if fail else None).communicate() + Popen([PYTHON, EMCC, path_from_root('tests', 'embind', 'embind_test.cpp'), '--post-js', path_from_root('tests', 'embind', 'underscore-1.4.2.js'), '--post-js', path_from_root('tests', 'embind', 'imvu_test_adapter.js'), '--post-js', path_from_root('tests', 'embind', 'embind.test.js')] + args, stderr=PIPE if fail else None).communicate() assert os.path.exists(self.in_dir('a.out.js')) == (not fail) if not fail: - output = run_js(self.in_dir('a.out.js')) - self.assertContained('''fixture: embind ---test: test value creation ---test: test passthrough ---test: test void return converts to undefined ---test: test booleans can be marshalled ---test: test convert double to unsigned ---test: test get length of array ---test: test add a bunch of things ---test: test sum array ---test: test strings ---test: test no memory leak when passing strings in by const reference -fixture: classes ---test: test class instance ---test: test class methods ---test: test can't call methods on deleted class instances ---test: test isinstance ---test: test can return class instances by value ---test: test can pass class instances to functions by reference ---test: test can access struct fields ---test: test can set struct fields ---test: test assignment returns value ---test: test assigning string to integer raises TypeError ---test: test can return tuples by value ---test: test tuples can contain tuples ---test: test can pass tuples by value ---test: test can return structs by value ---test: test can pass structs by value ---test: test can pass and return tuples in structs ---test: test can clone handles ---test: test can't clone if already deleted ---test: test moving handles is a clone+delete ---test: test StringHolder -fixture: embind enumerations ---test: test can compare enumeration values ---test: test repr includes enum value ---test: test instanceof ---test: test can pass and return enumeration values to functions -fixture: C++11 enum class ---test: test can compare enumeration values ---test: test repr includes enum value ---test: test instanceof ---test: test can pass and return enumeration values to functions -fixture: emval call tests ---test: test can call functions from C++ -fixture: interfaces ---test: test can wrap JS object in native interface ---test: test can pass arguments and return complicated values ---test: test can call interface methods that return nothing''', output) + output = run_js(self.in_dir('a.out.js'), stdout=PIPE, stderr=PIPE, full_output=True) + assert "FAIL" not in output, output def test_llvm_nativizer(self): try: @@ -11019,12 +10983,13 @@ elif 'browser' in str(sys.argv): self.run_browser('page.html', '', '/report_result?1') def test_sdl_image(self): - # load an image file, get pixel data. Also O2 coverage for --preload-file + # load an image file, get pixel data. Also O2 coverage for --preload-file, and memory-init shutil.copyfile(path_from_root('tests', 'screenshot.jpg'), os.path.join(self.get_dir(), 'screenshot.jpg')) open(os.path.join(self.get_dir(), 'sdl_image.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_image.c')).read())) - Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'sdl_image.c'), '-O2', '--preload-file', 'screenshot.jpg', '-o', 'page.html']).communicate() - self.run_browser('page.html', '', '/report_result?600') + for mem in [0, 1]: + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'sdl_image.c'), '-O2', '--preload-file', 'screenshot.jpg', '-o', 'page.html', '--memory-init-file', str(mem)]).communicate() + self.run_browser('page.html', '', '/report_result?600') def test_sdl_image_jpeg(self): shutil.copyfile(path_from_root('tests', 'screenshot.jpg'), os.path.join(self.get_dir(), 'screenshot.jpeg')) @@ -11619,6 +11584,9 @@ elif 'browser' in str(sys.argv): def test_gl_renderers(self): self.btest('gl_renderers.c', reference='gl_renderers.png', args=['-s', 'GL_UNSAFE_OPTS=0']) + def test_gl_stride(self): + self.btest('gl_stride.c', reference='gl_stride.png', args=['-s', 'GL_UNSAFE_OPTS=0']) + def test_matrix_identity(self): self.btest('gl_matrix_identity.c', expected=['-1882984448', '460451840']) @@ -11765,10 +11733,10 @@ elif 'browser' in str(sys.argv): main, supp = self.setup_runtimelink_test() open(self.in_dir('supp.cpp'), 'w').write(supp) - Popen([PYTHON, EMCC, self.in_dir('supp.cpp'), '-o', 'supp.js', '-s', 'LINKABLE=1', '-s', 'NAMED_GLOBALS=1', '-s', 'BUILD_AS_SHARED_LIB=2', '-O2']).communicate() + Popen([PYTHON, EMCC, self.in_dir('supp.cpp'), '-o', 'supp.js', '-s', 'LINKABLE=1', '-s', 'NAMED_GLOBALS=1', '-s', 'BUILD_AS_SHARED_LIB=2', '-O2', '-s', 'ASM_JS=0']).communicate() shutil.move(self.in_dir('supp.js'), self.in_dir('supp.so')) - self.btest(main, args=['-s', 'LINKABLE=1', '-s', 'NAMED_GLOBALS=1', '-s', 'RUNTIME_LINKED_LIBS=["supp.so"]', '-DBROWSER=1', '-O2'], expected='76') + self.btest(main, args=['-s', 'LINKABLE=1', '-s', 'NAMED_GLOBALS=1', '-s', 'RUNTIME_LINKED_LIBS=["supp.so"]', '-DBROWSER=1', '-O2', '-s', 'ASM_JS=0'], expected='76') def test_pre_run_deps(self): # Adding a dependency in preRun will delay run @@ -12130,13 +12098,12 @@ elif 'benchmark' in str(sys.argv): try_delete(final_filename) output = Popen([PYTHON, EMCC, filename, #'-O3', '-O2', '-s', 'INLINING_LIMIT=0', '-s', 'DOUBLE_MODE=0', '-s', 'PRECISE_I64_MATH=0',# '-s', 'EXPLICIT_ZEXT=1', - '-s', 'ASM_JS=1', '-s', 'USE_MATH_IMUL=1', '--llvm-lto', '1', + '-s', 'ASM_JS=1', '-s', 'USE_MATH_IMUL=1', '--llvm-lto', '1', '--memory-init-file', '0', '-s', 'TOTAL_MEMORY=128*1024*1024', '-s', 'FAST_MEMORY=10*1024*1024', '-o', final_filename] + shared_args + emcc_args, stdout=PIPE, stderr=self.stderr_redirect).communicate() assert os.path.exists(final_filename), 'Failed to compile file: ' + output[0] - if self.save_JS: - self.hardcode_arguments(final_filename, args) + self.hardcode_arguments(final_filename, args) # Run JS global total_times, tests_done @@ -12558,13 +12525,13 @@ elif 'sanity' in str(sys.argv): f = open(CONFIG_FILE, 'a') f.write('CLOSURE_COMPILER = "/tmp/nowhere/nothingtoseehere/kjadsfkjwelkjsdfkqgas/nonexistent.txt"\n') f.close() - output = self.check_working([EMCC, '-O2', '--closure', '1', 'tests/hello_world.cpp'], CLOSURE_FATAL) + output = self.check_working([EMCC, '-O2', '-s', 'ASM_JS=0', '--closure', '1', 'tests/hello_world.cpp'], CLOSURE_FATAL) # With a working path, all is well restore() try_delete('a.out.js') - output = self.check_working([EMCC, '-O2', '--closure', '1', 'tests/hello_world.cpp'], '') - assert os.path.exists('a.out.js') + output = self.check_working([EMCC, '-O2', '-s', 'ASM_JS=0', '--closure', '1', 'tests/hello_world.cpp'], '') + assert os.path.exists('a.out.js'), output def test_llvm(self): LLVM_WARNING = 'warning: LLVM version appears incorrect' @@ -12823,7 +12790,7 @@ fi print >> sys.stderr, phase, i opt = min(i, 2) try_delete('a.out.js') - output = Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world_loop.cpp'), '-O' + str(opt)], + output = Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world_loop.cpp'), '-O' + str(opt), '-g'], stdout=PIPE, stderr=PIPE).communicate() self.assertContained('hello, world!', run_js('a.out.js')) output = '\n'.join(output) @@ -12875,7 +12842,7 @@ fi (['--jcache'], 'hello_malloc.cpp', False, True, False, True, False, True, []), ([], 'hello_malloc.cpp', False, False, False, False, False, False, []), # new, huge file - ([], 'hello_libcxx.cpp', False, False, False, False, False, False, ('3 chunks',)), + ([], 'hello_libcxx.cpp', False, False, False, False, False, False, ('4 chunks',)), (['--jcache'], 'hello_libcxx.cpp', True, False, True, False, True, False, []), (['--jcache'], 'hello_libcxx.cpp', False, True, False, True, False, True, []), ([], 'hello_libcxx.cpp', False, False, False, False, False, False, []), diff --git a/tests/unistd/misc.out b/tests/unistd/misc.out index 810da215..a872a258 100644 --- a/tests/unistd/misc.out +++ b/tests/unistd/misc.out @@ -29,7 +29,7 @@ alarm: 0, errno: 0 ualarm: 0, errno: 0 fork: -1, errno: 11 vfork: -1, errno: 11 -crypt: 0, errno: 38 +crypt: (null), errno: 38 encrypt, errno: 38 getgid: 0, errno: 0 getegid: 0, errno: 0 diff --git a/tests/websockets_gethostbyname.c b/tests/websockets_gethostbyname.c index cba2c635..1580d9a7 100644 --- a/tests/websockets_gethostbyname.c +++ b/tests/websockets_gethostbyname.c @@ -44,7 +44,7 @@ unsigned int get_all_buf(int sock, char* output, unsigned int maxsize) int done = 0; -void iter(void *arg) { +void iter() { /* perform read write operations ... */ static char out[1024*2]; static int pos = 0; @@ -124,7 +124,7 @@ int main(void) #if EMSCRIPTEN emscripten_set_main_loop(iter, 0, 0); #else - while (!done) iter(NULL); + while (!done) iter(); #endif return EXIT_SUCCESS; diff --git a/tools/eliminator/asm-eliminator-test-output.js b/tools/eliminator/asm-eliminator-test-output.js index 4cf15c62..e477c320 100644 --- a/tools/eliminator/asm-eliminator-test-output.js +++ b/tools/eliminator/asm-eliminator-test-output.js @@ -108,4 +108,19 @@ function label() { i(); } } +function switchy() { + var no = 0, yes = 0; + while (1) switch (label | 0) { + case x: + no = 100; + break; + case y: + yes = 111; + yes = yes * 2; + print(yes); + yes--; + print(yes / 2); + continue; + } +} diff --git a/tools/eliminator/asm-eliminator-test.js b/tools/eliminator/asm-eliminator-test.js index d2c0507c..acc07edb 100644 --- a/tools/eliminator/asm-eliminator-test.js +++ b/tools/eliminator/asm-eliminator-test.js @@ -141,5 +141,20 @@ function label() { i(); } } +function switchy() { + var no = 0, yes = 0; + while (1) switch (label | 0) { + case x: + no = 100; // eliminatable in theory, but eliminator does not look into switch. must leave def above as well. + break; + case y: + yes = 111; + yes = yes*2; + print(yes); + yes--; + print(yes/2); + continue; + } +} // EMSCRIPTEN_GENERATED_FUNCTIONS: ["asm", "__Z11printResultPiS_j", "_segment_holding", "__ZN5identC2EiPKcPci", "_vec2Length", "exc", "label"] diff --git a/tools/file_packager.py b/tools/file_packager.py index 73ff4919..e434e813 100644 --- a/tools/file_packager.py +++ b/tools/file_packager.py @@ -11,7 +11,7 @@ data downloads. Usage: - file_packager.py TARGET [--preload A [B..]] [--embed C [D..]] [--compress COMPRESSION_DATA] [--pre-run] [--crunch[=X]] + file_packager.py TARGET [--preload A [B..]] [--embed C [D..]] [--compress COMPRESSION_DATA] [--pre-run] [--crunch[=X]] [--js-output=OUTPUT.js] [--no-force] --pre-run Will generate wrapper code that does preloading in Module.preRun. This is necessary if you add this code before the main file has been loading, which includes necessary components like addRunDependency. @@ -24,6 +24,10 @@ Usage: DDS files will not be crunched if the .crn is more recent than the .dds. This prevents a lot of unneeded computation. + --js-output=FILE Writes output in FILE, if not specified, standard output is used. + + --no-force Don't create output if no valid input file is specified. + Notes: * The file packager generates unix-style file paths. So if you are on windows and a file is accessed at @@ -39,6 +43,11 @@ import shared from shared import Compression, execute, suffix, unsuffixed from subprocess import Popen, PIPE, STDOUT +if len(sys.argv) == 1: + print '''Usage: file_packager.py TARGET [--preload A...] [--embed B...] [--compress COMPRESSION_DATA] [--pre-run] [--crunch[=X]] [--js-output=OUTPUT.js] [--no-force] +See the source for more details.''' + sys.exit(0) + data_target = sys.argv[1] IMAGE_SUFFIXES = ('.jpg', '.png', '.bmp') @@ -59,6 +68,8 @@ in_compress = 0 pre_run = False crunch = 0 plugins = [] +jsoutput = None +force = True for arg in sys.argv[1:]: if arg == '--preload': @@ -80,6 +91,10 @@ for arg in sys.argv[1:]: in_preload = False in_embed = False in_compress = 0 + elif arg == '--no-force': + force = False + elif arg.startswith('--js-output'): + jsoutput = arg.split('=')[1] if '=' in arg else None elif arg.startswith('--crunch'): from shared import CRUNCH crunch = arg.split('=')[1] if '=' in arg else '128' @@ -93,9 +108,15 @@ for arg in sys.argv[1:]: in_embed = False in_compress = 0 elif in_preload: - data_files.append({ 'name': arg, 'mode': 'preload' }) + if os.path.isfile(arg) or os.path.isdir(arg): + data_files.append({ 'name': arg, 'mode': 'preload' }) + else: + print >> sys.stderr, 'Warning: ' + arg + ' does not exist, ignoring.' elif in_embed: - data_files.append({ 'name': arg, 'mode': 'embed' }) + if os.path.isfile(arg) or os.path.isdir(arg): + data_files.append({ 'name': arg, 'mode': 'embed' }) + else: + print >> sys.stderr, 'Warning:' + arg + ' does not exist, ignoring.' elif in_compress: if in_compress == 1: Compression.encoder = arg @@ -107,7 +128,10 @@ for arg in sys.argv[1:]: Compression.js_name = arg in_compress = 0 -print ''' +if (not force) and len(data_files) == 0: + has_preloaded = False + +ret = ''' (function() { ''' @@ -152,7 +176,7 @@ for file_ in data_files: # Crunch files if crunch: shutil.copyfile(shared.path_from_root('tools', 'crunch-worker.js'), 'crunch-worker.js') - print ''' + ret += ''' var decrunchWorker = new Worker('crunch-worker.js'); var decrunchCallbacks = []; decrunchWorker.onmessage = function(msg) { @@ -386,26 +410,30 @@ if has_preloaded: ''' % (data_target, os.path.basename(Compression.compressed_name(data_target) if Compression.on else data_target), use_data, data_target) # use basename because from the browser's point of view, we need to find the datafile in the same dir as the html file if pre_run: - print ''' + ret += ''' if (typeof Module == 'undefined') Module = {}; if (!Module['preRun']) Module['preRun'] = []; Module["preRun"].push(function() { ''' - -print code +ret += code if pre_run: - print ' });\n' + ret += ' });\n' if crunch: - print ''' + ret += ''' if (!Module['postRun']) Module['postRun'] = []; Module["postRun"].push(function() { decrunchWorker.terminate(); }); ''' -print ''' +ret += ''' })(); ''' - +if force or len(data_files) > 0: + if jsoutput == None: + print ret + else: + f = open(jsoutput, 'w') + f.write(ret) diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index 5ede0ce8..598287db 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -1765,6 +1765,7 @@ function eliminate(ast, memSafe) { var values = {}; var locals = {}; var varsToRemove = {}; // variables being removed, that we can eliminate all 'var x;' of (this refers to 'var' nodes we should remove) + // 1 means we should remove it, 2 means we successfully removed it var varsToTryToRemove = {}; // variables that have 0 uses, but have side effects - when we scan we can try to remove them // add arguments as locals if (func[2]) { @@ -1822,7 +1823,7 @@ function eliminate(ast, memSafe) { }); } if (!hasSideEffects) { - varsToRemove[name] = 1; // remove it normally + varsToRemove[name] = !definitions[name] ? 2 : 1; // remove it normally sideEffectFree[name] = true; } else { varsToTryToRemove[name] = 1; // try to remove it later during scanning @@ -1979,7 +1980,7 @@ function eliminate(ast, memSafe) { for (var i = 0; i < value.length; i++) { node[i] = value[i]; } - varsToRemove[name] = 1; + varsToRemove[name] = 2; } } else { if (allowTracking) track(name, node[3], node); @@ -2019,7 +2020,7 @@ function eliminate(ast, memSafe) { for (var i = 0; i < value.length; i++) { node[i] = value[i]; } - varsToRemove[name] = 1; + varsToRemove[name] = 2; } } } @@ -2123,7 +2124,7 @@ function eliminate(ast, memSafe) { function doEliminate(name, node) { //printErr('elim!!!!! ' + name); // yes, eliminate! - varsToRemove[name] = 1; // both assign and var definitions can have other vars we must clean up + varsToRemove[name] = 2; // both assign and var definitions can have other vars we must clean up var info = tracked[name]; delete tracked[name]; var defNode = info.defNode; @@ -2181,7 +2182,7 @@ function eliminate(ast, memSafe) { //printErr('cleaning up ' + JSON.stringify(varsToRemove)); traverse(func, function(node, type) { if (type === 'var') { - node[1] = node[1].filter(function(pair) { return !(pair[0] in varsToRemove) }); + node[1] = node[1].filter(function(pair) { return !varsToRemove[pair[0]] }); if (node[1].length == 0) { // wipe out an empty |var;| node[0] = 'toplevel'; @@ -2192,7 +2193,7 @@ function eliminate(ast, memSafe) { if (asm) { for (var v in varsToRemove) { - delete asmData.vars[v]; + if (varsToRemove[v] == 2) delete asmData.vars[v]; } denormalizeAsm(func, asmData); } diff --git a/tools/response_file.py b/tools/response_file.py new file mode 100644 index 00000000..312cda73 --- /dev/null +++ b/tools/response_file.py @@ -0,0 +1,28 @@ +import tempfile, os, sys, shlex + +# Routes the given cmdline param list in args into a new response file and returns the filename to it. +# The returned filename has a suffix '.rsp'. +def create_response_file(args, directory): + (response_fd, response_filename) = tempfile.mkstemp(prefix='emscripten_', suffix='.rsp', dir=directory, text=True) + response_fd = os.fdopen(response_fd, "w") + #print >> sys.stderr, "Creating response file '%s'" % response_filename + args = map(lambda p: p.replace(' ', '').replace('\\', '\\\\').replace('"', '\\"'), args) + response_fd.write(' '.join(args)) + response_fd.close() + return response_filename + +# Reads a response file, and returns the list of cmdline params found in the file. +# The parameter response_filename may start with '@'. +def read_response_file(response_filename): + if response_filename.startswith('@'): + response_filename = response_filename[1:] + + #print >> sys.stderr, "Using response file '%s'" % response_filename + if not os.path.exists(response_filename): + raise Exception("Response file '%s' not found!" % response_filename) + + response_fd = open(response_filename, 'r') + args = response_fd.read() + response_fd.close() + args = shlex.split(args) + return args diff --git a/tools/shared.py b/tools/shared.py index 72f4868e..6b5ad835 100644 --- a/tools/shared.py +++ b/tools/shared.py @@ -2,6 +2,7 @@ import shutil, time, os, sys, json, tempfile, copy, shlex, atexit, subprocess, h from subprocess import Popen, PIPE, STDOUT from tempfile import mkstemp import jsrun, cache, tempfiles +from response_file import create_response_file def listify(x): if type(x) is not list: return [x] @@ -34,9 +35,15 @@ class WindowsPopen: self.stdout_ = PIPE if self.stderr_ == None: self.stderr_ = PIPE - - # Call the process with fixed streams. + + # emscripten.py supports reading args from a response file instead of cmdline. + # Use .rsp to avoid cmdline length limitations on Windows. + if len(args) >= 2 and args[1].endswith("emscripten.py"): + self.response_filename = create_response_file(args[2:], TEMP_DIR) + args = args[0:2] + ['@' + self.response_filename] + try: + # Call the process with fixed streams. self.process = subprocess.Popen(args, bufsize, executable, self.stdin_, self.stdout_, self.stderr_, preexec_fn, close_fds, shell, cwd, env, universal_newlines, startupinfo, creationflags) except Exception, e: print >> sys.stderr, '\nsubprocess.Popen(args=%s) failed! Exception %s\n' % (' '.join(args), str(e)) @@ -67,6 +74,13 @@ class WindowsPopen: def kill(self): return self.process.kill() + + def __del__(self): + try: + # Clean up the temporary response file that was used to spawn this process, so that we don't leave temp files around. + tempfiles.try_delete(self.response_filename) + except: + pass # Mute all exceptions in dtor, particularly if we didn't use a response file, self.response_filename doesn't exist. # Install our replacement Popen handler if we are running on Windows to avoid python spawn process function. if os.name == 'nt': @@ -385,13 +399,10 @@ try: except: COMPILER_OPTS = [] # Force a simple, standard target as much as possible: target 32-bit linux, and disable various flags that hint at other platforms -# -fno-ms-compatibility is passed, since on Windows, Clang enables a 'MS compatibility mode' by default, that disables char16_t and char32_t -# to be MSVC header -compatible. This would cause build errors in libcxx file __config. -# -fno-delayed-template-parsing is needed on Windows due to http://llvm.org/PR15651 -COMPILER_OPTS = COMPILER_OPTS + ['-m32', '-U__i386__', '-U__x86_64__', '-U__i386', '-U__x86_64', '-Ui386', '-Ux86_64', '-U__SSE__', '-U__SSE2__', '-U__MMX__', - '-UX87_DOUBLE_ROUNDING', '-UHAVE_GCC_ASM_FOR_X87', '-DEMSCRIPTEN', '-U__STRICT_ANSI__', '-U__CYGWIN__', - '-D__STDC__', '-Xclang', '-triple=i386-pc-linux-gnu', '-D__IEEE_LITTLE_ENDIAN', '-fno-math-errno', - '-fno-ms-compatibility', '-fno-delayed-template-parsing'] +COMPILER_OPTS = COMPILER_OPTS + ['-m32', '-U__i386__', '-U__i386', '-Ui386', + '-U__SSE__', '-U__SSE_MATH__', '-U__SSE2__', '-U__SSE2_MATH__', '-U__MMX__', + '-DEMSCRIPTEN', '-U__STRICT_ANSI__', + '-target', 'i386-pc-linux-gnu', '-D__IEEE_LITTLE_ENDIAN', '-fno-math-errno'] USE_EMSDK = not os.environ.get('EMMAKEN_NO_SDK') @@ -553,6 +564,7 @@ class Settings: @classmethod def apply_opt_level(self, opt_level, noisy=False): if opt_level >= 1: + Settings.ASM_JS = 1 Settings.ASSERTIONS = 0 Settings.DISABLE_EXCEPTION_CATCHING = 1 Settings.EMIT_GENERATED_FUNCTIONS = 1 @@ -718,13 +730,13 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e basename = os.path.basename(f) cache[cache_name].append((basename, open(f, 'rb').read())) break - except: + except Exception, e: if i > 0: # Due to the ugly hack above our best guess is to output the first run with open_make_err(0) as ferr: for line in ferr: sys.stderr.write(line) - raise Exception('could not build library ' + name) + raise Exception('could not build library ' + name + ' due to exception ' + str(e)) if old_dir: os.chdir(old_dir) return generated_libs @@ -1151,7 +1163,7 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e Building.emcc(os.path.join('relooper', 'Relooper.cpp'), ['-I' + os.path.join('relooper'), '--post-js', os.path.join('relooper', 'emscripten', 'glue.js'), '--memory-init-file', '0', - '-s', 'TOTAL_MEMORY=52428800', + '-s', 'TOTAL_MEMORY=67108864', '-s', 'EXPORTED_FUNCTIONS=["_rl_set_output_buffer","_rl_make_output_buffer","_rl_new_block","_rl_delete_block","_rl_block_add_branch_to","_rl_new_relooper","_rl_delete_relooper","_rl_relooper_add_block","_rl_relooper_calculate","_rl_relooper_render", "_rl_set_asm_js_mode"]', '-s', 'DEFAULT_LIBRARY_FUNCS_TO_INCLUDE=["memcpy", "memset", "malloc", "free", "puts"]', '-s', 'RELOOPER="' + relooper + '"', |