diff options
author | Alon Zakai <alonzakai@gmail.com> | 2013-04-16 14:03:43 -0700 |
---|---|---|
committer | Alon Zakai <alonzakai@gmail.com> | 2013-04-16 14:03:43 -0700 |
commit | 985765d9a5b2dcca05bc5f8401e3d2423964d0f2 (patch) | |
tree | 994fa25d2d1eee8688e7b334a186081a5b679739 | |
parent | 3cbdfcc318e45219edbe78c21cd25fe23f5c1c52 (diff) | |
parent | 8ae67d7642ebdd77a3c5f0d8fa7115fb372e98ff (diff) |
Merge pull request #1044 from imvu/embind_update
Embind update
-rw-r--r-- | AUTHORS | 13 | ||||
-rwxr-xr-x[-rw-r--r--] | src/embind/embind.js | 1507 | ||||
-rwxr-xr-x[-rw-r--r--] | src/embind/emval.js | 147 | ||||
-rwxr-xr-x[-rw-r--r--] | system/include/emscripten/bind.h | 1227 | ||||
-rw-r--r-- | system/include/emscripten/val.h | 224 | ||||
-rwxr-xr-x[-rw-r--r--] | system/include/emscripten/wire.h | 267 | ||||
-rwxr-xr-x | system/lib/embind/bind.cpp | 83 | ||||
-rwxr-xr-x | tests/embind/embind.test.js | 1587 | ||||
-rw-r--r-- | tests/embind/embind_test.cpp | 1874 | ||||
-rw-r--r-- | tests/embind/embind_test.js | 393 | ||||
-rwxr-xr-x | tests/embind/imvu_test_adapter.js | 616 | ||||
-rwxr-xr-x | tests/embind/underscore-1.4.2.js | 1200 | ||||
-rwxr-xr-x | tests/runner.py | 54 |
13 files changed, 7763 insertions, 1429 deletions
@@ -24,7 +24,7 @@ a license to everyone to use it as detailed in LICENSE.) * Pierre Renaux <pierre@talansoft.com> * Brian Anderson <banderson@mozilla.com> * Jon Bardin <diclophis@gmail.com> -* Jukka Jylänki <jujjyl@gmail.com> +* Jukka Jylänki <jujjyl@gmail.com> * Aleksander Guryanov <caiiiycuk@gmail.com> * Chad Austin <chad@chadaustin.me> (copyright owned by IMVU) * nandhp <nandhp@gmail.com> @@ -46,7 +46,7 @@ a license to everyone to use it as detailed in LICENSE.) * Anthony Liot <wolfviking0@yahoo.com> * Michael Riss <Michael.Riss@gmx.de> * Jasper St. Pierre <jstpierre@mecheye.net> -* Manuel Schölling <manuel.schoelling@gmx.de> +* Manuel Schölling <manuel.schoelling@gmx.de> * Bruce Mitchener, Jr. <bruce.mitchener@gmail.com> * Michael Bishop <mbtyke@gmail.com> * Roger Braun <roger@rogerbraun.net> @@ -57,9 +57,14 @@ a license to everyone to use it as detailed in LICENSE.) * Ting-Yuan Huang <thuang@mozilla.com> * Joshua Granick <jgranick@blackberry.com> * Felix H. Dahlke <fhd@ubercode.de> -* Éloi Rivard <azmeuk@gmail.com> +* Éloi Rivard <azmeuk@gmail.com> * Alexander Gladysh <ag@logiceditor.com> * Arlo Breault <arlolra@gmail.com> * Jacob Lee <artdent@gmail.com> (copyright owned by Google, Inc.) * Joe Lee <jlee@imvu.com> (copyright owned by IMVU) - +* Andy Friesen <andy@imvu.com> (copyright owned by IMVU) +* Bill Welden <bwelden@imvu.com> (copyright owned by IMVU) +* Michael Ey <mey@imvu.com> (copyright owned by IMVU) +* Llorens Marti Garcia <lgarcia@imvu.com> (copyright owned by IMVU) +* Jinsuck Kim <jkim@imvu.com> (copyright owned by IMVU) +* Todd Lee <tlee@imvu.com> (copyright owned by IMVU) diff --git a/src/embind/embind.js b/src/embind/embind.js index d40d6ca2..ee717f4b 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, HEAP32, HEAPU8*/ +/*global Pointer_stringify*/ /*global __emval_register, _emval_handle_array, __emval_decref*/ +/*global ___getTypeName*/ +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,136 +150,213 @@ 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) { +function getTypeName(type) { + var ptr = ___getTypeName(type); + var rv = Pointer_stringify(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 = Pointer_stringify(name); - validateType(voidType, name); - typeRegistry[voidType] = { + registerType(rawType, { name: name, fromWireType: function() { return undefined; - } - }; + }, + }); } -function __embind_register_bool(boolType, name, trueValue, falseValue) { +function __embind_register_bool(rawType, name, trueValue, falseValue) { name = Pointer_stringify(name); - validateType(boolType, name); - typeRegistry[boolType] = { + registerType(rawType, { name: name, + fromWireType: function(wt) { + // ambiguous emscripten ABI: sometimes return values are + // true or false, and sometimes integers (0 or 1) + return !!wt; + }, toWireType: function(destructors, o) { return o ? trueValue : falseValue; }, - fromWireType: function(wt) { - return wt === trueValue; - }, - }; + }); } -function __embind_register_integer(primitiveType, name) { +// 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 = Pointer_stringify(name); - validateType(primitiveType, name); - typeRegistry[primitiveType] = { + 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, + 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; - } - }; + }); } -function __embind_register_float(primitiveType, name) { +function __embind_register_float(rawType, name) { name = Pointer_stringify(name); - validateType(primitiveType, name); - typeRegistry[primitiveType] = { + registerType(rawType, { name: name, + 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; - } - }; + }); } -function __embind_register_cstring(stringType, name) { +function __embind_register_cstring(rawType, name) { name = Pointer_stringify(name); - validateType(stringType, name); - typeRegistry[stringType] = { + registerType(rawType, { name: name, + fromWireType: function(value) { + var length = HEAP32[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) { - var ptr = _malloc(value.length + 1); - writeStringToMemory(value, ptr); - destructors.push(_free); - destructors.push(ptr); + // assumes 4-byte alignment + var length = value.length; + var ptr = _malloc(4 + length); + HEAP32[ptr >> 2] = length; + for (var i = 0; i < length; ++i) { + HEAPU8[ptr + 4 + i] = value.charCodeAt(i); + } + destructors.push(_free, ptr); return ptr; }, - fromWireType: function(value) { - var rv = Pointer_stringify(value); - _free(value); - return rv; - } - }; + }); } -function __embind_register_emval(emvalType, name) { +function __embind_register_emval(rawType, name) { name = Pointer_stringify(name); - validateType(emvalType, name); - typeRegistry[emvalType] = { + registerType(rawType, { name: name, - toWireType: function(destructors, value) { - return __emval_register(value); - }, 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; -} - -function requireRegisteredType(type, humanName) { - var impl = typeRegistry[type]; - if (undefined === impl) { - throw new BindingError(humanName + " has unknown type: " + typeName(type)); - } - return impl; -} - -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; + }, + toWireType: function(destructors, value) { + return __emval_register(value); + }, + }); } function runDestructors(destructors) { @@ -163,427 +367,917 @@ 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); - - Module[name] = function() { - if (arguments.length !== argCount) { - throw new BindingError('emscripten binding function ' + name + ' called with ' + arguments.length + ' arguments, expected ' + argCount); +function makeInvoker(name, argCount, argTypes, invoker, fn) { + if (!FUNCTION_TABLE[fn]) { + throwBindingError('function '+name+' is not defined'); + } + return createNamedFunction(makeLegalFunctionName(name), function() { + if (arguments.length !== argCount - 1) { + throwBindingError('function ' + name + ' called with ' + arguments.length + ' arguments, expected ' + (argCount - 1)); } var destructors = []; - var args = new Array(argCount + 1); + var args = new Array(argCount); args[0] = fn; - for (var i = 0; i < argCount; ++i) { - args[i + 1] = argTypes[i].toWireType(destructors, arguments[i]); + for (var i = 1; i < argCount; ++i) { + args[i] = argTypes[i].toWireType(destructors, arguments[i - 1]); } - var rv = returnType.fromWireType(invoker.apply(null, args)); + var rv = invoker.apply(null, args); + rv = argTypes[0].fromWireType(rv); runDestructors(destructors); return rv; - }; + }); } -function __embind_register_tuple(tupleType, name, constructor, destructor) { +function __embind_register_function(name, argCount, rawArgTypesAddr, rawInvoker, fn) { + var argTypes = heap32VectorToArray(argCount, rawArgTypesAddr); name = Pointer_stringify(name); - constructor = FUNCTION_TABLE[constructor]; - destructor = FUNCTION_TABLE[destructor]; + rawInvoker = FUNCTION_TABLE[rawInvoker]; - var elements = []; + exposePublicSymbol(name, function() { + throwUnboundTypeError('Cannot call ' + name + ' due to unbound types', argTypes); + }, argCount - 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]); - } - destructors.push(destructor); - destructors.push(ptr); - return ptr; - } - }; + whenDependentTypesAreResolved([], argTypes, function(argTypes) { + replacePublicSymbol(name, makeInvoker(name, argCount, argTypes, rawInvoker, fn), argCount - 1); + return []; + }); } -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; +var tupleRegistrations = {}; + +function __embind_register_tuple(rawType, name, rawConstructor, rawDestructor) { + tupleRegistrations[rawType] = { + name: Pointer_stringify(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]); + } + destructors.push(rawDestructor, ptr); + return ptr; + }, + }]; }); } +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: Pointer_stringify(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: Pointer_stringify(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); + } + }; + }); - structType.fields[fieldName] = { - read: function(ptr) { - return fieldType.fromWireType(getter(ptr, memberPointer)); + 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]); + } + destructors.push(rawDestructor, ptr); + 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; +} + +RegisteredPointer.prototype.toWireType = function(destructors, handle) { + var self = this; + function throwCannotConvert() { + var name; + if (handle.$$.smartPtrType) { + name = handle.$$.smartPtrType.name; + } else { + name = handle.$$.ptrType.name; + } + throwBindingError('Cannot convert argument of type ' + name + ' to parameter type ' + self.name); + } + + 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 instanceof this.registeredClass.constructor)) { + throwBindingError('Expected null or instance of ' + this.name + ', got ' + _embind_repr(handle)); + } + // 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 |