summaryrefslogtreecommitdiff
path: root/src/embind
diff options
context:
space:
mode:
Diffstat (limited to 'src/embind')
-rwxr-xr-x[-rw-r--r--]src/embind/embind.js1816
-rwxr-xr-x[-rw-r--r--]src/embind/emval.js195
2 files changed, 1542 insertions, 469 deletions
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;
+}