aboutsummaryrefslogtreecommitdiff
path: root/src/embind/embind.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/embind/embind.js')
-rw-r--r--src/embind/embind.js1921
1 files changed, 1489 insertions, 432 deletions
diff --git a/src/embind/embind.js b/src/embind/embind.js
index d40d6ca2..91386c69 100644
--- a/src/embind/embind.js
+++ b/src/embind/embind.js
@@ -1,14 +1,142 @@
/*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) {
+ name = makeLegalFunctionName(name);
/*jshint evil:true*/
return new Function(
"body",
"return function " + name + "() {\n" +
+ " \"use strict\";" +
" return body.apply(this, arguments);\n" +
"};\n"
)(body);
@@ -23,136 +151,332 @@ 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 validateType(type, name) {
- if (!type) {
- throw new BindingError('type "' + name + '" must have a positive integer typeid pointer');
+ 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]);
+ }
}
- if (undefined !== typeRegistry[type]) {
- throw new BindingError('cannot register type "' + name + '" twice');
+
+ 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);
}
}
-function __embind_register_void(voidType, name) {
- name = Pointer_stringify(name);
- validateType(voidType, name);
- typeRegistry[voidType] = {
+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 heap32VectorToArray(count, firstElement) {
+ var array = [];
+ for (var i = 0; i < count; i++) {
+ array.push(HEAP32[(firstElement >> 2) + i]);
+ }
+ 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(rawType, name) {
+ name = readLatin1String(name);
+ registerType(rawType, {
name: name,
- fromWireType: function() {
+ 'fromWireType': function() {
return undefined;
- }
- };
+ },
+ 'toWireType': function(destructors, o) {
+ // TODO: assert if anything else is given?
+ 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) {
- if (typeof value !== "number") {
- throw new TypeError('Cannot convert "' + _embind_repr(value) + '" to ' + name);
+ 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" && typeof value !== "boolean") {
+ 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) {
- if (typeof value !== "number") {
- throw new TypeError('Cannot convert "' + _embind_repr(value) + '" to ' + name);
- }
+ 'fromWireType': function(value) {
return value;
},
- fromWireType: function(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" && typeof value !== "boolean") {
+ throw new TypeError('Cannot convert "' + _embind_repr(value) + '" to ' + this.name);
+ }
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;
- }
- };
-}
-
-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;
+ },
+ 'toWireType': function(destructors, value) {
+ return __emval_register(value);
+ },
+ destructorFunction: null, // This type does not need a destructor
+ });
}
-function requireRegisteredType(type, humanName) {
- var impl = typeRegistry[type];
- if (undefined === impl) {
- throw new BindingError(humanName + " has unknown type: " + typeName(type));
- }
- return impl;
-}
+function __embind_register_memory_view(rawType, name) {
+ var typeMapping = [
+ Int8Array,
+ Uint8Array,
+ Int16Array,
+ Uint16Array,
+ Int32Array,
+ Uint32Array,
+ Float32Array,
+ Float64Array,
+ ];
-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);
- }
- return argTypeImpls;
+ 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) {
@@ -163,428 +487,1175 @@ function runDestructors(destructors) {
}
}
-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);
+// 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");
+ }
- 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 rv = returnType.fromWireType(invoker.apply(null, args));
- runDestructors(destructors);
- return rv;
- };
+ /*
+ * 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 __embind_register_tuple(tupleType, name, constructor, destructor) {
- name = Pointer_stringify(name);
- constructor = FUNCTION_TABLE[constructor];
- destructor = FUNCTION_TABLE[destructor];
+// 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;
- var elements = [];
+ if (argCount < 2) {
+ throwBindingError("argTypes array size mismatch! Must at least get return value and 'this' types!");
+ }
+
+ var isClassMethodFunc = (argTypes[1] !== null && classType !== null);
- 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 && !FUNCTION_TABLE[cppTargetFunc]) {
+ throwBindingError('Global function '+humanName+' is not defined!');
+ }
+
+ // 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];
+// }
+
+ 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;
+ }
+ }
+
+ if (needsDestructorStack) {
+ invokerFnBody +=
+ "var destructors = [];\n";
+ }
+
+ 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]];
+
+ 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 for " + reg.name + ": expected=" + elementsLength + ", actual=" + o.length);
+ }
+ 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);
-
- structType.fields[fieldName] = {