diff options
-rw-r--r-- | src/analyzer.js | 5 | ||||
-rw-r--r-- | src/embind/embind.js | 99 | ||||
-rw-r--r-- | src/embind/emval.js | 177 | ||||
-rw-r--r-- | src/library.js | 6 | ||||
-rw-r--r-- | src/library_browser.js | 12 | ||||
-rw-r--r-- | src/parseTools.js | 15 | ||||
-rw-r--r-- | src/postamble.js | 4 | ||||
-rw-r--r-- | src/preamble.js | 4 | ||||
-rw-r--r-- | system/include/emscripten/bind.h | 62 | ||||
-rw-r--r-- | system/include/emscripten/val.h | 116 | ||||
-rw-r--r-- | system/include/emscripten/wire.h | 81 | ||||
-rw-r--r-- | system/lib/embind/bind.cpp | 1 | ||||
-rw-r--r-- | tests/embind/build_benchmark | 2 | ||||
-rw-r--r-- | tests/embind/embind.benchmark.js | 35 | ||||
-rw-r--r-- | tests/embind/embind.test.js | 153 | ||||
-rw-r--r-- | tests/embind/embind_benchmark.cpp | 71 | ||||
-rw-r--r-- | tests/embind/embind_test.cpp | 65 | ||||
-rw-r--r-- | tests/float_tex.cpp | 4 | ||||
-rw-r--r-- | tests/gl_subdata.cpp | 4 | ||||
-rw-r--r-- | tests/lua/binarytrees.lua | 50 | ||||
-rw-r--r-- | tests/lua/scimark.lua | 424 | ||||
-rw-r--r-- | tests/lua/src/Makefile | 2 | ||||
-rwxr-xr-x | tests/runner.py | 185 |
23 files changed, 1311 insertions, 266 deletions
diff --git a/src/analyzer.js b/src/analyzer.js index a131406c..2cc46ab6 100644 --- a/src/analyzer.js +++ b/src/analyzer.js @@ -411,8 +411,9 @@ function analyzer(data, sidePass) { // legalize parameters legalizeFunctionParameters(value.params); // legalize return value, if any - if (value.assignTo && isIllegalType(item.type)) { - bits = getBits(value.type); + var returnType = getReturnType(item.type); + if (value.assignTo && isIllegalType(returnType)) { + bits = getBits(returnType); var elements = getLegalVars(item.assignTo, bits); // legalize return value value.assignTo = elements[0].ident; diff --git a/src/embind/embind.js b/src/embind/embind.js index cadee700..91386c69 100644 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -131,6 +131,7 @@ function extendError(baseErrorType, errorName) { // from https://github.com/imvu/imvujs/blob/master/src/function.js function createNamedFunction(name, body) { + name = makeLegalFunctionName(name); /*jshint evil:true*/ return new Function( "body", @@ -270,6 +271,10 @@ function __embind_register_void(rawType, name) { 'fromWireType': function() { return undefined; }, + 'toWireType': function(destructors, o) { + // TODO: assert if anything else is given? + return undefined; + }, }); } @@ -306,7 +311,7 @@ function __embind_register_integer(primitiveType, name, minRange, maxRange) { '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") { + if (typeof value !== "number" && typeof value !== "boolean") { throw new TypeError('Cannot convert "' + _embind_repr(value) + '" to ' + this.name); } if (value < minRange || value > maxRange) { @@ -328,8 +333,8 @@ function __embind_register_float(rawType, name) { '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 ' +this.name); + if (typeof value !== "number" && typeof value !== "boolean") { + throw new TypeError('Cannot convert "' + _embind_repr(value) + '" to ' + this.name); } return value; }, @@ -449,6 +454,31 @@ function __embind_register_emval(rawType, name) { }); } +function __embind_register_memory_view(rawType, name) { + var typeMapping = [ + Int8Array, + Uint8Array, + Int16Array, + Uint16Array, + Int32Array, + Uint32Array, + Float32Array, + Float64Array, + ]; + + name = readLatin1String(name); + registerType(rawType, { + name: name, + 'fromWireType': function(handle) { + var type = HEAPU32[handle >> 2]; + var size = HEAPU32[(handle >> 2) + 1]; // in elements + var data = HEAPU32[(handle >> 2) + 2]; // byte offset into emscripten heap + var TA = typeMapping[type]; + return new TA(HEAP8.buffer, data, size); + }, + }); +} + function runDestructors(destructors) { while (destructors.length) { var ptr = destructors.pop(); @@ -677,7 +707,7 @@ function __embind_finalize_tuple(rawTupleType) { }, 'toWireType': function(destructors, o) { if (elementsLength !== o.length) { - throw new TypeError("Incorrect number of tuple elements"); + throw new TypeError("Incorrect number of tuple elements for " + reg.name + ": expected=" + elementsLength + ", actual=" + o.length); } var ptr = rawConstructor(); for (var i = 0; i < elementsLength; ++i) { @@ -685,7 +715,7 @@ function __embind_finalize_tuple(rawTupleType) { } if (destructors !== null) { destructors.push(rawDestructor, ptr); - } + } return ptr; }, destructorFunction: rawDestructor, @@ -802,7 +832,9 @@ var genericPointerToWireType = function(destructors, handle) { if (this.isSmartPointer) { var ptr = this.rawConstructor(); - destructors.push(this.rawDestructor, ptr); + if (destructors !== null) { + destructors.push(this.rawDestructor, ptr); + } return ptr; } else { return 0; @@ -854,7 +886,9 @@ var genericPointerToWireType = function(destructors, handle) { clonedHandle.delete(); }) ); - destructors.push(this.rawDestructor, ptr); + if (destructors !== null) { + destructors.push(this.rawDestructor, ptr); + } } break; @@ -1080,9 +1114,13 @@ ClassHandle.prototype.isAliasOf = function(other) { return leftClass === rightClass && left === right; }; +function throwInstanceAlreadyDeleted(obj) { + throwBindingError(getInstanceTypeName(obj) + ' instance already deleted'); +} + ClassHandle.prototype.clone = function() { if (!this.$$.ptr) { - throwBindingError(getInstanceTypeName(this) + ' instance already deleted'); + throwInstanceAlreadyDeleted(this); } var clone = Object.create(Object.getPrototypeOf(this), { @@ -1104,9 +1142,12 @@ function runDestructor(handle) { } } -ClassHandle.prototype['delete'] = function() { +ClassHandle.prototype['delete'] = function ClassHandle_delete() { if (!this.$$.ptr) { - throwBindingError(getInstanceTypeName(this) + ' instance already deleted'); + throwInstanceAlreadyDeleted(this); + } + if (this.$$.deleteScheduled) { + throwBindingError('Object already scheduled for deletion'); } this.$$.count.value -= 1; @@ -1116,6 +1157,44 @@ ClassHandle.prototype['delete'] = function() { this.$$.smartPtr = undefined; this.$$.ptr = undefined; }; + +var deletionQueue = []; + +ClassHandle.prototype['isDeleted'] = function isDeleted() { + return !this.$$.ptr; +}; + +ClassHandle.prototype['deleteLater'] = function deleteLater() { + if (!this.$$.ptr) { + throwInstanceAlreadyDeleted(this); + } + if (this.$$.deleteScheduled) { + throwBindingError('Object already scheduled for deletion'); + } + deletionQueue.push(this); + if (deletionQueue.length === 1 && delayFunction) { + delayFunction(flushPendingDeletes); + } + this.$$.deleteScheduled = true; + return this; +}; + +function flushPendingDeletes() { + while (deletionQueue.length) { + var obj = deletionQueue.pop(); + obj.$$.deleteScheduled = false; + obj['delete'](); + } +} +Module['flushPendingDeletes'] = flushPendingDeletes; + +var delayFunction; +Module['setDelayFunction'] = function setDelayFunction(fn) { + delayFunction = fn; + if (deletionQueue.length && delayFunction) { + delayFunction(flushPendingDeletes); + } +}; function RegisteredClass( name, diff --git a/src/embind/emval.js b/src/embind/emval.js index c02ffa92..77270597 100644 --- a/src/embind/emval.js +++ b/src/embind/emval.js @@ -1,8 +1,12 @@ -/*global Module*/ +/*global Module:true, Runtime*/ /*global HEAP32*/ +/*global new_*/ +/*global createNamedFunction*/ /*global readLatin1String, writeStringToMemory*/ /*global requireRegisteredType, throwBindingError*/ +var Module = Module || {}; + var _emval_handle_array = [{}]; // reserve zero var _emval_free_list = []; @@ -69,14 +73,8 @@ function __emval_incref(handle) { function __emval_decref(handle) { if (handle && 0 === --_emval_handle_array[handle].refcount) { - delete _emval_handle_array[handle]; + _emval_handle_array[handle] = undefined; _emval_free_list.push(handle); - - var actual_length = _emval_handle_array.length; - while (actual_length > 0 && _emval_handle_array[actual_length - 1] === undefined) { - --actual_length; - } - _emval_handle_array.length = actual_length; } } @@ -108,44 +106,73 @@ function __emval_take_value(type, v) { var __newers = {}; // arity -> function -function __emval_new(handle, argCount, argTypes) { - requireHandle(handle); - var args = parseParameters( - argCount, - argTypes, - Array.prototype.slice.call(arguments, 3)); +function craftEmvalAllocator(argCount) { + /*This function returns a new function that looks like this: + function emval_allocator_3(handle, argTypes, arg0Wired, arg1Wired, arg2Wired) { + var argType0 = requireRegisteredType(HEAP32[(argTypes >> 2)], "parameter 0"); + var arg0 = argType0.fromWireType(arg0Wired); + var argType1 = requireRegisteredType(HEAP32[(argTypes >> 2) + 1], "parameter 1"); + var arg1 = argType1.fromWireType(arg1Wired); + var argType2 = requireRegisteredType(HEAP32[(argTypes >> 2) + 2], "parameter 2"); + var arg2 = argType2.fromWireType(arg2Wired); + var constructor = _emval_handle_array[handle].value; + var emval = new constructor(arg0, arg1, arg2); + return emval; + } */ + + var args1 = ["requireRegisteredType", "HEAP32", "_emval_handle_array", "__emval_register"]; + var args2 = [requireRegisteredType, HEAP32, _emval_handle_array, __emval_register]; + + var argsList = ""; + var argsListWired = ""; + for(var i = 0; i < argCount; ++i) { + argsList += (i!==0?", ":"")+"arg"+i; // 'arg0, arg1, ..., argn' + argsListWired += ", arg"+i+"Wired"; // ', arg0Wired, arg1Wired, ..., argnWired' + } - // 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 invokerFnBody = + "return function emval_allocator_"+argCount+"(handle, argTypes " + argsListWired + ") {\n"; + for(var i = 0; i < argCount; ++i) { + invokerFnBody += + "var argType"+i+" = requireRegisteredType(HEAP32[(argTypes >> 2) + "+i+"], \"parameter "+i+"\");\n" + + "var arg"+i+" = argType"+i+".fromWireType(arg"+i+"Wired);\n"; + } + invokerFnBody += + "var constructor = _emval_handle_array[handle].value;\n" + + "var obj = new constructor("+argsList+");\n" + + "return __emval_register(obj);\n" + + "}\n"; + + args1.push(invokerFnBody); + var invokerFunction = new_(Function, args1).apply(null, args2); + return invokerFunction; +} + +function __emval_new(handle, argCount, argTypes) { + requireHandle(handle); + 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(',') + ");"); + newer = craftEmvalAllocator(argCount); + __newers[argCount] = newer; } - - 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; + + if (argCount === 0) { + return newer(handle, argTypes); + } else if (argCount === 1) { + return newer(handle, argTypes, arguments[3]); + } else if (argCount === 2) { + return newer(handle, argTypes, arguments[3], arguments[4]); + } else if (argCount === 3) { + return newer(handle, argTypes, arguments[3], arguments[4], arguments[5]); + } else if (argCount === 4) { + return newer(handle, argTypes, arguments[3], arguments[4], arguments[5], arguments[6]); + } else { + // This is a slow path! (.apply and .splice are slow), so a few specializations are present above. + return newer.apply(null, arguments.splice(1)); } -*/ - return __emval_register(obj); } // appease jshint (technically this code uses eval) @@ -192,38 +219,62 @@ function parseParameters(argCount, argTypes, argWireTypes) { function __emval_call(handle, argCount, argTypes) { requireHandle(handle); + var types = lookupTypes(argCount, argTypes); + + var args = new Array(argCount); + for (var i = 0; i < argCount; ++i) { + args[i] = types[i].fromWireType(arguments[3 + i]); + } + 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) { - requireHandle(handle); - name = getStringOrSymbol(name); - - var args = parseParameters( - argCount, - argTypes, - Array.prototype.slice.call(arguments, 4)); - var obj = _emval_handle_array[handle].value; - var rv = obj[name].apply(obj, args); - return __emval_register(rv); +function lookupTypes(argCount, argTypes, argWireTypes) { + var a = new Array(argCount); + for (var i = 0; i < argCount; ++i) { + a[i] = requireRegisteredType( + HEAP32[(argTypes >> 2) + i], + "parameter " + i); + } + return a; } -function __emval_call_void_method(handle, name, argCount, argTypes) { - requireHandle(handle); - name = getStringOrSymbol(name); +function __emval_get_method_caller(argCount, argTypes) { + var types = lookupTypes(argCount, argTypes); + + var retType = types[0]; + var signatureName = retType.name + "_$" + types.slice(1).map(function (t) { return t.name; }).join("_") + "$"; + + var args1 = ["Runtime", "createNamedFunction", "requireHandle", "getStringOrSymbol", "_emval_handle_array", "retType"]; + var args2 = [Runtime, createNamedFunction, requireHandle, getStringOrSymbol, _emval_handle_array, retType]; - var args = parseParameters( - argCount, - argTypes, - Array.prototype.slice.call(arguments, 4)); - var obj = _emval_handle_array[handle].value; - obj[name].apply(obj, args); + var argsList = ""; // 'arg0, arg1, arg2, ... , argN' + var argsListWired = ""; // 'arg0Wired, ..., argNWired' + for (var i = 0; i < argCount - 1; ++i) { + argsList += (i !== 0 ? ", " : "") + "arg" + i; + argsListWired += ", arg" + i + "Wired"; + args1.push("argType" + i); + args2.push(types[1 + i]); + } + + var invokerFnBody = + "return Runtime.addFunction(createNamedFunction('" + signatureName + "', function (handle, name" + argsListWired + ") {\n" + + "requireHandle(handle);\n" + + "name = getStringOrSymbol(name);\n"; + + for (var i = 0; i < argCount - 1; ++i) { + invokerFnBody += "var arg" + i + " = argType" + i + ".fromWireType(arg" + i + "Wired);\n"; + } + invokerFnBody += + "var obj = _emval_handle_array[handle].value;\n" + + "return retType.toWireType(null, obj[name](" + argsList + "));\n" + + "}));\n"; + + args1.push(invokerFnBody); + var invokerFunction = new_(Function, args1).apply(null, args2); + return invokerFunction; } function __emval_has_function(handle, name) { diff --git a/src/library.js b/src/library.js index 344ec8d6..2efb7a6f 100644 --- a/src/library.js +++ b/src/library.js @@ -6794,10 +6794,12 @@ LibraryManager.library = { 26: 'Text file busy', 18: 'Invalid cross-device link' }, + __errno_state: 0, + __setErrNo__deps: ['__errno_state'], + __setErrNo__postset: '___errno_state = Runtime.staticAlloc(4);', __setErrNo: function(value) { // For convenient setting and returning of errno. - if (!___setErrNo.ret) ___setErrNo.ret = allocate([0], 'i32', ALLOC_NORMAL); - {{{ makeSetValue('___setErrNo.ret', '0', 'value', 'i32') }}} + {{{ makeSetValue('___errno_state', '0', 'value', 'i32') }}} return value; }, __errno_location__deps: ['__setErrNo'], diff --git a/src/library_browser.js b/src/library_browser.js index 97233c36..5f9c4d06 100644 --- a/src/library_browser.js +++ b/src/library_browser.js @@ -311,10 +311,10 @@ mergeInto(LibraryManager.library, { lockPointer: undefined, resizeCanvas: undefined, requestFullScreen: function(lockPointer, resizeCanvas) { - this.lockPointer = lockPointer; - this.resizeCanvas = resizeCanvas; - if (typeof this.lockPointer === 'undefined') this.lockPointer = true; - if (typeof this.resizeCanvas === 'undefined') this.resizeCanvas = false; + Browser.lockPointer = lockPointer; + Browser.resizeCanvas = resizeCanvas; + if (typeof Browser.lockPointer === 'undefined') Browser.lockPointer = true; + if (typeof Browser.resizeCanvas === 'undefined') Browser.resizeCanvas = false; var canvas = Module['canvas']; function fullScreenChange() { @@ -335,8 +335,8 @@ mergeInto(LibraryManager.library, { if (Module['onFullScreen']) Module['onFullScreen'](Browser.isFullScreen); } - if (!this.fullScreenHandlersInstalled) { - this.fullScreenHandlersInstalled = true; + if (!Browser.fullScreenHandlersInstalled) { + Browser.fullScreenHandlersInstalled = true; document.addEventListener('fullscreenchange', fullScreenChange, false); document.addEventListener('mozfullscreenchange', fullScreenChange, false); document.addEventListener('webkitfullscreenchange', fullScreenChange, false); diff --git a/src/parseTools.js b/src/parseTools.js index 1a58b4e7..f30883b5 100644 --- a/src/parseTools.js +++ b/src/parseTools.js @@ -286,11 +286,22 @@ function isVarArgsFunctionType(type) { return type.substr(-varArgsSuffix.length) == varArgsSuffix; } +function getNumVars(type) { // how many variables are needed to represent this type + if (type in Runtime.FLOAT_TYPES) return 1; + return Math.max(getNumIntChunks(type), 1); +} + function countNormalArgs(type, out) { out = out || {}; if (!isFunctionType(type, out)) return -1; - if (isVarArgsFunctionType(type)) out.numArgs--; - return out.numArgs; + var ret = 0; + if (out.segments) { + for (var i = 0; i < out.segments.length; i++) { + ret += getNumVars(out.segments[i][0].text); + } + } + if (isVarArgsFunctionType(type)) ret--; + return ret; } function addIdent(token) { diff --git a/src/postamble.js b/src/postamble.js index 12471a19..d0b737f8 100644 --- a/src/postamble.js +++ b/src/postamble.js @@ -1,7 +1,7 @@ // === Auto-generated postamble setup entry stuff === -Module.callMain = function callMain(args) { +Module['callMain'] = function callMain(args) { assert(runDependencies == 0, 'cannot call main when async dependencies remain! (listen on __ATMAIN__)'); assert(!Module['preRun'] || Module['preRun'].length == 0, 'cannot call main when preRun functions remain to be called'); @@ -84,7 +84,7 @@ function run(args) { var ret = 0; calledRun = true; if (Module['_main'] && shouldRunNow) { - ret = Module.callMain(args); + ret = Module['callMain'](args); if (!Module['noExitRuntime']) { exitRuntime(); } diff --git a/src/preamble.js b/src/preamble.js index d10771e3..dbe5e655 100644 --- a/src/preamble.js +++ b/src/preamble.js @@ -777,7 +777,7 @@ Module['writeArrayToMemory'] = writeArrayToMemory; {{{ reSign }}} #if PRECISE_I32_MUL -if (!Math.imul) Math.imul = function(a, b) { +if (!Math['imul']) Math['imul'] = function(a, b) { var ah = a >>> 16; var al = a & 0xffff; var bh = b >>> 16; @@ -785,7 +785,7 @@ if (!Math.imul) Math.imul = function(a, b) { return (al*bl + ((ah*bl + al*bh) << 16))|0; }; #else -Math.imul = function(a, b) { +Math['imul'] = function(a, b) { return (a*b)|0; // fast but imprecise }; #endif diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index 7aa2a55e..cd465e45 100644 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -18,7 +18,6 @@ namespace emscripten { }; namespace internal { - typedef void (*GenericFunction)(); typedef long GenericEnumValue; // Implemented in JavaScript. Don't call these directly. @@ -60,6 +59,10 @@ namespace emscripten { TYPEID emvalType, const char* name); + void _embind_register_memory_view( + TYPEID memoryViewType, + const char* name); + void _embind_register_function( const char* name, unsigned argCount, @@ -414,11 +417,10 @@ namespace emscripten { // TODO: This could do a reinterpret-cast if sizeof(T) === sizeof(void*) template<typename T> - inline void* getContext(const T& t) { + inline T* 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)); + T* p = reinterpret_cast<T*>(malloc(sizeof(T))); + new(p) T(t); return p; } @@ -749,38 +751,19 @@ namespace emscripten { template<typename ReturnType, typename... Args> ReturnType call(const char* name, Args&&... args) const { - return Caller<ReturnType, Args...>::call(wrapped, name, std::forward<Args>(args)...); + return wrapped.call<ReturnType>(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)...); + if (wrapped.has_function(name)) { + return call<ReturnType>(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; }; @@ -844,23 +827,8 @@ namespace emscripten { } }; - 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) { @@ -881,7 +849,6 @@ namespace emscripten { public: class_() = delete; - template<typename = typename std::enable_if<!internal::is_ptr<ClassType>::value>::type> explicit class_(const char* name) { using namespace internal; @@ -927,16 +894,17 @@ namespace emscripten { policies...); } - template<typename... Args, typename... Policies> - class_& constructor(ClassType* (*factory)(Args...), Policies...) { + template<typename... Args, typename ReturnType, typename... Policies> + class_& constructor(ReturnType (*factory)(Args...), Policies...) { using namespace internal; - typename WithPolicies<Policies...>::template ArgTypeList<AllowedRawPointer<ClassType>, Args...> args; + // TODO: allows all raw pointers... policies need a rethink + typename WithPolicies<allow_raw_pointers, Policies...>::template ArgTypeList<ReturnType, Args...> args; _embind_register_class_constructor( TypeID<ClassType>::get(), args.count, args.types, - reinterpret_cast<GenericFunction>(&Invoker<ClassType*, Args...>::invoke), + reinterpret_cast<GenericFunction>(&Invoker<ReturnType, Args...>::invoke), reinterpret_cast<GenericFunction>(factory)); return *this; } diff --git a/system/include/emscripten/val.h b/system/include/emscripten/val.h index edd070e3..b712d164 100644 --- a/system/include/emscripten/val.h +++ b/system/include/emscripten/val.h @@ -10,7 +10,6 @@ namespace emscripten { extern "C" { void _emval_register_symbol(const char*); - typedef struct _EM_SIG* EM_SIG; typedef struct _EM_VAL* EM_VAL; void _emval_incref(EM_VAL value); @@ -39,34 +38,67 @@ namespace emscripten { unsigned argCount, internal::TYPEID argTypes[] /*, ... */); - EM_VAL _emval_call_method( - EM_VAL value, - const char* methodName, - unsigned argCount, - internal::TYPEID argTypes[] - /*, ... */); - void _emval_call_void_method( - EM_VAL value, - const char* methodName, - unsigned argCount, - internal::TYPEID argTypes[] - /*, ...*/); + + // DO NOT call this more than once per signature. It will leak function pointer offsets! + GenericFunction _emval_get_method_caller( + unsigned argCount, // including return value + 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); - } - }; + template<const char* address> + struct symbol_registrar { + symbol_registrar() { + internal::_emval_register_symbol(address); + } + }; + + template<typename ReturnType, typename... Args> + struct Signature { + typedef typename BindingType<ReturnType>::WireType (*MethodCaller)(EM_VAL value, const char* methodName, typename BindingType<Args>::WireType...); + + static MethodCaller get_method_caller() { + static MethodCaller fp = reinterpret_cast<MethodCaller>(init_method_caller()); + return fp; + } + + private: + static GenericFunction init_method_caller() { + WithPolicies<>::ArgTypeList<ReturnType, Args...> args; + return _emval_get_method_caller(args.count, args.types); + } + }; + + template<typename ReturnType, typename... Args> + struct MethodCaller { + static ReturnType call(EM_VAL handle, const char* methodName, Args&&... args) { + auto caller = Signature<ReturnType, Args...>::get_method_caller(); + auto wireType = caller( + handle, + methodName, + toWireType(std::forward<Args>(args))...); + WireDeleter<ReturnType> deleter(wireType); + return BindingType<ReturnType>::fromWireType(wireType); + } + }; + + template<typename... Args> + struct MethodCaller<void, Args...> { + static void call(EM_VAL handle, const char* methodName, Args&&... args) { + auto caller = Signature<void, Args...>::get_method_caller(); + return caller( + handle, + methodName, + toWireType(std::forward<Args>(args))...); + } + }; + } #define EMSCRIPTEN_SYMBOL(name) \ static const char name##_symbol[] = #name; \ - static const symbol_registrar<name##_symbol> name##_registrar + static const ::emscripten::internal::symbol_registrar<name##_symbol> name##_registrar class val { public: @@ -158,7 +190,7 @@ namespace emscripten { } bool hasOwnProperty(const char* key) const { - return val::global("Object")["prototype"]["hasOwnProperty"].call("c |