summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AUTHORS13
-rwxr-xr-x[-rw-r--r--]src/embind/embind.js1507
-rwxr-xr-x[-rw-r--r--]src/embind/emval.js147
-rwxr-xr-x[-rw-r--r--]system/include/emscripten/bind.h1227
-rw-r--r--system/include/emscripten/val.h224
-rwxr-xr-x[-rw-r--r--]system/include/emscripten/wire.h267
-rwxr-xr-xsystem/lib/embind/bind.cpp83
-rwxr-xr-xtests/embind/embind.test.js1587
-rw-r--r--tests/embind/embind_test.cpp1874
-rw-r--r--tests/embind/embind_test.js393
-rwxr-xr-xtests/embind/imvu_test_adapter.js616
-rwxr-xr-xtests/embind/underscore-1.4.2.js1200
-rwxr-xr-xtests/runner.py54
13 files changed, 7763 insertions, 1429 deletions
diff --git a/AUTHORS b/AUTHORS
index 87a656d6..1c6cf0ec 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -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 reference to the handle
+ if (this.isSmartPointer && undefined === handle.$$.smartPtr) {
+ throwBindingError('Passing raw pointer to smart pointer is illegal');
+ }
+ if (!this.isConst && handle.$$.ptrType.isConst) {
+ throwCannotConvert();
+ }
+ var handleClass = handle.$$.ptrType.registeredClass;
+ var ptr = upcastPointer(handle.$$.ptr, handleClass, this.registeredClass);
+ if (this.isSmartPointer) {
+ switch (this.sharingPolicy) {
+ case 0: // NONE
+ // no upcasting
+ if (handle.$$.smartPtrType === this) {
+ ptr = handle.$$.smartPtr;
+ } else {
+ throwCannotConvert();
+ }
+ break;
+
+ case 1: // INTRUSIVE
+ throwBindingError('INTRUSIVE sharing policy not yet supported');
+ 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;
+};
+
+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];
+ 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 clone = Object.create(Handle.prototype);
- clone.count = this.count;
- clone.ptr = this.ptr;
+ 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 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 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) {
+ 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_method(
- classType,
+function __embind_register_class_function(
+ rawClassType,
methodName,
- returnType,
argCount,
- argTypes,
- invoker,
- memberFunctionSize,
- memberFunction
+ rawArgTypesAddr, // [ReturnType, ThisType, Args...]
+ rawInvoker,
+ context
) {
- classType = requireRegisteredType(classType, 'class');
+ var rawArgTypes = heap32VectorToArray(argCount, rawArgTypesAddr);
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);
+ 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);
- }
-
- 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]);
+ 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;
}
- var rv = returnType.fromWireType(invoker.apply(null, args));
- runDestructors(destructors);
- return rv;
- };
+ whenDependentTypesAreResolved([], rawArgTypes, function(argTypes) {
+ var memberFunction = createNamedFunction(makeLegalFunctionName(humanName), function() {
+ if (arguments.length !== argCount - 2) {
+ throwBindingError(humanName + ' called with ' + arguments.length + ' arguments, expected ' + (argCount-2));
+ }
+
+ validateThis(this, classType, humanName);
+
+ var destructors = [];
+ var args = new Array(argCount + 1);
+ args[0] = context;
+ args[1] = argTypes[1].toWireType(destructors, this);
+ for (var i = 2; i < argCount; ++i) {
+ args[i] = argTypes[i].toWireType(destructors, arguments[i - 2]);
+ }
+ var rv = rawInvoker.apply(null, args);
+ rv = argTypes[0].fromWireType(rv);
+ 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');
+ var rawArgTypes = heap32VectorToArray(argCount, rawArgTypesAddr);
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);
- }
+ rawInvoker = FUNCTION_TABLE[rawInvoker];
+ whenDependentTypesAreResolved([], [rawClassType], function(classType) {
+ classType = classType[0];
+ var humanName = classType.name + '.' + methodName;
- var destructors = [];
- var args = new Array(argCount);
- for (var i = 0; i < argCount; ++i) {
- args[i] = argTypes[i].toWireType(destructors, arguments[i]);
+ var unboundTypesHandler = function() {
+ throwUnboundTypeError('Cannot call ' + humanName + ' due to unbound types', rawArgTypes);
+ };
+
+ 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 func = makeInvoker(humanName, argCount, argTypes, rawInvoker, fn);
+ if (undefined === proto[methodName].overloadTable) {
+ proto[methodName] = func;
+ } else {
+ proto[methodName].overloadTable[argCount-1] = func;
+ }
+ return [];
+ });
+ return [];
+ });
}
-function __embind_register_class_field(
- classType,
+function __embind_register_class_property(
+ rawClassType,
fieldName,
- fieldType,
+ rawFieldType,
getter,
setter,
- memberPointerSize,
- memberPointer
+ context
) {
- classType = requireRegisteredType(classType, 'class');
fieldName = Pointer_stringify(fieldName);
- var humanName = classType.name + '.' + fieldName;
- fieldType = requireRegisteredType(fieldType, 'field ' + humanName);
getter = FUNCTION_TABLE[getter];
setter = FUNCTION_TABLE[setter];
- memberPointer = copyMemberPointer(memberPointer, memberPointerSize);
+ whenDependentTypesAreResolved([], [rawClassType], function(classType) {
+ classType = classType[0];
+ var humanName = classType.name + '.' + fieldName;
- 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');
- }
- var destructors = [];
- setter(this.ptr, memberPointer, fieldType.toWireType(destructors, v));
- runDestructors(destructors);
- },
- enumerable: true
+ Object.defineProperty(classType.registeredClass.instancePrototype, fieldName, {
+ get: function() {
+ throwUnboundTypeError('Cannot access ' + humanName + ' due to unbound types', [rawFieldType]);
+ },
+ set: function() {
+ throwUnboundTypeError('Cannot access ' + humanName + ' due to unbound types', [rawFieldType]);
+ },
+ enumerable: true,
+ configurable: true
+ });
+
+ whenDependentTypesAreResolved([], [rawFieldType], function(fieldType) {
+ fieldType = fieldType[0];
+ Object.defineProperty(classType.registeredClass.instancePrototype, fieldName, {
+ get: function() {
+ var ptr = validateThis(this, classType, humanName + ' getter');
+ return fieldType.fromWireType(getter(context, ptr));
+ },
+ set: function(v) {
+ var ptr = validateThis(this, classType, humanName + ' setter');
+ var destructors = [];
+ setter(context, ptr, fieldType.toWireType(destructors, v));
+ runDestructors(destructors);
+ },
+ enumerable: true
+ });
+ 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 = Pointer_stringify(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);
- function Enum() {
+ function constructor() {
}
- Enum.values = {};
+ constructor.values = {};
- typeRegistry[enumType] = {
+ registerType(rawType, {
name: name,
- constructor: Enum,
+ constructor: constructor,
+ fromWireType: function(c) {
+ return this.constructor.values[c];
+ },
toWireType: function(destructors, c) {
return c.value;
},
- fromWireType: function(c) {
- return Enum.values[c];
- },
- };
-
- Module[name] = Enum;
+ });
+ exposePublicSymbol(name, constructor);
}
function __embind_register_enum_value(
- enumType,
+ rawEnumType,
name,
enumValue
) {
- enumType = requireRegisteredType(enumType, 'enum');
+ var enumType = requireRegisteredType(rawEnumType, 'enum');
name = Pointer_stringify(name);
var Enum = enumType.constructor;
@@ -597,24 +1291,33 @@ function __embind_register_enum_value(
}
function __embind_register_interface(
- interfaceType,
+ rawType,
name,
- constructor,
- destructor
+ rawConstructor,
+ rawDestructor
) {
name = Pointer_stringify(name);
- constructor = FUNCTION_TABLE[constructor];
- destructor = FUNCTION_TABLE[destructor];
+ rawConstructor = FUNCTION_TABLE[rawConstructor];
+ rawDestructor = FUNCTION_TABLE[rawDestructor];
- typeRegistry[interfaceType] = {
+ registerType(rawType, {
name: name,
+ rawConstructor: rawConstructor,
+ rawDestructor: rawDestructor,
toWireType: function(destructors, o) {
var handle = __emval_register(o);
- var ptr = constructor(handle);
- destructors.push(destructor);
- destructors.push(ptr);
+ var ptr = this.rawConstructor(handle);
+ destructors.push(this.rawDestructor, ptr);
return ptr;
},
- };
+ });
}
+function __embind_register_constant(name, type, value) {
+ name = Pointer_stringify(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..45030b99 100644..100755
--- a/src/embind/emval.js
+++ b/src/embind/emval.js
@@ -10,7 +10,23 @@ var _emval_free_list = [];
/** @expose */
Module.count_emval_handles = function() {
- return _emval_handle_array.length;
+ var count = 0;
+ for (var i = 0; i < _emval_handle_array.length; ++i) {
+ if (_emval_handle_array[i] !== undefined) {
+ ++count;
+ }
+ }
+ return count;
+};
+
+/** @expose */
+Module.get_first_emval = function() {
+ for (var i = 0; i < _emval_handle_array.length; ++i) {
+ if (_emval_handle_array[i] !== undefined) {
+ return _emval_handle_array[i];
+ }
+ }
+ return null;
};
// Private C++ API
@@ -19,6 +35,7 @@ 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;
}
@@ -40,38 +57,91 @@ 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(Pointer_stringify(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) {
+ 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);
}
-function __emval_set_property(handle, k, value) {
- k = Pointer_stringify(k);
- _emval_handle_array[handle].value[k] = _emval_handle_array[value].value;
+// appease jshint (technically this code uses eval)
+var global = (function(){return Function;})()('return this')();
+
+function __emval_get_global(name) {
+ name = Pointer_stringify(name);
+ return __emval_register(global[name]);
+}
+
+function __emval_get_module_property(name) {
+ name = Pointer_stringify(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) {
+ return __emval_register(_emval_handle_array[handle].value[_emval_handle_array[key].value]);
+}
+
+function __emval_set_property(handle, key, value) {
+ _emval_handle_array[handle].value[_emval_handle_array[key].value] = _emval_handle_array[value].value;
}
function __emval_as(handle, returnType) {
@@ -81,31 +151,46 @@ function __emval_as(handle, returnType) {
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) {
+ 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);
+
+ 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) {
+ name = Pointer_stringify(name);
+
+ var args = parseParameters(
+ argCount,
+ argTypes,
+ Array.prototype.slice.call(arguments, 4));
+ var obj = _emval_handle_array[handle].value;
+ obj[name].apply(obj, args);
+}
diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h
index 55fda986..57e44c0c 100644..100755
--- a/system/include/emscripten/bind.h
+++ b/system/include/emscripten/bind.h
@@ -3,11 +3,20 @@
#include <stddef.h>
#include <assert.h>
#include <string>
+#include <functional>
+#include <vector>
+#include <map>
#include <type_traits>
#include <emscripten/val.h>
#include <emscripten/wire.h>
namespace emscripten {
+ enum class sharing_policy {
+ NONE = 0,
+ INTRUSIVE = 1,
+ BY_EMVAL = 2,
+ };
+
namespace internal {
typedef void (*GenericFunction)();
typedef long GenericEnumValue;
@@ -19,150 +28,205 @@ namespace emscripten {
const char* payload) __attribute__((noreturn));
void _embind_register_void(
- TypeID voidType,
+ TYPEID voidType,
const char* name);
void _embind_register_bool(
- TypeID boolType,
+ TYPEID boolType,
const char* name,
bool trueValue,
bool falseValue);
void _embind_register_integer(
- TypeID integerType,
- const char* name);
+ TYPEID integerType,
+ const char* name,
+ long minRange,
+ unsigned long maxRange);
void _embind_register_float(
- TypeID floatType,
+ TYPEID floatType,
const char* name);
void _embind_register_cstring(
- TypeID stringType,
+ TYPEID stringType,
const char* name);
void _embind_register_emval(
- TypeID emvalType,
+ TYPEID emvalType,
const char* name);
void _embind_register_function(
const char* name,
- TypeID returnType,
unsigned argCount,
- TypeID argTypes[],
+ TYPEID argTypes[],
GenericFunction invoker,
GenericFunction function);
void _embind_register_tuple(
- TypeID tupleType,
+ TYPEID tupleType,
const char* name,
GenericFunction constructor,
GenericFunction destructor);
void _embind_register_tuple_element(
- TypeID tupleType,
- TypeID elementType,
+ TYPEID tupleType,
+ TYPEID getterReturnType,
GenericFunction getter,
+ void* getterContext,
+ TYPEID setterArgumentType,
GenericFunction setter,
- size_t memberPointerSize,
- void* memberPointer);
-
- void _embind_register_tuple_element_accessor(
- TypeID tupleType,
- TypeID elementType,
- GenericFunction staticGetter,
- size_t getterSize,
- void* getter,
- GenericFunction staticSetter,
- size_t setterSize,
- void* setter);
+ void* setterContext);
+
+ void _embind_finalize_tuple(TYPEID tupleType);
void _embind_register_struct(
- TypeID structType,
- const char* name,
+ TYPEID structType,
+ const char* fieldName,
GenericFunction constructor,
GenericFunction destructor);
void _embind_register_struct_field(
- TypeID structType,
- const char* name,
- TypeID fieldType,
+ TYPEID structType,
+ const char* fieldName,
+ TYPEID getterReturnType,
GenericFunction getter,
+ void* getterContext,
+ TYPEID setterArgumentType,
GenericFunction setter,
- size_t memberPointerSize,
- void* memberPointer);
+ void* setterContext);
+
+ void _embind_finalize_struct(TYPEID structType);
+
+ void _embind_register_smart_ptr(
+ TYPEID pointerType,
+ TYPEID pointeeType,
+ const char* pointerName,
+ sharing_policy sharingPolicy,
+ GenericFunction getPointee,
+ GenericFunction constructor,
+ GenericFunction share,
+ GenericFunction destructor);
void _embind_register_class(
- TypeID classType,
+ TYPEID classType,
+ TYPEID pointerType,
+ TYPEID constPointerType,
+ TYPEID baseClassType,
+ GenericFunction getActualType,
+ GenericFunction upcast,
+ GenericFunction downcast,
const char* className,
GenericFunction destructor);
void _embind_register_class_constructor(
- TypeID classType,
+ TYPEID classType,
unsigned argCount,
- TypeID argTypes[],
+ TYPEID argTypes[],
+ GenericFunction invoker,
GenericFunction constructor);
- void _embind_register_class_method(
- TypeID classType,
+ void _embind_register_class_function(
+ TYPEID classType,
const char* methodName,
- TypeID returnType,
unsigned argCount,
- TypeID argTypes[],
+ TYPEID argTypes[],
GenericFunction invoker,
- size_t memberFunctionSize,
- void* memberFunction);
+ void* context);
- void _embind_register_class_field(
- TypeID classType,
+ void _embind_register_class_property(
+ TYPEID classType,
const char* fieldName,
- TypeID fieldType,
+ TYPEID fieldType,
GenericFunction getter,
GenericFunction setter,
- size_t memberPointerSize,
- void* memberPointer);
+ void* context);
- void _embind_register_class_classmethod(
- TypeID classType,
+ void _embind_register_class_class_function(
+ TYPEID classType,
const char* methodName,
- TypeID returnType,
unsigned argCount,
- TypeID argTypes[],
+ TYPEID argTypes[],
+ GenericFunction invoker,
GenericFunction method);
void _embind_register_enum(
- TypeID enumType,
+ TYPEID enumType,
const char* name);
void _embind_register_enum_value(
- TypeID enumType,
+ TYPEID enumType,
const char* valueName,
GenericEnumValue value);
void _embind_register_interface(
- TypeID interfaceType,
+ TYPEID interfaceType,
const char* name,
GenericFunction constructor,
GenericFunction destructor);
+
+ void _embind_register_constant(
+ const char* name,
+ TYPEID constantType,
+ uintptr_t value);
}
+ }
+}
- extern void registerStandardTypes();
+namespace emscripten {
+ ////////////////////////////////////////////////////////////////////////////////
+ // POLICIES
+ ////////////////////////////////////////////////////////////////////////////////
- class BindingsDefinition {
- public:
- template<typename Function>
- BindingsDefinition(Function fn) {
- fn();
- }
+ template<int Index>
+ struct arg {
+ static constexpr int index = Index + 1;
+ };
+
+ struct ret_val {
+ static constexpr int index = 0;
+ };
+
+ /*
+ template<typename Slot>
+ struct allow_raw_pointer {
+ template<typename InputType, int Index>
+ struct Transform {
+ typedef typename std::conditional<
+ Index == Slot::index,
+ internal::AllowedRawPointer<typename std::remove_pointer<InputType>::type>,
+ InputType
+ >::type type;
+ };
+ };
+ */
+
+ // whitelist all raw pointers
+ struct allow_raw_pointers {
+ template<typename InputType, int Index>
+ struct Transform {
+ typedef typename std::conditional<
+ std::is_pointer<InputType>::value,
+ internal::AllowedRawPointer<typename std::remove_pointer<InputType>::type>,
+ InputType
+ >::type type;
};
+ };
+
+ // this is temporary until arg policies are reworked
+ template<typename Slot>
+ struct allow_raw_pointer : public allow_raw_pointers {
+ };
+
+ template<typename Signature>
+ typename std::add_pointer<Signature>::type select_overload(typename std::add_pointer<Signature>::type fn) {
+ return fn;
}
-}
-namespace emscripten {
namespace internal {
template<typename ReturnType, typename... Args>
struct Invoker {
static typename internal::BindingType<ReturnType>::WireType invoke(
- ReturnType (fn)(Args...),
+ ReturnType (*fn)(Args...),
typename internal::BindingType<Args>::WireType... args
) {
return internal::BindingType<ReturnType>::toWireType(
@@ -176,7 +240,7 @@ namespace emscripten {
template<typename... Args>
struct Invoker<void, Args...> {
static void invoke(
- void (fn)(Args...),
+ void (*fn)(Args...),
typename internal::BindingType<Args>::WireType... args
) {
return fn(
@@ -186,22 +250,38 @@ namespace emscripten {
};
}
- template<typename ReturnType, typename... Args>
- void function(const char* name, ReturnType (fn)(Args...)) {
- internal::registerStandardTypes();
+ ////////////////////////////////////////////////////////////////////////////////
+ // FUNCTIONS
+ ////////////////////////////////////////////////////////////////////////////////
+
+ extern "C" {
+ void* __getDynamicPointerType(void* p);
+ }
- internal::ArgTypeList<Args...> args;
- internal::_embind_register_function(
+ template<typename ReturnType, typename... Args, typename... Policies>
+ void function(const char* name, ReturnType (*fn)(Args...), Policies...) {
+ using namespace internal;
+ typename WithPolicies<Policies...>::template ArgTypeList<ReturnType, Args...> args;
+ _embind_register_function(
name,
- internal::getTypeID<ReturnType>(),
args.count,
args.types,
- reinterpret_cast<internal::GenericFunction>(&internal::Invoker<ReturnType, Args...>::invoke),
- reinterpret_cast<internal::GenericFunction>(fn));
+ reinterpret_cast<GenericFunction>(&Invoker<ReturnType, Args...>::invoke),
+ reinterpret_cast<GenericFunction>(fn));
}
namespace internal {
template<typename ClassType, typename... Args>
+ ClassType* operator_new(Args... args) {
+ return new ClassType(args...);
+ }
+
+ template<typename WrapperType, typename ClassType, typename... Args>
+ WrapperType wrapped_new(Args... args) {
+ return WrapperType(new ClassType(args...));
+ }
+
+ template<typename ClassType, typename... Args>
ClassType* raw_constructor(
typename internal::BindingType<Args>::WireType... args
) {
@@ -215,302 +295,812 @@ namespace emscripten {
delete ptr;
}
- template<typename ClassType, typename ReturnType, typename... Args>
- struct MethodInvoker {
- typedef ReturnType (ClassType::*MemberPointer)(Args...);
+ template<typename FunctionPointerType, typename ReturnType, typename ThisType, typename... Args>
+ struct FunctionInvoker {
static typename internal::BindingType<ReturnType>::WireType invoke(
- ClassType* ptr,
- const MemberPointer& method,
+ FunctionPointerType* function,
+ typename internal::BindingType<ThisType>::WireType wireThis,
typename internal::BindingType<Args>::WireType... args
) {
return internal::BindingType<ReturnType>::toWireType(
- (ptr->*method)(
- internal::BindingType<Args>::fromWireType(args)...
- )
+ (*function)(
+ internal::BindingType<ThisType>::fromWireType(wireThis),
+ internal::BindingType<Args>::fromWireType(args)...)
);
}
};
- template<typename ClassType, typename... Args>
- struct MethodInvoker<ClassType, void, Args...> {
- typedef void (ClassType::*MemberPointer)(Args...);
+ template<typename FunctionPointerType, typename ThisType, typename... Args>
+ struct FunctionInvoker<FunctionPointerType, void, ThisType, Args...> {
static void invoke(
- ClassType* ptr,
- const MemberPointer& method,
+ FunctionPointerType* function,
+ typename internal::BindingType<ThisType>::WireType wireThis,
typename internal::BindingType<Args>::WireType... args
) {
- return (ptr->*method)(
- internal::BindingType<Args>::fromWireType(args)...
- );
+ (*function)(
+ internal::BindingType<ThisType>::fromWireType(wireThis),
+ internal::BindingType<Args>::fromWireType(args)...);
}
};
- template<typename ClassType, typename ReturnType, typename... Args>
- struct ConstMethodInvoker {
- typedef ReturnType (ClassType::*MemberPointer)(Args...) const;
+ template<typename MemberPointer,
+ typename ReturnType,
+ typename ThisType,
+ typename... Args>
+ struct MethodInvoker {
static typename internal::BindingType<ReturnType>::WireType invoke(
- const ClassType* ptr,
const MemberPointer& method,
+ typename internal::BindingType<ThisType>::WireType wireThis,
typename internal::BindingType<Args>::WireType... args
) {
return internal::BindingType<ReturnType>::toWireType(
- (ptr->*method)(
+ (internal::BindingType<ThisType>::fromWireType(wireThis)->*method)(
internal::BindingType<Args>::fromWireType(args)...
)
);
}
};
- template<typename ClassType, typename... Args>
- struct ConstMethodInvoker<ClassType, void, Args...> {
- typedef void (ClassType::*MemberPointer)(Args...) const;
+ template<typename MemberPointer,
+ typename ThisType,
+ typename... Args>
+ struct MethodInvoker<MemberPointer, void, ThisType, Args...> {
static void invoke(
- const ClassType* ptr,
const MemberPointer& method,
+ typename internal::BindingType<ThisType>::WireType wireThis,
typename internal::BindingType<Args>::WireType... args
) {
- return (ptr->*method)(
+ return (internal::BindingType<ThisType>::fromWireType(wireThis)->*method)(
internal::BindingType<Args>::fromWireType(args)...
);
}
};
- template<typename ClassType, typename FieldType>
- struct FieldAccess {
- typedef FieldType ClassType::*MemberPointer;
- typedef internal::BindingType<FieldType> FieldBinding;
- typedef typename FieldBinding::WireType WireType;
+ template<typename InstanceType, typename MemberType>
+ struct MemberAccess {
+ typedef MemberType InstanceType::*MemberPointer;
+ typedef internal::BindingType<MemberType> MemberBinding;
+ typedef typename MemberBinding::WireType WireType;
- static WireType get(
- ClassType& ptr,
- const MemberPointer& field
+ template<typename ClassType>
+ static WireType getWire(
+ const MemberPointer& field,
+ const ClassType& ptr
) {
- return FieldBinding::toWireType(ptr.*field);
+ return MemberBinding::toWireType(ptr.*field);
}
- static void set(
- ClassType& ptr,
+ template<typename ClassType>
+ static void setWire(
const MemberPointer& field,
+ ClassType& ptr,
WireType value
) {
- ptr.*field = FieldBinding::fromWireType(value);
+ ptr.*field = MemberBinding::fromWireType(value);
}
+ };
- template<typename Getter>
- static WireType propertyGet(
- ClassType& ptr,
- const Getter& getter
- ) {
- return FieldBinding::toWireType(getter(ptr));
+ // TODO: This could do a reinterpret-cast if sizeof(T) === sizeof(void*)
+ template<typename T>
+ inline void* getContext(const T& t) {
+ // not a leak because this is called once per binding
+ void* p = malloc(sizeof(T));
+ assert(p);
+ memcpy(p, &t, sizeof(T));
+ return p;
+ }
+
+ template<typename T>
+ struct GetterPolicy;
+
+ template<typename GetterReturnType, typename GetterThisType>
+ struct GetterPolicy<GetterReturnType (GetterThisType::*)() const> {
+ typedef GetterReturnType ReturnType;
+ typedef GetterReturnType (GetterThisType::*Context)() const;
+
+ typedef internal::BindingType<ReturnType> Binding;
+ typedef typename Binding::WireType WireType;
+
+ template<typename ClassType>
+ static WireType get(const Context& context, const ClassType& ptr) {
+ return Binding::toWireType((ptr.*context)());
}
- template<typename Setter>
- static void propertySet(
- ClassType& ptr,
- const Setter& setter,
- WireType value
- ) {
- setter(ptr, FieldBinding::fromWireType(value));
+ static void* getContext(Context context) {
+ return internal::getContext(context);
+ }
+ };
+
+ template<typename GetterReturnType, typename GetterThisType>
+ struct GetterPolicy<GetterReturnType (*)(const GetterThisType&)> {
+ typedef GetterReturnType ReturnType;
+ typedef GetterReturnType (*Context)(const GetterThisType&);
+
+ typedef internal::BindingType<ReturnType> Binding;
+ typedef typename Binding::WireType WireType;
+
+ template<typename ClassType>
+ static WireType get(const Context& context, const ClassType& ptr) {
+ return Binding::toWireType(context(ptr));
+ }
+
+ static void* getContext(Context context) {
+ return internal::getContext(context);
+ }
+ };
+
+ template<typename T>
+ struct SetterPolicy;
+
+ template<typename SetterThisType, typename SetterArgumentType>
+ struct SetterPolicy<void (SetterThisType::*)(SetterArgumentType)> {
+ typedef SetterArgumentType ArgumentType;
+ typedef void (SetterThisType::*Context)(SetterArgumentType);
+
+ typedef internal::BindingType<SetterArgumentType> Binding;
+ typedef typename Binding::WireType WireType;
+
+ template<typename ClassType>
+ static void set(const Context& context, ClassType& ptr, WireType wt) {
+ (ptr.*context)(Binding::fromWireType(wt));
+ }
+
+ static void* getContext(Context context) {
+ return internal::getContext(context);
+ }
+ };
+
+ template<typename SetterThisType, typename SetterArgumentType>
+ struct SetterPolicy<void (*)(SetterThisType&, SetterArgumentType)> {
+ typedef SetterArgumentType ArgumentType;
+ typedef void (*Context)(SetterThisType&, SetterArgumentType);
+
+ typedef internal::BindingType<SetterArgumentType> Binding;
+ typedef typename Binding::WireType WireType;
+
+ template<typename ClassType>
+ static void set(const Context& context, ClassType& ptr, WireType wt) {
+ context(ptr, Binding::fromWireType(wt));
+ }
+
+ static void* getContext(Context context) {
+ return internal::getContext(context);
}
};
+
+ class noncopyable {
+ protected:
+ noncopyable() {}
+ ~noncopyable() {}
+ private:
+ noncopyable(const noncopyable&) = delete;
+ const noncopyable& operator=(const noncopyable&) = delete;
+ };
+
+ template<typename ClassType, typename ElementType>
+ typename BindingType<ElementType>::WireType get_by_index(int index, ClassType& ptr) {
+ return BindingType<ElementType>::toWireType(ptr[index]);
+ }
+
+ template<typename ClassType, typename ElementType>
+ void set_by_index(int index, ClassType& ptr, typename BindingType<ElementType>::WireType wt) {
+ ptr[index] = BindingType<ElementType>::fromWireType(wt);
+ }
}
+ template<int Index>
+ struct index {
+ };
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // VALUE TUPLES
+ ////////////////////////////////////////////////////////////////////////////////
+
template<typename ClassType>
- class value_tuple {
+ class value_tuple : public internal::noncopyable {
public:
value_tuple(const char* name) {
- internal::registerStandardTypes();
- internal::_embind_register_tuple(
- internal::getTypeID<ClassType>(),
+ using namespace internal;
+ _embind_register_tuple(
+ TypeID<ClassType>::get(),
name,
- reinterpret_cast<internal::GenericFunction>(&internal::raw_constructor<ClassType>),
- reinterpret_cast<internal::GenericFunction>(&internal::raw_destructor<ClassType>));
- }
-
- template<typename ElementType>
- value_tuple& element(ElementType ClassType::*field) {
- internal::_embind_register_tuple_element(
- internal::getTypeID<ClassType>(),
- internal::getTypeID<ElementType>(),
- reinterpret_cast<internal::GenericFunction>(&internal::FieldAccess<ClassType, ElementType>::get),
- reinterpret_cast<internal::GenericFunction>(&internal::FieldAccess<ClassType, ElementType>::set),
- sizeof(field),
- &field);
-
- return *this;
+ reinterpret_cast<GenericFunction>(&raw_constructor<ClassType>),
+ reinterpret_cast<GenericFunction>(&raw_destructor<ClassType>));
}
- template<typename ElementType>
- value_tuple& element(ElementType (*getter)(const ClassType&), void (*setter)(ClassType&, ElementType)) {
- internal::_embind_register_tuple_element_accessor(
- internal::getTypeID<ClassType>(),
- internal::getTypeID<ElementType>(),
- reinterpret_cast<internal::GenericFunction>(&internal::FieldAccess<ClassType, ElementType>::template propertyGet<ElementType(const ClassType&)>),
- sizeof(getter),
- &getter,
- reinterpret_cast<internal::GenericFunction>(&internal::FieldAccess<ClassType, ElementType>::template propertySet<void(ClassType&, ElementType)>),
- sizeof(setter),
- &setter);
- return *this;
+ ~value_tuple() {
+ using namespace internal;
+ _embind_finalize_tuple(TypeID<ClassType>::get());
}
- template<typename ElementType>
- value_tuple& element(ElementType (*getter)(const ClassType&), void (*setter)(ClassType&, const ElementType&)) {
- internal::_embind_register_tuple_element_accessor(
- internal::getTypeID<ClassType>(),
- internal::getTypeID<ElementType>(),
- reinterpret_cast<internal::GenericFunction>(&internal::FieldAccess<ClassType, ElementType>::template propertyGet<ElementType(const ClassType&)>),
- sizeof(getter),
- &getter,
- reinterpret_cast<internal::GenericFunction>(&internal::FieldAccess<ClassType, ElementType>::template propertySet<void(ClassType&, ElementType)>),
- sizeof(setter),
- &setter);
+ template<typename InstanceType, typename ElementType>
+ value_tuple& element(ElementType InstanceType::*field) {
+ using namespace internal;
+ _embind_register_tuple_element(
+ TypeID<ClassType>::get(),
+ TypeID<ElementType>::get(),
+ reinterpret_cast<GenericFunction>(
+ &MemberAccess<InstanceType, ElementType>
+ ::template getWire<ClassType>),
+ getContext(field),
+ TypeID<ElementType>::get(),
+ reinterpret_cast<GenericFunction>(
+ &MemberAccess<InstanceType, ElementType>
+ ::template setWire<ClassType>),
+ getContext(field));
return *this;
}
- template<typename ElementType>
- value_tuple& element(ElementType (*getter)(const ClassType&), void (*setter)(ClassType&, const ElementType&&)) {
- internal::_embind_register_tuple_element_accessor(
- internal::getTypeID<ClassType>(),
- internal::getTypeID<ElementType>(),
- reinterpret_cast<internal::GenericFunction>(&internal::FieldAccess<ClassType, ElementType>::template propertyGet<ElementType(const ClassType&)>),
- sizeof(getter),
- &getter,
- reinterpret_cast<internal::GenericFunction>(&internal::FieldAccess<ClassType, ElementType>::template propertySet<void(ClassType&, ElementType)>),
- sizeof(setter),
- &setter);
+ template<typename Getter, typename Setter>
+ value_tuple& element(Getter getter, Setter setter) {
+ using namespace internal;
+ typedef GetterPolicy<Getter> GP;
+ typedef SetterPolicy<Setter> SP;
+ _embind_register_tuple_element(
+ TypeID<ClassType>::get(),
+ TypeID<typename GP::ReturnType>::get(),
+ reinterpret_cast<GenericFunction>(&GP::template get<ClassType>),
+ GP::getContext(getter),
+ TypeID<typename SP::ArgumentType>::get(),
+ reinterpret_cast<GenericFunction>(&SP::template set<ClassType>),
+ SP::getContext(setter));
return *this;
}
- template<typename ElementType>
- value_tuple& element(ElementType (*getter)(const ClassType&), void (*setter)(ClassType&, ElementType&)) {
- internal::_embind_register_tuple_element_accessor(
- internal::getTypeID<ClassType>(),
- internal::getTypeID<ElementType>(),
- reinterpret_cast<internal::GenericFunction>(&internal::FieldAccess<ClassType, ElementType>::template propertyGet<ElementType(const ClassType&)>),
- sizeof(getter),
- &getter,
- reinterpret_cast<internal::GenericFunction>(&internal::FieldAccess<ClassType, ElementType>::template propertySet<void(ClassType&, ElementType)>),
- sizeof(setter),
- &setter);
+ template<int Index>
+ value_tuple& element(index<Index>) {
+ using namespace internal;
+ ClassType* null = 0;
+ typedef typename std::remove_reference<decltype((*null)[Index])>::type ElementType;
+ _embind_register_tuple_element(
+ TypeID<ClassType>::get(),
+ TypeID<ElementType>::get(),
+ reinterpret_cast<GenericFunction>(&internal::get_by_index<ClassType, ElementType>),
+ reinterpret_cast<void*>(Index),
+ TypeID<ElementType>::get(),
+ reinterpret_cast<GenericFunction>(&internal::set_by_index<ClassType, ElementType>),
+ reinterpret_cast<void*>(Index));
return *this;
}
};
+ ////////////////////////////////////////////////////////////////////////////////
+ // VALUE STRUCTS
+ ////////////////////////////////////////////////////////////////////////////////
+
template<typename ClassType>
- class value_struct {
+ class value_struct : public internal::noncopyable {
public:
value_struct(const char* name) {
- internal::registerStandardTypes();
- internal::_embind_register_struct(
- internal::getTypeID<ClassType>(),
+ using namespace internal;
+ _embind_register_struct(
+ TypeID<ClassType>::get(),
name,
- reinterpret_cast<internal::GenericFunction>(&internal::raw_constructor<ClassType>),
- reinterpret_cast<internal::GenericFunction>(&internal::raw_destructor<ClassType>));
+ reinterpret_cast<GenericFunction>(&raw_constructor<ClassType>),
+ reinterpret_cast<GenericFunction>(&raw_destructor<ClassType>));
}
- template<typename FieldType>
- value_struct& field(const char* fieldName, FieldType ClassType::*field) {
- internal::_embind_register_struct_field(
- internal::getTypeID<ClassType>(),
+ ~value_struct() {
+ _embind_finalize_struct(internal::TypeID<ClassType>::get());
+ }
+
+ template<typename InstanceType, typename FieldType>
+ value_struct& field(const char* fieldName, FieldType InstanceType::*field) {
+ using namespace internal;
+ _embind_register_struct_field(
+ TypeID<ClassType>::get(),
fieldName,
- internal::getTypeID<FieldType>(),
- reinterpret_cast<internal::GenericFunction>(&internal::FieldAccess<ClassType, FieldType>::get),
- reinterpret_cast<internal::GenericFunction>(&internal::FieldAccess<ClassType, FieldType>::set),
- sizeof(field),
- &field);
-
+ TypeID<FieldType>::get(),
+ reinterpret_cast<GenericFunction>(
+ &MemberAccess<InstanceType, FieldType>
+ ::template getWire<ClassType>),
+ getContext(field),
+ TypeID<FieldType>::get(),
+ reinterpret_cast<GenericFunction>(
+ &MemberAccess<InstanceType, FieldType>
+ ::template setWire<ClassType>),
+ getContext(field));
+ return *this;
+ }
+
+ template<typename Getter, typename Setter>
+ value_struct& field(
+ const char* fieldName,
+ Getter getter,
+ Setter setter
+ ) {
+ using namespace internal;
+ typedef GetterPolicy<Getter> GP;
+ typedef SetterPolicy<Setter> SP;
+ _embind_register_struct_field(
+ TypeID<ClassType>::get(),
+ fieldName,
+ TypeID<typename GP::ReturnType>::get(),
+ reinterpret_cast<GenericFunction>(&GP::template get<ClassType>),
+ GP::getContext(getter),
+ TypeID<typename SP::ArgumentType>::get(),
+ reinterpret_cast<GenericFunction>(&SP::template set<ClassType>),
+ SP::getContext(setter));
+ return *this;
+ }
+
+ template<int Index>
+ value_struct& field(const char* fieldName, index<Index>) {
+ using namespace internal;
+ ClassType* null = 0;
+ typedef typename std::remove_reference<decltype((*null)[Index])>::type ElementType;
+ _embind_register_struct_field(
+ TypeID<ClassType>::get(),
+ fieldName,
+ TypeID<ElementType>::get(),
+ reinterpret_cast<GenericFunction>(&internal::get_by_index<ClassType, ElementType>),
+ reinterpret_cast<void*>(Index),
+ TypeID<ElementType>::get(),
+ reinterpret_cast<GenericFunction>(&internal::set_by_index<ClassType, ElementType>),
+ reinterpret_cast<void*>(Index));
return *this;
}
};
- // TODO: support class definitions without constructors.
- // TODO: support external class constructors
- template<typename ClassType>
+ ////////////////////////////////////////////////////////////////////////////////
+ // SMART POINTERS
+ ////////////////////////////////////////////////////////////////////////////////
+
+ template<typename PointerType>
+ struct default_smart_ptr_trait {
+ static sharing_policy get_sharing_policy() {
+ return sharing_policy::NONE;
+ }
+
+ static void* share(void* v) {
+ return 0; // no sharing
+ }
+ };
+
+ // specialize if you have a different pointer type
+ template<typename PointerType>
+ struct smart_ptr_trait : public default_smart_ptr_trait<PointerType> {
+ typedef typename PointerType::element_type element_type;
+
+ static element_type* get(const PointerType& ptr) {
+ return ptr.get();
+ }
+ };
+
+ template<typename PointeeType>
+ struct smart_ptr_trait<std::shared_ptr<PointeeType>> {
+ typedef std::shared_ptr<PointeeType> PointerType;
+ typedef typename PointerType::element_type element_type;
+
+ static element_type* get(const PointerType& ptr) {
+ return ptr.get();
+ }
+
+ static sharing_policy get_sharing_policy() {
+ return sharing_policy::BY_EMVAL;
+ }
+
+ static std::shared_ptr<PointeeType>* share(PointeeType* p, internal::EM_VAL v) {
+ return new std::shared_ptr<PointeeType>(
+ p,
+ val_deleter(val::take_ownership(v)));
+ }
+
+ private:
+ class val_deleter {
+ public:
+ val_deleter() = delete;
+ explicit val_deleter(val v)
+ : v(v)
+ {}
+ void operator()(void const*) {
+ v();
+ // eventually we'll need to support emptied out val
+ v = val::undefined();
+ }
+ private:
+ val v;
+ };
+ };
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // CLASSES
+ ////////////////////////////////////////////////////////////////////////////////
+
+ // abstract classes
+ template<typename T>
+ class wrapper : public T {
+ public:
+ wrapper(const val& wrapped)
+ : wrapped(wrapped)
+ {}
+
+ template<typename ReturnType, typename... Args>
+ ReturnType call(const char* name, Args... args) const {
+ return Caller<ReturnType, Args...>::call(wrapped, name, args...);
+ }
+
+ private:
+ // this class only exists because you can't partially specialize function templates
+ template<typename ReturnType, typename... Args>
+ struct Caller {
+ static ReturnType call(const val& v, const char* name, Args... args) {
+ return v.call(name, args...).template as<ReturnType>();
+ }
+ };
+
+ template<typename... Args>
+ struct Caller<void, Args...> {
+ static void call(const val& v, const char* name, Args... args) {
+ v.call_void(name, args...);
+ }
+ };
+
+ val wrapped;
+ };
+
+#define EMSCRIPTEN_WRAPPER(T) \
+ T(const ::emscripten::val& v): wrapper(v) {}
+
+ namespace internal {
+ struct NoBaseClass {
+ template<typename ClassType>
+ static void verify() {
+ }
+
+ static TYPEID get() {
+ return nullptr;
+ }
+
+ template<typename ClassType>
+ static GenericFunction getUpcaster() {
+ return nullptr;
+ }
+
+ template<typename ClassType>
+ static GenericFunction getDowncaster() {
+ return nullptr;
+ }
+ };
+
+ // NOTE: this returns the class type, not the pointer type
+ template<typename T>
+ inline TYPEID getActualType(T* ptr) {
+ assert(ptr);
+ return reinterpret_cast<TYPEID>(&typeid(*ptr));
+ };
+ }
+
+ template<typename BaseClass>
+ struct base {
+ template<typename ClassType>
+ static void verify() {
+ static_assert(!std::is_same<ClassType, BaseClass>::value, "Base must not have same type as class");
+ static_assert(std::is_base_of<BaseClass, ClassType>::value, "Derived class must derive from base");
+ }
+
+ static internal::TYPEID get() {
+ return internal::TypeID<BaseClass>::get();
+ }
+
+ template<typename ClassType>
+ static internal::GenericFunction getUpcaster() {
+ return reinterpret_cast<internal::GenericFunction>(&convertPointer<ClassType, BaseClass>);
+ }
+
+ template<typename ClassType>
+ static internal::GenericFunction getDowncaster() {
+ return reinterpret_cast<internal::GenericFunction>(&convertPointer<BaseClass, ClassType>);
+ }
+
+ template<typename From, typename To>
+ static To* convertPointer(From* ptr) {
+ return static_cast<To*>(ptr);
+ }
+ };
+
+ template<typename PointerType>
+ struct ptr {
+ typedef PointerType pointer_type;
+ };
+
+ namespace internal {
+ template<typename T>
+ struct is_ptr {
+ enum { value = false };
+ };
+
+ template<typename T>
+ struct is_ptr<ptr<T>> {
+ enum { value = true };
+ };
+
+ template<typename T>
+ struct SmartPtrIfNeeded {
+ template<typename U>
+ SmartPtrIfNeeded(U& cls) {
+ cls.template smart_ptr<T>();
+ }
+ };
+
+ template<typename T>
+ struct SmartPtrIfNeeded<T*> {
+ template<typename U>
+ SmartPtrIfNeeded(U&) {
+ }
+ };
+ };
+
+ template<typename ClassType, typename BaseSpecifier = internal::NoBaseClass>
class class_ {
public:
- class_(const char* name) {
- internal::registerStandardTypes();
- internal::_embind_register_class(
- internal::getTypeID<ClassType>(),
+ class_() = delete;
+
+ template<typename = typename std::enable_if<!internal::is_ptr<ClassType>::value>::type>
+ explicit class_(const char* name) {
+ using namespace internal;
+
+ BaseSpecifier::template verify<ClassType>();
+
+ _embind_register_class(
+ TypeID<ClassType>::get(),
+ TypeID<AllowedRawPointer<ClassType>>::get(),
+ TypeID<AllowedRawPointer<const ClassType>>::get(),
+ BaseSpecifier::get(),
+ reinterpret_cast<GenericFunction>(&getActualType<ClassType>),
+ BaseSpecifier::template getUpcaster<ClassType>(),
+ BaseSpecifier::template getDowncaster<ClassType>(),
name,
- reinterpret_cast<internal::GenericFunction>(&internal::raw_destructor<ClassType>));
+ reinterpret_cast<GenericFunction>(&raw_destructor<ClassType>));
}
- template<typename... ConstructorArgs>
- class_& constructor() {
- internal::ArgTypeList<ConstructorArgs...> args;
- internal::_embind_register_class_constructor(
- internal::getTypeID<ClassType>(),
+ template<typename PointerType>
+ class_& smart_ptr() {
+ using namespace internal;
+
+ typedef smart_ptr_trait<PointerType> PointerTrait;
+ typedef typename PointerTrait::element_type PointeeType;
+
+ static_assert(std::is_same<ClassType, typename std::remove_cv<PointeeType>::type>::value, "smart pointer must point to this class");
+
+ _embind_register_smart_ptr(
+ TypeID<PointerType>::get(),
+ TypeID<PointeeType>::get(),
+ typeid(PointerType).name(),
+ PointerTrait::get_sharing_policy(),
+ reinterpret_cast<GenericFunction>(&PointerTrait::get),
+ reinterpret_cast<GenericFunction>(&operator_new<PointerType>),
+ reinterpret_cast<GenericFunction>(&PointerTrait::share),
+ reinterpret_cast<GenericFunction>(&raw_destructor<PointerType>));
+ return *this;
+ };
+
+ template<typename... ConstructorArgs, typename... Policies>
+ class_& constructor(Policies... policies) {
+ return constructor(
+ &internal::operator_new<ClassType, ConstructorArgs...>,
+ policies...);
+ }
+
+ template<typename... Args, typename... Policies>
+ class_& constructor(ClassType* (*factory)(Args...), Policies...) {
+ using namespace internal;
+
+ typename WithPolicies<Policies...>::template ArgTypeList<AllowedRawPointer<ClassType>, Args...> args;
+ _embind_register_class_constructor(
+ TypeID<ClassType>::get(),
args.count,
args.types,
- reinterpret_cast<internal::GenericFunction>(&internal::raw_constructor<ClassType, ConstructorArgs...>));
+ reinterpret_cast<GenericFunction>(&Invoker<ClassType*, Args...>::invoke),
+ reinterpret_cast<GenericFunction>(factory));
return *this;
}
- template<typename ReturnType, typename... Args>
- class_& method(const char* methodName, ReturnType (ClassType::*memberFunction)(Args...)) {
- internal::ArgTypeList<Args...> args;
- internal::_embind_register_class_method(
- internal::getTypeID<ClassType>(),
+ template<typename SmartPtr, typename... Args, typename... Policies>
+ class_& smart_ptr_constructor(SmartPtr (*factory)(Args...), Policies...) {
+ using namespace internal;
+
+ smart_ptr<SmartPtr>();
+
+ typename WithPolicies<Policies...>::template ArgTypeList<SmartPtr, Args...> args;
+ _embind_register_class_constructor(
+ TypeID<ClassType>::get(),
+ args.count,
+ args.types,
+ reinterpret_cast<GenericFunction>(&Invoker<SmartPtr, Args...>::invoke),
+ reinterpret_cast<GenericFunction>(factory));
+ return *this;
+ }
+
+ template<typename WrapperType, typename PointerType = WrapperType*>
+ class_& allow_subclass() {
+ using namespace internal;
+
+ auto cls = class_<WrapperType, base<ClassType>>(typeid(WrapperType).name())
+ ;
+ SmartPtrIfNeeded<PointerType> _(cls);
+
+ return class_function(
+ "implement",
+ &wrapped_new<PointerType, WrapperType, val>,
+ allow_raw_pointer<ret_val>());
+ }
+
+ template<typename ReturnType, typename... Args, typename... Policies>
+ class_& function(const char* methodName, ReturnType (ClassType::*memberFunction)(Args...), Policies...) {
+ using namespace internal;
+
+ typename WithPolicies<Policies...>::template ArgTypeList<ReturnType, AllowedRawPointer<ClassType>, Args...> args;
+ _embind_register_class_function(
+ TypeID<ClassType>::get(),
methodName,
- internal::getTypeID<ReturnType>(),
args.count,
args.types,
- reinterpret_cast<internal::GenericFunction>(&internal::MethodInvoker<ClassType, ReturnType, Args...>::invoke),
- sizeof(memberFunction),
- &memberFunction);
+ reinterpret_cast<GenericFunction>(&MethodInvoker<decltype(memberFunction), ReturnType, ClassType*, Args...>::invoke),
+ getContext(memberFunction));
return *this;
}
- template<typename ReturnType, typename... Args>
- class_& method(const char* methodName, ReturnType (ClassType::*memberFunction)(Args...) const) {
- internal::ArgTypeList<Args...> args;
- internal::_embind_register_class_method(
- internal::getTypeID<ClassType>(),
+ template<typename ReturnType, typename... Args, typename... Policies>
+ class_& function(const char* methodName, ReturnType (ClassType::*memberFunction)(Args...) const, Policies...) {
+ using namespace internal;
+
+ typename WithPolicies<Policies...>::template ArgTypeList<ReturnType, AllowedRawPointer<const ClassType>, Args...> args;
+ _embind_register_class_function(
+ TypeID<ClassType>::get(),
methodName,
- internal::getTypeID<ReturnType>(),
args.count,
args.types,
- reinterpret_cast<internal::GenericFunction>(&internal::ConstMethodInvoker<ClassType, ReturnType, Args...>::invoke),
- sizeof(memberFunction),
- &memberFunction);
+ reinterpret_cast<GenericFunction>(&MethodInvoker<decltype(memberFunction), ReturnType, const ClassType*, Args...>::invoke),
+ getContext(memberFunction));
+ return *this;
+ }
+
+ template<typename ReturnType, typename ThisType, typename... Args, typename... Policies>
+ class_& function(const char* methodName, ReturnType (*function)(ThisType, Args...), Policies...) {
+ using namespace internal;
+
+ typename WithPolicies<Policies...>::template ArgTypeList<ReturnType, ThisType, Args...> args;
+ _embind_register_class_function(
+ TypeID<ClassType>::get(),
+ methodName,
+ args.count,
+ args.types,
+ reinterpret_cast<GenericFunction>(&FunctionInvoker<decltype(function), ReturnType, ThisType, Args...>::invoke),
+ getContext(function));
return *this;
}
template<typename FieldType>
- class_& field(const char* fieldName, FieldType ClassType::*field) {
- internal::_embind_register_class_field(
- internal::getTypeID<ClassType>(),
+ class_& property(const char* fieldName, FieldType ClassType::*field) {
+ using namespace internal;
+
+ _embind_register_class_property(
+ TypeID<ClassType>::get(),
fieldName,
- internal::getTypeID<FieldType>(),
- reinterpret_cast<internal::GenericFunction>(&internal::FieldAccess<ClassType, FieldType>::get),
- reinterpret_cast<internal::GenericFunction>(&internal::FieldAccess<ClassType, FieldType>::set),
- sizeof(field),
- &field);
+ TypeID<FieldType>::get(),
+ reinterpret_cast<GenericFunction>(&MemberAccess<ClassType, FieldType>::template getWire<ClassType>),
+ reinterpret_cast<GenericFunction>(&MemberAccess<ClassType, FieldType>::template setWire<ClassType>),
+ getContext(field));
return *this;
}
- template<typename ReturnType, typename... Args>
- class_& classmethod(const char* methodName, ReturnType (*classMethod)(Args...)) {
- internal::ArgTypeList<Args...> args;
- internal::_embind_register_class_classmethod(
- internal::getTypeID<ClassType>(),
+ template<typename ReturnType, typename... Args, typename... Policies>
+ class_& class_function(const char* methodName, ReturnType (*classMethod)(Args...), Policies...) {
+ using namespace internal;
+
+ typename WithPolicies<Policies...>::template ArgTypeList<ReturnType, Args...> args;
+ _embind_register_class_class_function(
+ TypeID<ClassType>::get(),
methodName,
- internal::getTypeID<ReturnType>(),
args.count,
args.types,
- reinterpret_cast<internal::GenericFunction>(classMethod));
+ reinterpret_cast<internal::GenericFunction>(&internal::Invoker<ReturnType, Args...>::invoke),
+ reinterpret_cast<GenericFunction>(classMethod));
return *this;
}
+
+ template<typename ReturnType, typename... Args, typename... Policies>
+ class_& calloperator(const char* methodName, Policies... policies) {
+ return function(methodName, &ClassType::operator(), policies...);
+ }
};
+ ////////////////////////////////////////////////////////////////////////////////
+ // VECTORS
+ ////////////////////////////////////////////////////////////////////////////////
+
+ namespace internal {
+ template<typename VectorType>
+ struct VectorAccess {
+ static val get(
+ const VectorType& v,
+ typename VectorType::size_type index
+ ) {
+ if (index < v.size()) {
+ return val(v[index]);
+ } else {
+ return val::undefined();
+ }
+ }
+
+ static bool set(
+ VectorType& v,
+ typename VectorType::size_type index,
+ const typename VectorType::value_type& value
+ ) {
+ v[index] = value;
+ return true;
+ }
+ };
+ }
+
+ template<typename T>
+ class_<std::vector<T>> register_vector(const char* name) {
+ typedef std::vector<T> VecType;
+
+ void (VecType::*push_back)(const T&) = &VecType::push_back;
+ return class_<std::vector<T>>(name)
+ .template constructor<>()
+ .function("push_back", push_back)
+ .function("size", &VecType::size)
+ .function("get", &internal::VectorAccess<VecType>::get)
+ .function("set", &internal::VectorAccess<VecType>::set)
+ ;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // MAPS
+ ////////////////////////////////////////////////////////////////////////////////
+
+ namespace internal {
+ template<typename MapType>
+ struct MapAccess {
+ static val get(
+ const MapType& m,
+ const typename MapType::key_type& k
+ ) {
+ auto i = m.find(k);
+ if (i == m.end()) {
+ return val::undefined();
+ } else {
+ return val(i->second);
+ }
+ }
+
+ static void set(
+ MapType& m,
+ const typename MapType::key_type& k,
+ const typename MapType::mapped_type& v
+ ) {
+ m[k] = v;
+ }
+ };
+ }
+
+ template<typename K, typename V>
+ class_<std::map<K, V>> register_map(const char* name) {
+ typedef std::map<K,V> MapType;
+
+ return class_<MapType>(name)
+ .template constructor<>()
+ .function("size", &MapType::size)
+ .function("get", internal::MapAccess<MapType>::get)
+ .function("set", internal::MapAccess<MapType>::set)
+ ;
+ }
+
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // ENUMS
+ ////////////////////////////////////////////////////////////////////////////////
+
template<typename EnumType>
class enum_ {
public:
enum_(const char* name) {
_embind_register_enum(
- internal::getTypeID<EnumType>(),
+ internal::TypeID<EnumType>::get(),
name);
}
@@ -520,13 +1110,39 @@ namespace emscripten {
static_assert(sizeof(value) <= sizeof(internal::GenericEnumValue), "enum type must fit in a GenericEnumValue");
_embind_register_enum_value(
- internal::getTypeID<EnumType>(),
+ internal::TypeID<EnumType>::get(),
name,
static_cast<internal::GenericEnumValue>(value));
return *this;
}
};
+ ////////////////////////////////////////////////////////////////////////////////
+ // CONSTANTS
+ ////////////////////////////////////////////////////////////////////////////////
+
+ namespace internal {
+ template<typename T>
+ uintptr_t asGenericValue(T t) {
+ return static_cast<uintptr_t>(t);
+ }
+
+ template<typename T>
+ uintptr_t asGenericValue(T* p) {
+ return reinterpret_cast<uintptr_t>(p);
+ }
+ }
+
+ template<typename ConstantType>
+ void constant(const char* name, const ConstantType& v) {
+ using namespace internal;
+ typedef BindingType<const ConstantType&> BT;
+ _embind_register_constant(
+ name,
+ TypeID<const ConstantType&>::get(),
+ asGenericValue(BindingType<const ConstantType&>::toWireType(v)));
+ }
+
namespace internal {
template<typename T>
class optional {
@@ -541,13 +1157,22 @@ namespace emscripten {
}
}
- optional(const optional&) = delete;
+ optional(const optional& rhs)
+ : initialized(false)
+ {
+ *this = rhs;
+ }
T& operator*() {
assert(initialized);
return *get();
}
+ const T& operator*() const {
+ assert(initialized);
+ return *get();
+ }
+
explicit operator bool() const {
return initialized;
}
@@ -560,41 +1185,66 @@ namespace emscripten {
initialized = true;
return *this;
}
-
+
+ optional& operator=(const optional& o) {
+ if (initialized) {
+ get()->~T();
+ }
+ if (o.initialized) {
+ new(get()) T(*o);
+ }
+ initialized = o.initialized;
+ return *this;
+ }
+
private:
T* get() {
return reinterpret_cast<T*>(&data);
}
+ T const* get() const {
+ return reinterpret_cast<T const*>(&data);
+ }
+
bool initialized;
typename std::aligned_storage<sizeof(T)>::type data;
};
}
- template<typename InterfaceType>
- class wrapper : public InterfaceType {
+ ////////////////////////////////////////////////////////////////////////////////
+ // NEW INTERFACE
+ ////////////////////////////////////////////////////////////////////////////////
+
+ class JSInterface {
public:
- // Not necessary in any example so far, but appeases a compiler warning.
- virtual ~wrapper() {}
+ JSInterface(internal::EM_VAL handle) {
+ initialize(handle);
+ }
- typedef InterfaceType interface;
+ JSInterface(const JSInterface& obj)
+ : jsobj(obj.jsobj)
+ {}
+ template<typename ReturnType, typename... Args>
+ ReturnType call(const char* name, Args... args) {
+ assertInitialized();
+ return Caller<ReturnType, Args...>::call(*jsobj, name, args...);
+ }
+
+ static std::shared_ptr<JSInterface> cloneToSharedPtr(const JSInterface& i) {
+ return std::make_shared<JSInterface>(i);
+ }
+
+ private:
void initialize(internal::EM_VAL handle) {
if (jsobj) {
internal::_embind_fatal_error(
"Cannot initialize interface wrapper twice",
- typeid(InterfaceType).name());
+ "JSInterface");
}
jsobj = val::take_ownership(handle);
}
- template<typename ReturnType, typename... Args>
- ReturnType call(const char* name, Args... args) {
- assertInitialized();
- return Caller<ReturnType, Args...>::call(*jsobj, name, args...);
- }
-
- private:
// this class only exists because you can't partially specialize function templates
template<typename ReturnType, typename... Args>
struct Caller {
@@ -606,15 +1256,14 @@ namespace emscripten {
template<typename... Args>
struct Caller<void, Args...> {
static void call(val& v, const char* name, Args... args) {
- v.call(name, args...);
+ v.call_void(name, args...);
}
};
void assertInitialized() {
if (!jsobj) {
internal::_embind_fatal_error(
- "Cannot invoke call on uninitialized interface wrapper.",
- typeid(InterfaceType).name());
+ "Cannot invoke call on uninitialized Javascript interface wrapper.", "JSInterface");
}
}
@@ -622,27 +1271,35 @@ namespace emscripten {
};
namespace internal {
- template<typename WrapperType>
- WrapperType* create_interface_wrapper(EM_VAL e) {
- WrapperType* p = new WrapperType;
- p->initialize(e);
- return p;
- }
+ extern JSInterface* create_js_interface(EM_VAL e);
}
- template<typename WrapperType>
- class interface {
+ class register_js_interface {
public:
- typedef typename WrapperType::interface InterfaceType;
-
- interface(const char* name) {
+ register_js_interface() {
_embind_register_interface(
- internal::getTypeID<InterfaceType>(),
- name,
- reinterpret_cast<internal::GenericFunction>(&internal::create_interface_wrapper<WrapperType>),
- reinterpret_cast<internal::GenericFunction>(&internal::raw_destructor<WrapperType>));
+ internal::TypeID<JSInterface>::get(),
+ "JSInterface",
+ reinterpret_cast<internal::GenericFunction>(&internal::create_js_interface),
+ reinterpret_cast<internal::GenericFunction>(&internal::raw_destructor<JSInterface>));
}
};
}
-#define EMSCRIPTEN_BINDINGS(fn) static emscripten::internal::BindingsDefinition anon_symbol(fn);
+namespace emscripten {
+ namespace internal {
+ class BindingsDefinition {
+ public:
+ template<typename Function>
+ BindingsDefinition(Function fn) {
+ fn();
+ }
+ };
+ }
+}
+
+#define EMSCRIPTEN_BINDINGS(name) \
+ static struct BindingInitializer_##name { \
+ BindingInitializer_##name(); \
+ } BindingInitializer_##name##_instance; \
+ BindingInitializer_##name::BindingInitializer_##name()
diff --git a/system/include/emscripten/val.h b/system/include/emscripten/val.h
index 96db9326..91f775a7 100644
--- a/system/include/emscripten/val.h
+++ b/system/include/emscripten/val.h
@@ -2,6 +2,7 @@
#include <stdint.h> // uintptr_t
#include <emscripten/wire.h>
+#include <vector>
namespace emscripten {
namespace internal {
@@ -11,49 +12,104 @@ namespace emscripten {
void _emval_incref(EM_VAL value);
void _emval_decref(EM_VAL value);
+
+ EM_VAL _emval_new_array();
EM_VAL _emval_new_object();
- EM_VAL _emval_new_long(long value);
- EM_VAL _emval_new_cstring(const char* str);
- EM_VAL _emval_get_property(EM_VAL object, const char* key);
- EM_VAL _emval_get_property_by_long(EM_VAL object, long key);
- EM_VAL _emval_get_property_by_unsigned_long(EM_VAL object, unsigned long key);
- void _emval_set_property(EM_VAL object, const char* key, EM_VAL value);
- void _emval_set_property_by_int(EM_VAL object, long key, EM_VAL value);
- void _emval_as(EM_VAL value, emscripten::internal::TypeID returnType);
+ EM_VAL _emval_undefined();
+ EM_VAL _emval_null();
+ EM_VAL _emval_new_cstring(const char*);
+ void _emval_take_value(TYPEID type/*, ...*/);
+
+ EM_VAL _emval_new(
+ EM_VAL value,
+ unsigned argCount,
+ internal::TYPEID argTypes[]
+ /*, ... */);
+
+ EM_VAL _emval_get_global(const char* name);
+ EM_VAL _emval_get_module_property(const char* name);
+ EM_VAL _emval_get_property(EM_VAL object, EM_VAL key);
+ void _emval_set_property(EM_VAL object, EM_VAL key, EM_VAL value);
+ void _emval_as(EM_VAL value, TYPEID returnType);
EM_VAL _emval_call(
EM_VAL value,
unsigned argCount,
- internal::TypeID argTypes[]
+ internal::TYPEID argTypes[]
/*, ... */);
EM_VAL _emval_call_method(
EM_VAL value,
const char* methodName,
unsigned argCount,
- internal::TypeID argTypes[]
+ internal::TYPEID argTypes[]
/*, ... */);
+ void _emval_call_void_method(
+ EM_VAL value,
+ const char* methodName,
+ unsigned argCount,
+ internal::TYPEID argTypes[]
+ /*, ...*/);
}
}
class val {
public:
+ // missing operators:
+ // * delete
+ // * in
+ // * instanceof
+ // * typeof
+ // * ! ~ - + ++ --
+ // * * / %
+ // * + -
+ // * << >> >>>
+ // * < <= > >=
+ // * == != === !==
+ // * & ^ | && || ?:
+ //
+ // exposing void, comma, and conditional is unnecessary
+ // same with: = += -= *= /= %= <<= >>= >>>= &= ^= |=
+
+ static val array() {
+ return val(internal::_emval_new_array());
+ }
+
static val object() {
return val(internal::_emval_new_object());
- };
+ }
+
+ static val undefined() {
+ return val(internal::_emval_undefined());
+ }
+
+ static val null() {
+ return val(internal::_emval_null());
+ }
static val take_ownership(internal::EM_VAL e) {
return val(e);
}
- explicit val(long l)
- : handle(internal::_emval_new_long(l))
- {}
+ static val global(const char* name) {
+ return val(internal::_emval_get_global(name));
+ }
- explicit val(const char* str)
- : handle(internal::_emval_new_cstring(str))
- {}
+ static val module_property(const char* name) {
+ return val(internal::_emval_get_module_property(name));
+ }
+
+ template<typename T>
+ explicit val(const T& value) {
+ typedef internal::BindingType<T> BT;
+ auto taker = reinterpret_cast<internal::EM_VAL (*)(internal::TYPEID, typename BT::WireType)>(&internal::_emval_take_value);
+ handle = taker(internal::TypeID<T>::get(), BT::toWireType(value));
+ }
val() = delete;
+ val(const char* v)
+ : handle(internal::_emval_new_cstring(v))
+ {}
+
val(const val& v)
: handle(v.handle)
{
@@ -71,82 +127,114 @@ namespace emscripten {
return *this;
}
- val get(const char* key) const {
- return val(internal::_emval_get_property(handle, key));
- }
-
- val get(int key) const {
- return get(long(key));
+ bool hasOwnProperty(const char* key) const {
+ return val::global("Object")["prototype"]["hasOwnProperty"].call("call", *this, val(key)).as<bool>();
}
- val get(unsigned int key) const {
- typedef unsigned long T;
- return get(T(key));
- }
-
- val get(long key) const {
- return val(internal::_emval_get_property_by_long(handle, key));
- }
+ template<typename... Args>
+ val new_(Args... args) const {
+ using namespace internal;
- val get(unsigned long key) const {
- return val(internal::_emval_get_property_by_unsigned_long(handle, key));
+ WithPolicies<>::ArgTypeList<Args...> argList;
+ // todo: this is awfully similar to operator(), can we
+ // merge them somehow?
+ typedef EM_VAL (*TypedNew)(
+ EM_VAL,
+ unsigned,
+ TYPEID argTypes[],
+ typename BindingType<Args>::WireType...);
+ TypedNew typedNew = reinterpret_cast<TypedNew>(&_emval_new);
+ return val(
+ typedNew(
+ handle,
+ argList.count,
+ argList.types,
+ toWireType(args)...));
}
-
- void set(const char* key, val v) {
- internal::_emval_set_property(handle, key, v.handle);
+
+ template<typename T>
+ val operator[](const T& key) const {
+ return val(internal::_emval_get_property(handle, val(key).handle));
}
- void set(long key, val v) {
- internal::_emval_set_property_by_int(handle, key, v.handle);
+ template<typename T>
+ void set(const T& key, val v) {
+ internal::_emval_set_property(handle, val(key).handle, v.handle);
}
- template<typename ...Args>
+ template<typename... Args>
val operator()(Args... args) {
- internal::ArgTypeList<Args...> argList;
- typedef internal::EM_VAL (*TypedCall)(
- internal::EM_VAL,
+ using namespace internal;
+
+ WithPolicies<>::ArgTypeList<Args...> argList;
+ typedef EM_VAL (*TypedCall)(
+ EM_VAL,
unsigned,
- internal::TypeID argTypes[],
- typename internal::BindingType<Args>::WireType...);
- TypedCall typedCall = reinterpret_cast<TypedCall>(&internal::_emval_call);
+ TYPEID argTypes[],
+ typename BindingType<Args>::WireType...);
+ TypedCall typedCall = reinterpret_cast<TypedCall>(&_emval_call);
return val(
typedCall(
handle,
argList.count,
argList.types,
- internal::toWireType(args)...));
+ toWireType(args)...));
}
template<typename ...Args>
- val call(const char* name, Args... args) {
- internal::ArgTypeList<Args...> argList;
- typedef internal::EM_VAL (*TypedCall)(
- internal::EM_VAL,
+ val call(const char* name, Args... args) const {
+ using namespace internal;
+
+ WithPolicies<>::ArgTypeList<Args...> argList;
+ typedef EM_VAL (*TypedCall)(
+ EM_VAL,
const char* name,
unsigned,
- internal::TypeID argTypes[],
- typename internal::BindingType<Args>::WireType...);
- TypedCall typedCall = reinterpret_cast<TypedCall>(&internal::_emval_call_method);
+ TYPEID argTypes[],
+ typename BindingType<Args>::WireType...);
+ TypedCall typedCall = reinterpret_cast<TypedCall>(&_emval_call_method);
return val(
typedCall(
handle,
name,
argList.count,
argList.types,
- internal::toWireType(args)...));
+ toWireType(args)...));
+ }
+
+ template<typename ...Args>
+ void call_void(const char* name, Args... args) const {
+ using namespace internal;
+
+ WithPolicies<>::ArgTypeList<Args...> argList;
+ typedef void (*TypedCall)(
+ EM_VAL,
+ const char* name,
+ unsigned,
+ TYPEID argTypes[],
+ typename BindingType<Args>::WireType...);
+ TypedCall typedCall = reinterpret_cast<TypedCall>(&_emval_call_void_method);
+ return typedCall(
+ handle,
+ name,
+ argList.count,
+ argList.types,
+ toWireType(args)...);
}
template<typename T>
T as() const {
- typedef internal::BindingType<T> BT;
+ using namespace internal;
+
+ typedef BindingType<T> BT;
typedef typename BT::WireType (*TypedAs)(
- internal::EM_VAL value,
- emscripten::internal::TypeID returnType);
- TypedAs typedAs = reinterpret_cast<TypedAs>(&internal::_emval_as);
+ EM_VAL value,
+ TYPEID returnType);
+ TypedAs typedAs = reinterpret_cast<TypedAs>(&_emval_as);
- typename BT::WireType wt = typedAs(handle, internal::getTypeID<T>());
- internal::WireDeleter<T> deleter(wt);
+ typename BT::WireType wt = typedAs(handle, TypeID<T>::get());
+ WireDeleter<T> deleter(wt);
return BT::fromWireType(wt);
}
@@ -170,8 +258,22 @@ namespace emscripten {
return v.handle;
}
static val fromWireType(WireType v) {
- return val(v);
+ return val::take_ownership(v);
+ }
+ static void destroy(WireType v) {
}
};
}
+
+ template<typename T>
+ std::vector<T> vecFromJSArray(val v) {
+ auto l = v["length"].as<unsigned>();
+
+ std::vector<T> rv;
+ for(unsigned i = 0; i < l; ++i) {
+ rv.push_back(v[i].as<T>());
+ }
+
+ return rv;
+ };
}
diff --git a/system/include/emscripten/wire.h b/system/include/emscripten/wire.h
index fbf0897d..64114491 100644..100755
--- a/system/include/emscripten/wire.h
+++ b/system/include/emscripten/wire.h
@@ -7,66 +7,110 @@
//
// We'll call the on-the-wire type WireType.
+#include <cstdlib>
+#include <memory>
+#include <string>
+
namespace emscripten {
namespace internal {
- typedef const struct _TypeID* TypeID;
+ typedef const struct _TYPEID* TYPEID;
// This implementation is technically not legal, as it's not
// required that two calls to typeid produce the same exact
- // std::type_info instance. That said, it's likely to work.
- // Should it not work in the future: replace TypeID with
- // an int, and store all TypeInfo we see in a map, allocating
- // new TypeIDs as we add new items to the map.
+ // std::type_info instance. That said, it's likely to work
+ // given Emscripten compiles everything into one binary.
+ // Should it not work in the future: replace TypeID with an
+ // int, and store all TypeInfo we see in a map, allocating new
+ // TypeIDs as we add new items to the map.
template<typename T>
- inline TypeID getTypeID() {
- return reinterpret_cast<TypeID>(&typeid(T));
- }
+ struct TypeID {
+ static TYPEID get() {
+ return reinterpret_cast<TYPEID>(&typeid(T));
+ }
+ };
- // count<>
+ template<typename T>
+ struct TypeID<std::unique_ptr<T>> {
+ static TYPEID get() {
+ return TypeID<T>::get();
+ }
+ };
- template<typename... Args>
- struct count;
+ template<typename T>
+ struct TypeID<T*> {
+ static_assert(!std::is_pointer<T*>::value, "Implicitly binding raw pointers is illegal. Specify allow_raw_pointer<arg<?>>");
+ };
- template<>
- struct count<> {
- enum { value = 0 };
+ template<typename T>
+ struct AllowedRawPointer {
};
- template<typename T, typename... Args>
- struct count<T, Args...> {
- enum { value = 1 + count<Args...>::value };
+ template<typename T>
+ struct TypeID<AllowedRawPointer<T>> {
+ static TYPEID get() {
+ return reinterpret_cast<TYPEID>(&typeid(T*));
+ }
};
+
+ // ExecutePolicies<>
- // ArgTypeList<>
+ template<typename... Policies>
+ struct ExecutePolicies;
- template<typename... Args>
+ template<>
+ struct ExecutePolicies<> {
+ template<typename T, int Index>
+ struct With {
+ typedef T type;
+ };
+ };
+
+ template<typename Policy, typename... Remaining>
+ struct ExecutePolicies<Policy, Remaining...> {
+ template<typename T, int Index>
+ struct With {
+ typedef typename Policy::template Transform<
+ typename ExecutePolicies<Remaining...>::template With<T, Index>::type,
+ Index
+ >::type type;
+ };
+ };
+
+ // ArgTypes<>
+
+ template<int Index, typename... Args>
struct ArgTypes;
- template<>
- struct ArgTypes<> {
- static void fill(TypeID* argTypes) {
+ template<int Index>
+ struct ArgTypes<Index> {
+ template<typename... Policies>
+ static void fill(TYPEID* argTypes) {
}
};
- template<typename T, typename... Args>
- struct ArgTypes<T, Args...> {
- static void fill(TypeID* argTypes) {
- *argTypes = getTypeID<T>();
- return ArgTypes<Args...>::fill(argTypes + 1);
+ template<int Index, typename T, typename... Remaining>
+ struct ArgTypes<Index, T, Remaining...> {
+ template<typename... Policies>
+ static void fill(TYPEID* argTypes) {
+ typedef typename ExecutePolicies<Policies...>::template With<T, Index>::type TransformT;
+ *argTypes = TypeID<TransformT>::get();
+ return ArgTypes<Index + 1, Remaining...>::template fill<Policies...>(argTypes + 1);
}
};
- template<typename... Args>
- struct ArgTypeList {
- enum { args_count = count<Args...>::value };
-
- ArgTypeList() {
- count = args_count;
- ArgTypes<Args...>::fill(types);
- }
+ // WithPolicies<...>::ArgTypeList<...>
+ template<typename... Policies>
+ struct WithPolicies {
+ template<typename... Args>
+ struct ArgTypeList {
+ ArgTypeList() {
+ count = sizeof...(Args);
+ ArgTypes<0, Args...>::template fill<Policies...>(types);
+ }
- unsigned count;
- TypeID types[args_count];
+ unsigned count;
+ TYPEID types[sizeof...(Args)];
+ };
};
// BindingType<T>
@@ -74,19 +118,18 @@ namespace emscripten {
template<typename T>
struct BindingType;
-#define EMSCRIPTEN_DEFINE_NATIVE_BINDING_TYPE(type) \
- template<> \
- struct BindingType<type> { \
- typedef type WireType; \
- \
- constexpr static WireType toWireType(type v) { \
- return v; \
- } \
- constexpr static type fromWireType(WireType v) { \
- return v; \
- } \
- static void destroy(WireType) { \
- } \
+#define EMSCRIPTEN_DEFINE_NATIVE_BINDING_TYPE(type) \
+ template<> \
+ struct BindingType<type> { \
+ typedef type WireType; \
+ constexpr static WireType toWireType(const type& v) { \
+ return v; \
+ } \
+ constexpr static type fromWireType(WireType v) { \
+ return v; \
+ } \
+ static void destroy(WireType) { \
+ } \
}
EMSCRIPTEN_DEFINE_NATIVE_BINDING_TYPE(char);
@@ -120,35 +163,51 @@ namespace emscripten {
template<>
struct BindingType<std::string> {
- typedef char* WireType;
- static WireType toWireType(std::string v) {
- return strdup(v.c_str());
+ typedef struct {
+ size_t length;
+ char data[1]; // trailing data
+ }* WireType;
+ static WireType toWireType(const std::string& v) {
+ WireType wt = (WireType)malloc(sizeof(size_t) + v.length());
+ wt->length = v.length();
+ memcpy(wt->data, v.data(), v.length());
+ return wt;
}
- static std::string fromWireType(char* v) {
- return std::string(v);
+ static std::string fromWireType(WireType v) {
+ return std::string(v->data, v->length);
+ }
+ static void destroy(WireType v) {
+ free(v);
}
};
- template<>
- struct BindingType<const std::string&> {
- typedef char* WireType;
- static WireType toWireType(std::string v) {
- return strdup(v.c_str());
+ template<typename T>
+ struct BindingType<const T> : public BindingType<T> {
+ };
+
+ template<typename T>
+ struct BindingType<const T&> : public BindingType<T> {
+ };
+
+ template<typename T>
+ struct BindingType<T&&> {
+ typedef typename BindingType<T>::WireType WireType;
+ static WireType toWireType(const T& v) {
+ return BindingType<T>::toWireType(v);
}
- static std::string fromWireType(char* v) {
- return std::string(v);
+ static T fromWireType(WireType wt) {
+ return BindingType<T>::fromWireType(wt);
}
};
- template<typename Enum>
- struct EnumBindingType {
- typedef Enum WireType;
-
- static WireType toWireType(Enum v) {
- return v;
+ template<typename T>
+ struct BindingType<T*> {
+ typedef T* WireType;
+ static WireType toWireType(T* p) {
+ return p;
}
- static Enum fromWireType(WireType v) {
- return v;
+ static T* fromWireType(WireType wt) {
+ return wt;
}
};
@@ -157,33 +216,12 @@ namespace emscripten {
typedef typename std::remove_reference<T>::type ActualT;
typedef ActualT* WireType;
- struct Marshaller {
- explicit Marshaller(WireType wt)
- : wireType(wt)
- {}
-
- Marshaller(Marshaller&& wt)
- : wireType(wt.wireType)
- {
- wt.wireType = 0;
- }
-
- operator ActualT&() const {
- return *wireType;
- }
-
- private:
- Marshaller() = delete;
- Marshaller(const Marshaller&) = delete;
- ActualT* wireType;
- };
-
static WireType toWireType(T v) {
return new T(v);
}
- static Marshaller fromWireType(WireType p) {
- return Marshaller(p);
+ static ActualT& fromWireType(WireType p) {
+ return *p;
}
static void destroy(WireType p) {
@@ -191,19 +229,28 @@ namespace emscripten {
}
};
+ // Is this necessary?
template<typename T>
- struct WireDeleter {
+ struct GenericBindingType<std::unique_ptr<T>> {
typedef typename BindingType<T>::WireType WireType;
-
- WireDeleter(WireType wt)
- : wt(wt)
- {}
-
- ~WireDeleter() {
- BindingType<T>::destroy(wt);
+
+ static WireType toWireType(std::unique_ptr<T> p) {
+ return BindingType<T>::toWireType(*p);
+ }
+ };
+
+ template<typename Enum>
+ struct EnumBindingType {
+ typedef Enum WireType;
+
+ static WireType toWireType(Enum v) {
+ return v;
+ }
+ static Enum fromWireType(WireType v) {
+ return v;
+ }
+ static void destroy(WireType) {
}
-
- WireType wt;
};
// catch-all generic binding
@@ -219,5 +266,19 @@ namespace emscripten {
return BindingType<T>::toWireType(v);
}
+ template<typename T>
+ struct WireDeleter {
+ typedef typename BindingType<T>::WireType WireType;
+
+ WireDeleter(WireType wt)
+ : wt(wt)
+ {}
+
+ ~WireDeleter() {
+ BindingType<T>::destroy(wt);
+ }
+
+ WireType wt;
+ };
}
}
diff --git a/system/lib/embind/bind.cpp b/system/lib/embind/bind.cpp
index b63a86aa..deb55138 100755
--- a/system/lib/embind/bind.cpp
+++ b/system/lib/embind/bind.cpp
@@ -1,34 +1,69 @@
#include <emscripten/bind.h>
+#ifdef USE_CXA_DEMANGLE
+#include <../lib/libcxxabi/include/cxxabi.h>
+#endif
+#include <list>
+#include <vector>
+#include <typeinfo>
+#include <algorithm>
+#include <emscripten/emscripten.h>
+#include <climits>
using namespace emscripten;
-namespace emscripten {
- namespace internal {
- void registerStandardTypes() {
- static bool first = true;
- if (first) {
- first = false;
+extern "C" {
+ const char* __attribute__((used)) __getTypeName(const std::type_info* ti) {
+#ifdef USE_CXA_DEMANGLE
+ int stat;
+ char* demangled = abi::__cxa_demangle(ti->name(), NULL, NULL, &stat);
+ if (stat == 0 && demangled) {
+ return demangled;
+ }
- _embind_register_void(getTypeID<void>(), "void");
+ switch (stat) {
+ case -1:
+ return strdup("<allocation failure>");
+ case -2:
+ return strdup("<invalid C++ symbol>");
+ case -3:
+ return strdup("<invalid argument>");
+ default:
+ return strdup("<unknown error>");
+ }
+#else
+ return strdup(ti->name());
+#endif
+ }
+}
- _embind_register_bool(getTypeID<bool>(), "bool", true, false);
+namespace emscripten {
+ namespace internal {
+ JSInterface* create_js_interface(EM_VAL e) {
+ return new JSInterface(e);
+ }
+ }
+}
- _embind_register_integer(getTypeID<char>(), "char");
- _embind_register_integer(getTypeID<signed char>(), "signed char");
- _embind_register_integer(getTypeID<unsigned char>(), "unsigned char");
- _embind_register_integer(getTypeID<signed short>(), "short");
- _embind_register_integer(getTypeID<unsigned short>(), "unsigned short");
- _embind_register_integer(getTypeID<signed int>(), "int");
- _embind_register_integer(getTypeID<unsigned int>(), "unsigned int");
- _embind_register_integer(getTypeID<signed long>(), "long");
- _embind_register_integer(getTypeID<unsigned long>(), "unsigned long");
+EMSCRIPTEN_BINDINGS(native_and_builtin_types) {
+ using namespace emscripten::internal;
- _embind_register_float(getTypeID<float>(), "float");
- _embind_register_float(getTypeID<double>(), "double");
+ _embind_register_void(TypeID<void>::get(), "void");
+
+ _embind_register_bool(TypeID<bool>::get(), "bool", true, false);
- _embind_register_cstring(getTypeID<std::string>(), "std::string");
- _embind_register_emval(getTypeID<val>(), "emscripten::val");
- }
- }
- }
+ _embind_register_integer(TypeID<char>::get(), "char", CHAR_MIN, CHAR_MAX);
+ _embind_register_integer(TypeID<signed char>::get(), "signed char", SCHAR_MIN, SCHAR_MAX);
+ _embind_register_integer(TypeID<unsigned char>::get(), "unsigned char", 0, UCHAR_MAX);
+ _embind_register_integer(TypeID<signed short>::get(), "short", SHRT_MIN, SHRT_MAX);
+ _embind_register_integer(TypeID<unsigned short>::get(), "unsigned short", 0, USHRT_MAX);
+ _embind_register_integer(TypeID<signed int>::get(), "int", INT_MIN, INT_MAX);
+ _embind_register_integer(TypeID<unsigned int>::get(), "unsigned int", 0, UINT_MAX);
+ _embind_register_integer(TypeID<signed long>::get(), "long", LONG_MIN, LONG_MAX);
+ _embind_register_integer(TypeID<unsigned long>::get(), "unsigned long", 0, ULONG_MAX);
+
+ _embind_register_float(TypeID<float>::get(), "float");
+ _embind_register_float(TypeID<double>::get(), "double");
+
+ _embind_register_cstring(TypeID<std::string>::get(), "std::string");
+ _embind_register_emval(TypeID<val>::get(), "emscripten::val");
}
diff --git a/tests/embind/embind.test.js b/tests/embind/embind.test.js
new file mode 100755
index 00000000..290fed72
--- /dev/null
+++ b/tests/embind/embind.test.js
@@ -0,0 +1,1587 @@
+module({
+ Emscripten: '../../../../build/embind_test.js',
+}, function(imports) {
+ var cm = imports.Emscripten;
+
+ var CheckForLeaks = fixture("check for leaks", function() {
+ this.setUp(function() {
+ if (typeof INVOKED_FROM_EMSCRIPTEN_TEST_RUNNER === "undefined") { // TODO: Enable this to work in Emscripten runner as well!
+ cm._mallocDebug(2);
+ assert.equal(0, cm.count_emval_handles());
+ cm._mallocAssertAllMemoryFree();
+ }
+ });
+ this.tearDown(function() {
+ if (typeof INVOKED_FROM_EMSCRIPTEN_TEST_RUNNER === "undefined") { // TODO: Enable this to work in Emscripten runner as well!
+ cm._mallocAssertAllMemoryFree();
+ assert.equal(0, cm.count_emval_handles());
+ }
+ });
+ });
+
+ var BaseFixture = CheckForLeaks;
+
+ BaseFixture.extend("temp jig", function() {
+ test("temp test", function() {
+ });
+ });
+
+ if (typeof INVOKED_FROM_EMSCRIPTEN_TEST_RUNNER === "undefined") { // TODO: Enable this to work in Emscripten runner as well!
+
+ BaseFixture.extend("leak testing", function() {
+ test("no memory allocated at start of test", function() {
+ cm._mallocAssertAllMemoryFree();
+ });
+ test("assert when memory is allocated", function() {
+ var ptr = cm._malloc(42);
+ assert.throws(cm._MemoryAllocationError, function() {
+ cm._mallocAssertAllMemoryFree();
+ });
+ cm._free(ptr);
+ });
+ test("allocated memory counts down again for free", function() {
+ var ptr = cm._malloc(42);
+ cm._free(ptr);
+ cm._mallocAssertAllMemoryFree();
+ });
+ test("free without malloc throws MemoryAllocationError", function() {
+ var ptr = cm._malloc(42);
+ cm._free(ptr);
+ assert.throws(cm._MemoryAllocationError, function() {
+ cm._free(ptr);
+ });
+ });
+ });
+
+ }
+
+ BaseFixture.extend("access to base class members", function() {
+ test("method name in derived class silently overrides inherited name", function() {
+ var derived = new cm.Derived();
+ assert.equal("Derived", derived.getClassName());
+ derived.delete();
+ });
+ test("can reference base method from derived class", function(){
+ var derived = new cm.Derived();
+ assert.equal("Base", derived.getClassNameFromBase());
+ derived.delete();
+ });
+ test("can reference base method from doubly derived class", function() {
+ var derivedTwice = new cm.DerivedTwice();
+ assert.equal("Base", derivedTwice.getClassNameFromBase());
+ derivedTwice.delete();
+ });
+ test("can reference base method through unbound classes", function() {
+ var derivedThrice = new cm.DerivedThrice();
+ assert.equal("Base", derivedThrice.getClassNameFromBase());
+ derivedThrice.delete();
+ });
+ test("property name in derived class hides identically named property in base class for set", function() {
+ var derived = new cm.Derived();
+ derived.setMember(7);
+
+ derived.member = 17;
+
+ assert.equal(17, derived.getMember());
+ derived.delete();
+ });
+ test("can reference base property from derived class for get", function(){
+ var derived = new cm.Derived();
+ derived.setBaseMember(5);
+
+ assert.equal(5, derived.baseMember);
+
+ derived.delete();
+ });
+ test("can reference property of any base class for get when multiply derived", function(){
+ var derived = new cm.MultiplyDerived();
+ derived.setBaseMember(11);
+
+ assert.equal(11, derived.baseMember);
+
+ derived.delete();
+ });
+ test("can reference base property from derived class for set", function(){
+ var derived = new cm.Derived();
+
+ derived.baseMember = 32;
+
+ assert.equal(32, derived.getBaseMember());
+ derived.delete();
+ });
+ test("can reference property of any base for set when multiply derived", function(){
+ var derived = new cm.MultiplyDerived();
+ derived.setBaseMember(97);
+
+ derived.baseMember = 32;
+
+ assert.equal(32, derived.getBaseMember());
+ derived.delete();
+ });
+ test("can reach around derived property to access base property with same name for get", function() {
+ var derived = new cm.Derived();
+ derived.setMember(12);
+ derived.delete();
+ });
+
+ test("if deriving from second base adjusts pointer", function() {
+ var derived = new cm.HasTwoBases;
+ assert.equal("Base2", derived.getField());
+ derived.delete();
+ });
+
+ test("properties adjust pointer", function() {
+ var derived = new cm.HasTwoBases;
+ derived.field = "Foo";
+ assert.equal("Foo", derived.getField());
+ assert.equal("Foo", derived.field);
+ derived.delete();
+ });
+
+ test("calling method on unrelated class throws error", function() {
+ var a = new cm.HasTwoBases;
+ var e = assert.throws(cm.BindingError, function() {
+ cm.Derived.prototype.setMember.call(a, "foo");
+ });
+ assert.equal('Derived.setMember incompatible with "this" of type HasTwoBases', e.message);
+ a.delete();
+ });
+
+ test("calling method with invalid this throws error", function() {
+ var e = assert.throws(cm.BindingError, function() {
+ cm.Derived.prototype.setMember.call(undefined, "foo");
+ });
+ if (typeof INVOKED_FROM_EMSCRIPTEN_TEST_RUNNER === "undefined") { // TODO: Enable this to work in Emscripten runner as well!
+ // got Error: expected: Derived.setMember with invalid "this": undefined, actual: Derived.setMember incompatible with "this" of type Object
+ assert.equal('Derived.setMember with invalid "this": undefined', e.message);
+ }
+
+ var e = assert.throws(cm.BindingError, function() {
+ cm.Derived.prototype.setMember.call("this", "foo");
+ });
+ if (typeof INVOKED_FROM_EMSCRIPTEN_TEST_RUNNER === "undefined") { // TODO: Enable this to work in Emscripten runner as well!
+ // TODO got 'Derived.setMember incompatible with "this" of type Object'
+ assert.equal('Derived.setMember with invalid "this": this', e.message);
+ }
+
+ var e = assert.throws(cm.BindingError, function() {
+ cm.Derived.prototype.setMember.call({}, "foo");
+ });
+ if (typeof INVOKED_FROM_EMSCRIPTEN_TEST_RUNNER === "undefined") { // TODO: Enable this to work in Emscripten runner as well!
+ assert.equal('Derived.setMember incompatible with "this" of type Object', e.message);
+ }
+ });
+
+ test("setting and getting property on unrelated class throws error", function() {
+ var a = new cm.HasTwoBases;
+ var e = assert.throws(cm.BindingError, function() {
+ Object.getOwnPropertyDescriptor(cm.HeldBySmartPtr.prototype, 'i').set.call(a, 10);
+ });
+ assert.equal('HeldBySmartPtr.i setter incompatible with "this" of type HasTwoBases', e.message);
+
+ var e = assert.throws(cm.BindingError, function() {
+ Object.getOwnPropertyDescriptor(cm.HeldBySmartPtr.prototype, 'i').get.call(a);
+ });
+ assert.equal('HeldBySmartPtr.i getter incompatible with "this" of type HasTwoBases', e.message);
+
+ a.delete();
+ });
+ });
+
+ BaseFixture.extend("automatic upcasting of parameters passed to C++", function() {
+ // raw
+ test("raw pointer argument is upcast to parameter type", function() {
+ var derived = new cm.Derived();
+ var name = cm.embind_test_get_class_name_via_base_ptr(derived);
+ assert.equal("Base", name);
+ derived.delete();
+ });
+
+ test("automatic raw pointer upcasting works with multiple inheritance", function() {
+ var derived = new cm.MultiplyDerived();
+ var name = cm.embind_test_get_class_name_via_base_ptr(derived);
+ assert.equal("Base", name);
+ derived.delete();
+ });
+
+ test("automatic raw pointer upcasting does not change local pointer", function() {
+ var derived = new cm.MultiplyDerived();
+ cm.embind_test_get_class_name_via_base_ptr(derived);
+ assert.equal("MultiplyDerived", derived.getClassName());
+ derived.delete();
+ });
+
+ test("passing incompatible raw pointer to method throws exception", function() {
+ var base = new cm.Base();
+ assert.throws(cm.BindingError, function() {
+ cm.embind_test_get_class_name_via_second_base_ptr(base);
+ });
+ base.delete();
+ });
+
+ // raw polymorphic
+ test("polymorphic raw pointer argument is upcast to parameter type", function() {
+ var derived = new cm.PolyDerived();
+ var name = cm.embind_test_get_class_name_via_polymorphic_base_ptr(derived);
+ assert.equal("PolyBase", name);
+ derived.delete();
+ });
+
+ test("automatic polymorphic raw pointer upcasting works with multiple inheritance", function() {
+ var derived = new cm.PolyMultiplyDerived();
+ var name = cm.embind_test_get_class_name_via_polymorphic_base_ptr(derived);
+ assert.equal("PolyBase", name);
+ derived.delete();
+ });
+
+ test("passing incompatible raw polymorphic pointer to method throws exception", function() {
+ var base = new cm.PolyBase();
+ assert.throws(cm.BindingError, function() {
+ cm.embind_test_get_class_name_via_polymorphic_second_base_ptr(base);
+ });
+ base.delete();
+
+ });
+
+ // smart
+ test("can pass smart pointer to raw pointer parameter", function() {
+ var smartBase = cm.embind_test_return_smart_base_ptr();
+ assert.equal("Base", cm.embind_test_get_class_name_via_base_ptr(smartBase));
+ smartBase.delete();
+ });
+
+ test("can pass and upcast smart pointer to raw pointer parameter", function() {
+ var smartDerived = cm.embind_test_return_smart_derived_ptr();
+ assert.equal("Base", cm.embind_test_get_class_name_via_base_ptr(smartDerived));
+ smartDerived.delete();
+ });
+
+ test("smart pointer argument is upcast to parameter type", function() {
+ var derived = cm.embind_test_return_smart_derived_ptr();
+ assert.instanceof(derived, cm.Derived);
+ assert.instanceof(derived, cm.Base);
+ var name = cm.embind_test_get_class_name_via_smart_base_ptr(derived);
+ assert.equal("Base", name);
+ derived.delete();
+ });
+
+ test("return smart derived ptr as base", function() {
+ var derived = cm.embind_test_return_smart_derived_ptr_as_base();
+ assert.equal("PolyDerived", cm.embind_test_get_virtual_class_name_via_smart_polymorphic_base_ptr(derived));
+ assert.equal("PolyDerived", derived.getClassName());
+ derived.delete();
+ });
+
+ test("return smart derived ptr as val", function() {
+ var derived = cm.embind_test_return_smart_derived_ptr_as_val();
+ assert.equal("PolyDerived", cm.embind_test_get_virtual_class_name_via_smart_polymorphic_base_ptr(derived));
+ derived.delete();
+ });
+
+ test("automatic smart pointer upcasting works with multiple inheritance", function() {
+ var derived = cm.embind_test_return_smart_multiply_derived_ptr();
+ var name = cm.embind_test_get_class_name_via_smart_base_ptr(derived);
+ assert.equal("Base", name);
+ derived.delete();
+ });
+
+ test("automatically upcasted smart pointer parameter shares ownership with original argument", function() {
+ var derived = cm.embind_test_return_smart_multiply_derived_ptr();
+ assert.equal(1, cm.MultiplyDerived.getInstanceCount());
+ cm.embind_save_smart_base_pointer(derived);
+ assert.equal(1, cm.MultiplyDerived.getInstanceCount());
+ derived.delete();
+ assert.equal(1, cm.MultiplyDerived.getInstanceCount());
+ cm.embind_save_smart_base_pointer(null);
+ assert.equal(0, cm.MultiplyDerived.getInstanceCount());
+ });
+
+ // smart polymorphic
+ test("smart polymorphic pointer argument is upcast to parameter type", function() {
+ var derived = cm.embind_test_return_smart_polymorphic_derived_ptr();
+ var name = cm.embind_test_get_class_name_via_smart_polymorphic_base_ptr(derived);
+ assert.equal("PolyBase", name);
+ derived.delete();
+ });
+
+ test("automatic smart polymorphic pointer upcasting works with multiple inheritance", function() {
+ var derived = cm.embind_test_return_smart_polymorphic_multiply_derived_ptr();
+ var name = cm.embind_test_get_class_name_via_smart_polymorphic_base_ptr(derived);
+ assert.equal("PolyBase", name);
+ derived.delete();
+ });
+ });
+
+ BaseFixture.extend("automatic downcasting of return values received from C++", function() {
+ // raw
+ test("non-polymorphic raw pointers are not downcast and do not break automatic casting mechanism", function() {
+ var base = cm.embind_test_return_raw_derived_ptr_as_base();
+ assert.equal("Base", base.getClassName());
+ assert.instanceof(base, cm.Base);
+ base.delete();
+ });
+
+ // raw polymorphic
+ test("polymorphic raw pointer return value is downcast to allocated type (if that is bound)", function() {
+ var derived = cm.embind_test_return_raw_polymorphic_derived_ptr_as_base();
+ assert.instanceof(derived, cm.PolyBase);
+ assert.instanceof(derived, cm.PolyDerived);
+ assert.equal("PolyDerived", derived.getClassName());
+ var siblingDerived = cm.embind_test_return_raw_polymorphic_sibling_derived_ptr_as_base();
+ assert.equal("PolySiblingDerived", siblingDerived.getClassName());
+ siblingDerived.delete();
+ derived.delete();
+ });
+
+ test("polymorphic raw pointer return value is downcast to the most derived bound type", function() {
+ var derivedThrice = cm.embind_test_return_raw_polymorphic_derived_four_times_not_bound_as_base();
+ // if the actual returned type is not bound, then don't assume anything
+ assert.equal("PolyBase", derivedThrice.getClassName());
+ // if we ever fix this, then reverse the assertion
+ //assert.equal("PolyDerivedThrice", derivedThrice.getClassName());
+ derivedThrice.delete();
+ });
+
+ test("polymorphic smart pointer return value is downcast to the most derived type which has an associated smart pointer", function() {
+ var derived = cm.embind_test_return_poly_derived_twice_without_smart_pointer_as_poly_base();
+ // if the actual returned type is not bound, then don't assume anything
+ assert.equal("PolyBase", derived.getClassName());
+ // if we ever fix this, then remove the assertion
+ //assert.equal("PolyDerived", derived.getClassName());
+ derived.delete();
+ });
+
+ test("automatic downcasting works with multiple inheritance", function() {
+ var base = cm.embind_test_return_raw_polymorphic_multiply_derived_ptr_as_base();
+ var secondBase = cm.embind_test_return_raw_polymorphic_multiply_derived_ptr_as_second_base();
+ assert.equal("PolyMultiplyDerived", base.getClassName());
+ // embind does not support multiple inheritance
+ //assert.equal("PolyMultiplyDerived", secondBase.getClassName());
+ secondBase.delete();
+ base.delete();
+ });
+
+ // smart
+ test("non-polymorphic smart pointers do not break automatic casting mechanism", function() {
+ });
+
+ // smart polymorphic
+ test("automatically downcasting a smart pointer does not change the underlying pointer", function() {
+ cm.PolyDerived.setPtrDerived();
+ assert.equal("PolyBase", cm.PolyDerived.getPtrClassName());
+ var derived = cm.PolyDerived.getPtr();
+ assert.equal("PolyDerived", derived.getClassName());
+ assert.equal("PolyBase", cm.PolyDerived.getPtrClassName());
+ derived.delete();
+ cm.PolyDerived.releasePtr();
+ });
+
+ test("polymorphic smart pointer return value is actual allocated type (when bound)", function() {
+ var derived = cm.embind_test_return_smart_polymorphic_derived_ptr_as_base();
+ assert.equal("PolyDerived", derived.getClassName());
+
+ var siblingDerived = cm.embind_test_return_smart_polymorphic_sibling_derived_ptr_as_base();
+ assert.equal("PolySiblingDerived", siblingDerived.getClassName());
+
+ siblingDerived.delete();
+ derived.delete();
+ });
+ });
+
+ BaseFixture.extend("embind", function() {
+ test("value creation", function() {
+ assert.equal(15, cm.emval_test_new_integer());
+ assert.equal("Hello everyone", cm.emval_test_new_string());
+ assert.equal("Hello everyone", cm.emval_test_get_string_from_val({key: "Hello everyone"}));
+
+ var object = cm.emval_test_new_object();
+ assert.equal('bar', object.foo);
+ assert.equal(1, object.baz);
+ });
+
+ test("pass const reference to primitive", function() {
+ assert.equal(3, cm.const_ref_adder(1, 2));
+ });
+
+ test("passthrough", function() {
+ var a = {foo: 'bar'};
+ var b = cm.emval_test_passthrough(a);
+ a.bar = 'baz';
+ assert.equal('baz', b.bar);
+
+ assert.equal(0, cm.count_emval_handles());
+ });
+
+ test("void return converts to undefined", function() {
+ assert.equal(undefined, cm.emval_test_return_void());
+ });
+
+ test("booleans can be marshalled", function() {
+ assert.equal(false, cm.emval_test_not(true));
+ assert.equal(true, cm.emval_test_not(false));
+ });
+
+ test("convert double to unsigned", function() {
+ var rv = cm.emval_test_as_unsigned(1.5);
+ assert.equal('number', typeof rv);
+ assert.equal(1, rv);
+ assert.equal(0, cm.count_emval_handles());
+ });
+
+ test("get length of array", function() {
+ assert.equal(10, cm.emval_test_get_length([0, 1, 2, 3, 4, 5, 'a', 'b', 'c', 'd']));
+ assert.equal(0, cm.count_emval_handles());
+ });
+
+ test("add a bunch of things", function() {
+ assert.equal(66.0, cm.emval_test_add(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11));
+ assert.equal(0, cm.count_emval_handles());
+ });
+
+ test("sum array", function() {
+ assert.equal(66, cm.emval_test_sum([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]));
+ assert.equal(0, cm.count_emval_handles());
+ });
+
+ test("strings", function() {
+ assert.equal("foobar", "foo" + "bar");
+ assert.equal("foobar", cm.emval_test_take_and_return_std_string("foobar"));
+
+ assert.equal("foobar", cm.emval_test_take_and_return_std_string_const_ref("foobar"));
+ });
+
+ test("nuls pass through strings", function() {
+ assert.equal("foo\0bar", cm.emval_test_take_and_return_std_string("foo\0bar"));
+ });
+
+ test("no memory leak when passing strings in by const reference", function() {
+ cm.emval_test_take_and_return_std_string_const_ref("foobar");
+ });
+
+ test("can create new object", function() {
+ assert.deepEqual({}, cm.embind_test_new_Object());
+ });
+
+ test("can invoke constructors with arguments", function() {
+ function constructor(i, s, argument) {
+ this.i = i;
+ this.s = s;
+ this.argument = argument;
+ }
+ constructor.prototype.method = function() {
+ return this.argument;
+ };
+ var x = {};
+ var instance = cm.embind_test_new_factory(constructor, x);
+ assert.equal(10, instance.i);
+ assert.equal("hello", instance.s);
+ assert.equal(x, instance.argument);
+ });
+
+ test("can return module property objects", function() {
+ assert.equal(cm.HEAP8, cm.get_module_property("HEAP8"));
+ });
+
+ test("can return big class instances", function() {
+ var c = cm.embind_test_return_big_class_instance();
+ assert.equal(11, c.member);
+ c.delete();
+ });
+
+ test("can return small class instances", function() {
+ var c = cm.embind_test_return_small_class_instance();
+ assert.equal(7, c.member);
+ c.delete();
+ });
+
+ test("can pass small class instances", function() {
+ var c = new cm.SmallClass();
+ var m = cm.embind_test_accept_small_class_instance(c);
+ assert.equal(7, m);
+ c.delete();
+ });
+
+ test("can pass big class instances", function() {
+ var c = new cm.BigClass();
+ var m = cm.embind_test_accept_big_class_instance(c);
+ assert.equal(11, m);
+ c.delete();
+ });
+
+ test("can get member classes then call its member functions", function() {
+ var p = new cm.ParentClass();
+ var c = p.getBigClass();
+ var m = c.getMember();
+ assert.equal(11, m);
+ c.delete();
+ p.delete();
+ });
+
+ test('C++ -> JS primitive type range checks', function() {
+ // all types should have zero.
+ assert.equal("0", cm.char_to_string(0));
+ assert.equal("0", cm.signed_char_to_string(0));
+ assert.equal("0", cm.unsigned_char_to_string(0));
+ assert.equal("0", cm.short_to_string(0));
+ assert.equal("0", cm.unsigned_short_to_string(0));
+ assert.equal("0", cm.int_to_string(0));
+ assert.equal("0", cm.unsigned_int_to_string(0));
+ assert.equal("0", cm.long_to_string(0));
+ assert.equal("0", cm.unsigned_long_to_string(0));
+
+ // all types should have positive values.
+ assert.equal("5", cm.char_to_string(5));
+ assert.equal("5", cm.signed_char_to_string(5));
+ assert.equal("5", cm.unsigned_char_to_string(5));
+ assert.equal("5", cm.short_to_string(5));
+ assert.equal("5", cm.unsigned_short_to_string(5));
+ assert.equal("5", cm.int_to_string(5));
+ assert.equal("5", cm.unsigned_int_to_string(5));
+ assert.equal("5", cm.long_to_string(5));
+ assert.equal("5", cm.unsigned_long_to_string(5));
+
+ // signed types should have negative values.
+ assert.equal("-5", cm.char_to_string(-5)); // Assuming char as signed.
+ assert.equal("-5", cm.signed_char_to_string(-5));
+ assert.equal("-5", cm.short_to_string(-5));
+ assert.equal("-5", cm.int_to_string(-5));
+ assert.equal("-5", cm.long_to_string(-5));
+
+ // assumptions: char == signed char == 8 bits
+ // unsigned char == 8 bits
+ // short == 16 bits
+ // int == long == 32 bits
+
+ // all types should have their max positive values.
+ assert.equal("127", cm.char_to_string(127));
+ assert.equal("127", cm.signed_char_to_string(127));
+ assert.equal("255", cm.unsigned_char_to_string(255));
+ assert.equal("32767", cm.short_to_string(32767));
+ assert.equal("65535", cm.unsigned_short_to_string(65535));
+ assert.equal("2147483647", cm.int_to_string(2147483647));
+ assert.equal("4294967295", cm.unsigned_int_to_string(4294967295));
+ assert.equal("2147483647", cm.long_to_string(2147483647));
+ assert.equal("4294967295", cm.unsigned_long_to_string(4294967295));
+
+ // signed types should have their min negative values.
+ assert.equal("-128", cm.char_to_string(-128));
+ assert.equal("-128", cm.signed_char_to_string(-128));
+ assert.equal("-32768", cm.short_to_string(-32768));
+ assert.equal("-2147483648", cm.int_to_string(-2147483648));
+ assert.equal("-2147483648", cm.long_to_string(-2147483648));
+
+ // passing out of range values should fail.
+ assert.throws(TypeError, function() { cm.char_to_string(-129); });
+ assert.throws(TypeError, function() { cm.char_to_string(128); });
+ assert.throws(TypeError, function() { cm.signed_char_to_string(-129); });
+ assert.throws(TypeError, function() { cm.signed_char_to_string(128); });
+ assert.throws(TypeError, function() { cm.unsigned_char_to_string(-1); });
+ assert.throws(TypeError, function() { cm.unsigned_char_to_string(256); });
+ assert.throws(TypeError, function() { cm.short_to_string(-32769); });
+ assert.throws(TypeError, function() { cm.short_to_string(32768); });
+ assert.throws(TypeError, function() { cm.unsigned_short_to_string(-1); });
+ assert.throws(TypeError, function() { cm.unsigned_short_to_string(65536); });
+ assert.throws(TypeError, function() { cm.int_to_string(-2147483649); });
+ assert.throws(TypeError, function() { cm.int_to_string(2147483648); });
+ assert.throws(TypeError, function() { cm.unsigned_int_to_string(-1); });
+ assert.throws(TypeError, function() { cm.unsigned_int_to_string(4294967296); });
+ assert.throws(TypeError, function() { cm.long_to_string(-2147483649); });
+ assert.throws(TypeError, function() { cm.long_to_string(2147483648); });
+ assert.throws(TypeError, function() { cm.unsigned_long_to_string(-1); });
+ assert.throws(TypeError, function() { cm.unsigned_long_to_string(4294967296); });
+ });
+
+ test("access multiple class ctors", function() {
+ var a = new cm.MultipleCtors(10);
+ assert.equal(a.WhichCtorCalled(), 1);
+ var b = new cm.MultipleCtors(20, 20);
+ assert.equal(b.WhichCtorCalled(), 2);
+ var c = new cm.MultipleCtors(30, 30, 30);
+ assert.equal(c.WhichCtorCalled(), 3);
+ a.delete();
+ b.delete();
+ c.delete();
+ });
+
+ test("wrong number of constructor arguments throws", function() {
+ assert.throws(cm.BindingError, function() { new cm.MultipleCtors(); });
+ assert.throws(cm.BindingError, function() { new cm.MultipleCtors(1,2,3,4); });
+ });
+
+ test("overloading of free functions", function() {
+ var a = cm.overloaded_function(10);
+ assert.equal(a, 1);
+ var b = cm.overloaded_function(20, 20);
+ assert.equal(b, 2);
+ });
+
+ test("wrong number of arguments to an overloaded free function", function() {
+ assert.throws(cm.BindingError, function() { cm.overloaded_function(); });
+ assert.throws(cm.BindingError, function() { cm.overloaded_function(30, 30, 30); });
+ });
+
+ test("overloading of class member functions", function() {
+ var foo = new cm.MultipleOverloads();
+ assert.equal(foo.Func(10), 1);
+ assert.equal(foo.WhichFuncCalled(), 1);
+ assert.equal(foo.Func(20, 20), 2);
+ assert.equal(foo.WhichFuncCalled(), 2);
+ foo.delete();
+ });
+
+ test("wrong number of arguments to an overloaded class member function", function() {
+ var foo = new cm.MultipleOverloads();
+ assert.throws(cm.BindingError, function() { foo.Func(); });
+ assert.throws(cm.BindingError, function() { foo.Func(30, 30, 30); });
+ foo.delete();
+ });
+
+ test("wrong number of arguments to an overloaded class static function", function() {
+ assert.throws(cm.BindingError, function() { cm.MultipleOverloads.StaticFunc(); });
+ assert.throws(cm.BindingError, function() { cm.MultipleOverloads.StaticFunc(30, 30, 30); });
+ });
+
+ test("overloading of derived class member functions", function() {
+ var foo = new cm.MultipleOverloadsDerived();
+
+ // NOTE: In C++, default lookup rules will hide overloads from base class if derived class creates them.
+ // In JS, we make the base class overloads implicitly available. In C++, they would need to be explicitly
+ // invoked, like foo.MultipleOverloads::Func(10);
+ assert.equal(foo.Func(10), 1);
+ assert.equal(foo.WhichFuncCalled(), 1);
+ assert.equal(foo.Func(20, 20), 2);
+ assert.equal(foo.WhichFuncCalled(), 2);
+
+ assert.equal(foo.Func(30, 30, 30), 3);
+ assert.equal(foo.WhichFuncCalled(), 3);
+ assert.equal(foo.Func(40, 40, 40, 40), 4);
+ assert.equal(foo.WhichFuncCalled(), 4);
+ foo.delete();
+ });
+
+ test("overloading of class static functions", function() {
+ assert.equal(cm.MultipleOverloads.StaticFunc(10), 1);
+ assert.equal(cm.MultipleOverloads.WhichStaticFuncCalled(), 1);
+ assert.equal(cm.MultipleOverloads.StaticFunc(20, 20), 2);
+ assert.equal(cm.MultipleOverloads.WhichStaticFuncCalled(), 2);
+ });
+
+ test("overloading of derived class static functions", function() {
+ assert.equal(cm.MultipleOverloadsDerived.StaticFunc(30, 30, 30), 3);
+ // TODO: Cannot access static member functions of a Base class via Derived.
+// assert.equal(cm.MultipleOverloadsDerived.WhichStaticFuncCalled(), 3);
+ assert.equal(cm.MultipleOverloads.WhichStaticFuncCalled(), 3);
+ assert.equal(cm.MultipleOverloadsDerived.StaticFunc(40, 40, 40, 40), 4);
+ // TODO: Cannot access static member functions of a Base class via Derived.
+// assert.equal(cm.MultipleOverloadsDerived.WhichStaticFuncCalled(), 4);
+ assert.equal(cm.MultipleOverloads.WhichStaticFuncCalled(), 4);
+ });
+/*
+ test("can get templated member classes then call its member functions", function() {
+ var p = new cm.ContainsTemplatedMemberClass();
+ var c = p.getTestTemplate();
+ var m = c.getMember(1);
+ assert.equal(87, m);
+ c.delete();
+ p.delete();
+ });
+*/
+ });
+
+ BaseFixture.extend("vector", function() {
+ test("std::vector returns as an native object", function() {
+ var vec = cm.emval_test_return_vector();
+
+ assert.equal(3, vec.size());
+ assert.equal(10, vec.get(0));
+ assert.equal(20, vec.get(1));
+ assert.equal(30, vec.get(2));
+ vec.delete();
+ });
+
+ test("out of bounds std::vector access returns undefined", function() {
+ var vec = cm.emval_test_return_vector();
+
+ assert.throws(TypeError, function() { vec.get(-1); });
+ assert.equal(undefined, vec.get(4));
+
+ vec.delete();
+ });
+
+ test("std::vector<std::shared_ptr<>> can be passed back", function() {
+ var vec = cm.emval_test_return_shared_ptr_vector();
+
+ assert.equal(2, vec.size());
+ var str0 = vec.get(0);
+ var str1 = vec.get(1);
+
+ assert.equal('string #1', str0.get());
+ assert.equal('string #2', str1.get());
+ str0.delete();
+ str1.delete();
+
+ vec.delete();
+ });
+
+ test("objects can be pushed back", function() {
+ var vectorHolder = new cm.VectorHolder();
+ var vec = vectorHolder.get();
+ assert.equal(2, vec.size());
+
+ var str = new cm.StringHolder('abc');
+ vec.push_back(str);
+ str.delete();
+ assert.equal(3, vec.size());
+ var str = vec.get(2);
+ assert.equal('abc', str.get());
+
+ str.delete();
+ vec.delete();
+ vectorHolder.delete();
+ });
+
+ test("can get elements with array operator", function(){
+ var vec = cm.emval_test_return_vector();
+ assert.equal(10, vec.get(0));
+ vec.delete();
+ });
+
+ test("can set elements with array operator", function() {
+ var vec = cm.emval_test_return_vector();
+ assert.equal(10, vec.get(0));
+ vec.set(2, 60);
+ assert.equal(60, vec.get(2));
+ vec.delete();
+ });
+
+ test("can set and get objects", function() {
+ var vec = cm.emval_test_return_shared_ptr_vector();
+ var str = vec.get(0);
+ assert.equal('string #1', str.get());
+ str.delete();
+ vec.delete();
+ });
+ });
+
+ BaseFixture.extend("map", function() {
+ test("std::map returns as native object", function() {
+ var map = cm.embind_test_get_string_int_map();
+
+ assert.equal(2, map.size());
+ assert.equal(1, map.get("one"));
+ assert.equal(2, map.get("two"));
+
+ map.delete();
+ });
+
+ test("std::map can set keys and values", function() {
+ var map = cm.embind_test_get_string_int_map();
+
+ assert.equal(2, map.size());
+
+ map.set("three", 3);
+
+ assert.equal(3, map.size());
+ assert.equal(3, map.get("three"));
+
+ map.set("three", 4);
+
+ assert.equal(3, map.size());
+ assert.equal(4, map.get("three"));
+
+ map.delete();
+ });
+ });
+
+ BaseFixture.extend("functors", function() {
+ test("can get and call function ptrs", function() {
+ var ptr = cm.emval_test_get_function_ptr();
+ assert.equal("foobar", ptr.opcall("foobar"));
+ ptr.delete();
+ });
+
+ test("can pass functor to C++", function() {
+ var ptr = cm.emval_test_get_function_ptr();
+ assert.equal("asdf", cm.emval_test_take_and_call_functor(ptr));
+ ptr.delete();
+ });
+
+ test("can clone handles", function() {
+ var a = cm.emval_test_get_function_ptr();
+ assert.equal(1, a.$$.count.value);
+ var b = a.clone();
+ assert.equal(2, a.$$.count.value);
+ assert.equal(2, b.$$.count.value);
+ a.delete();
+
+ assert.throws(cm.BindingError, function() {
+ a.delete();
+ });
+ b.delete();
+ });
+ });
+
+ BaseFixture.extend("classes", function() {
+ test("class instance", function() {
+ var a = {foo: 'bar'};
+ assert.equal(0, cm.count_emval_handles());
+ var c = new cm.ValHolder(a);
+ assert.equal(1, cm.count_emval_handles());
+ assert.equal('bar', c.getVal().foo);
+ assert.equal(1, cm.count_emval_handles());
+
+ c.setVal('1234');
+ assert.equal('1234', c.getVal());
+
+ c.delete();
+ assert.equal(0, cm.count_emval_handles());
+ });
+
+ test("class instance $$ property is non-enumerable", function() {
+ var c = new cm.ValHolder(undefined);
+ assert.deepEqual([], Object.keys(c));
+ var d = c.clone();
+ c.delete();
+
+ assert.deepEqual([], Object.keys(d));
+ d.delete();
+ });
+
+ test("class methods", function() {
+ assert.equal(10, cm.ValHolder.some_class_method(10));
+
+ var b = cm.ValHolder.makeValHolder("foo");
+ assert.equal("foo", b.getVal());
+ b.delete();
+ });
+
+ test("can't call methods on deleted class instances", function() {
+ var c = new cm.ValHolder(undefined);
+ c.delete();
+ assert.throws(cm.BindingError, function() {
+ c.getVal();
+ });
+ assert.throws(cm.BindingError, function() {
+ c.delete();
+ });
+ });
+
+ test("calling constructor without new raises BindingError", function() {
+ var e = assert.throws(cm.BindingError, function() {
+ cm.ValHolder(undefined);
+ });
+ assert.equal("Use 'new' to construct ValHolder", e.message);
+ });
+
+ test("can return class instances by value", function() {
+ var c = cm.emval_test_return_ValHolder();
+ assert.deepEqual({}, c.getVal());
+ c.delete();
+ });
+
+ test("can pass class instances to functions by reference", function() {
+ var a = {a:1};
+ var c = new cm.ValHolder(a);
+ cm.emval_test_set_ValHolder_to_empty_object(c);
+ assert.deepEqual({}, c.getVal());
+ c.delete();
+ });
+
+ test("can pass smart pointer by reference", function() {
+ var base = cm.embind_test_return_smart_base_ptr();
+ var name = cm.embind_test_get_class_name_via_reference_to_smart_base_ptr(base);
+ assert.equal("Base", name);
+ base.delete();
+ });
+
+ test("can pass smart pointer by value", function() {
+ var base = cm.embind_test_return_smart_base_ptr();
+ var name = cm.embind_test_get_class_name_via_smart_base_ptr(base);
+ assert.equal("Base", name);
+ base.delete();
+ });
+
+ // todo: fix this
+ // This test does not work because we make no provision for argument values
+ // having been changed after returning from a C++ routine invocation. In
+ // this specific case, the original pointee of the smart pointer was
+ // freed and replaced by a new one, but the ptr in our local handle
+ // was never updated after returning from the call.
+ test("can modify smart pointers passed by reference", function() {
+// var base = cm.embind_test_return_smart_base_ptr();
+// cm.embind_modify_smart_pointer_passed_by_reference(base);
+// assert.equal("Changed", base.getClassName());
+// base.delete();
+ });
+
+ test("can not modify smart pointers passed by value", function() {
+ var base = cm.embind_test_return_smart_base_ptr();
+ cm.embind_attempt_to_modify_smart_pointer_when_passed_by_value(base);
+ assert.equal("Base", base.getClassName());
+ base.delete();
+ });
+
+ test("const return value", function() {
+ var c = new cm.ValHolder("foo");
+ assert.equal("foo", c.getConstVal());
+ c.delete();
+ });
+
+ test("return object by const ref", function() {
+ var c = new cm.ValHolder("foo");
+ assert.equal("foo", c.getValConstRef());
+ c.delete();
+ });
+
+ test("instanceof", function() {
+ var c = new cm.ValHolder("foo");
+ assert.instanceof(c, cm.ValHolder);
+ c.delete();
+ });
+
+ test("can access struct fields", function() {
+ var c = new cm.CustomStruct();
+ assert.equal(10, c.field);
+ assert.equal(10, c.getField());
+ c.delete();
+ });
+
+ test("can set struct fields", function() {
+ var c = new cm.CustomStruct();
+ c.field = 15;
+ assert.equal(15, c.field);
+ c.delete();
+ });
+
+ test("assignment returns value", function() {
+ var c = new cm.CustomStruct();
+ assert.equal(15, c.field = 15);
+ c.delete();
+ });
+
+ test("assigning string to integer raises TypeError", function() {
+ var c = new cm.CustomStruct();
+
+ var e = assert.throws(TypeError, function() {
+ c.field = "hi";
+ });
+ assert.equal('Cannot convert "hi" to int', e.message);
+
+ var e = assert.throws(TypeError, function() {
+ c.field = {foo:'bar'};
+ });
+ assert.equal('Cannot convert "[object Object]" to int', e.message);
+
+ c.delete();
+ });
+
+ test("can return tuples by value", function() {
+ var c = cm.emval_test_return_TupleVector();
+ assert.deepEqual([1, 2, 3, 4], c);
+ });
+
+ test("tuples can contain tuples", function() {
+ var c = cm.emval_test_return_TupleVectorTuple();
+ assert.deepEqual([[1, 2, 3, 4]], c);
+ });
+
+ test("can pass tuples by value", function() {
+ var c = cm.emval_test_take_and_return_TupleVector([4, 5, 6, 7]);
+ assert.deepEqual([4, 5, 6, 7], c);
+ });
+
+ test("can return structs by value", function() {
+ var c = cm.emval_test_return_StructVector();
+ assert.deepEqual({x: 1, y: 2, z: 3, w: 4}, c);
+ });
+
+ test("can pass structs by value", function() {
+ var c = cm.emval_test_take_and_return_StructVector({x: 4, y: 5, z: 6, w: 7});
+ assert.deepEqual({x: 4, y: 5, z: 6, w: 7}, c);
+ });
+
+ test("can pass and return tuples in structs", function() {
+ var d = cm.emval_test_take_and_return_TupleInStruct({field: [1, 2, 3, 4]});
+ assert.deepEqual({field: [1, 2, 3, 4]}, d);
+ });
+
+ test("can clone handles", function() {
+ var a = new cm.ValHolder({});
+ assert.equal(1, cm.count_emval_handles());
+ var b = a.clone();
+ a.delete();
+
+ assert.equal(1, cm.count_emval_handles());
+
+ assert.throws(cm.BindingError, function() {
+ a.delete();
+ });
+ b.delete();
+
+ assert.equal(0, cm.count_emval_handles());
+ });
+
+ test("A shared pointer set/get point to the same underlying pointer", function() {
+ var a = new cm.SharedPtrHolder();
+ var b = a.get();
+
+ a.set(b);
+ var c = a.get();
+
+ assert.equal(b.$$.ptr, c.$$.ptr);
+ b.delete();
+ c.delete();
+ a.delete();
+ });
+
+ test("can return shared ptrs from instance methods", function() {
+ var a = new cm.SharedPtrHolder();
+
+ // returns the shared_ptr.
+ var b = a.get();
+
+ assert.equal("a string", b.get());
+ b.delete();
+ a.delete();
+ });
+
+ test("smart ptrs clone correctly", function() {
+ assert.equal(0, cm.count_emval_handles());
+
+ var a = cm.emval_test_return_shared_ptr();
+
+ var b = a.clone();
+ a.delete();
+
+ assert.equal(1, cm.count_emval_handles());
+
+ assert.throws(cm.BindingError, function() {
+ a.delete();
+ });
+ b.delete();
+
+ assert.equal(0, cm.count_emval_handles());
+ });
+
+ test("can't clone if already deleted", function() {
+ var a = new cm.ValHolder({});
+ a.delete();
+ assert.throws(cm.BindingError, function() {
+ a.clone();
+ });
+ });
+
+ test("virtual calls work correctly", function() {
+ var derived = cm.embind_test_return_raw_polymorphic_derived_ptr_as_base();
+ assert.equal("PolyDerived", derived.virtualGetClassName());
+ derived.delete();
+ });
+
+ test("virtual calls work correctly on smart ptrs", function() {
+ var derived = cm.embind_test_return_smart_polymorphic_derived_ptr_as_base();
+ assert.equal("PolyDerived", derived.virtualGetClassName());
+ derived.delete();
+ });
+
+ test("Empty smart ptr is null", function() {
+ var a = cm.emval_test_return_empty_shared_ptr();
+ assert.equal(null, a);
+ });
+
+ test("string cannot be given as smart pointer argument", function() {
+ assert.throws(cm.BindingError, function() {
+ cm.emval_test_is_shared_ptr_null("hello world");
+ });
+ });
+
+ test("number cannot be given as smart pointer argument", function() {
+ assert.throws(cm.BindingError, function() {
+ cm.emval_test_is_shared_ptr_null(105);
+ });
+ });
+
+ test("raw pointer cannot be given as smart pointer argument", function() {
+ var p = new cm.ValHolder({});
+ assert.throws(cm.BindingError, function() { cm.emval_test_is_shared_ptr_null(p); });
+ p.delete();
+ });
+
+ test("null is passed as empty smart pointer", function() {
+ assert.true(cm.emval_test_is_shared_ptr_null(null));
+ });
+
+ test("Deleting already deleted smart ptrs fails", function() {
+ var a = cm.emval_test_return_shared_ptr();
+ a.delete();
+ assert.throws(cm.BindingError, function() {
+ a.delete();
+ });
+ });
+
+ test("StringHolder", function() {
+ var a = new cm.StringHolder("foobar");
+ assert.equal("foobar", a.get());
+
+ a.set("barfoo");
+ assert.equal("barfoo", a.get());
+
+ assert.equal("barfoo", a.get_const_ref());
+
+ a.delete();
+ });
+
+ test("can call methods on unique ptr", function() {
+ var result = cm.emval_test_return_unique_ptr();
+
+ result.setVal('1234');
+ assert.equal('1234', result.getVal());
+ result.delete();
+ });
+
+ test("can call methods on shared ptr", function(){
+ var result = cm.emval_test_return_shared_ptr();
+ result.setVal('1234');
+
+ assert.equal('1234', result.getVal());
+ result.delete();
+ });
+
+ test("Non functors throw exception", function() {
+ var a = {foo: 'bar'};
+ var c = new cm.ValHolder(a);
+ assert.throws(TypeError, function() {
+ c();
+ });
+ c.delete();
+ });
+
+ test("non-member methods", function() {
+ var a = {foo: 'bar'};
+ var c = new cm.ValHolder(a);
+ c.setEmpty(); // non-member method
+ assert.deepEqual({}, c.getValNonMember());
+ c.delete();
+ });
+
+ test("instantiating class without constructor gives error", function() {
+ var e = assert.throws(cm.BindingError, function() {
+ cm.AbstractClass();
+ });
+ assert.equal("Use 'new' to construct AbstractClass", e.message);
+
+ var e = assert.throws(cm.BindingError, function() {
+ new cm.AbstractClass();
+ });
+ assert.equal("AbstractClass has no accessible constructor", e.message);
+ });
+
+ test("can construct class with external constructor", function() {
+ var e = new cm.HasExternalConstructor("foo");
+ assert.instanceof(e, cm.HasExternalConstructor);
+ assert.equal("foo", e.getString());
+ e.delete();
+ });
+ });
+
+ BaseFixture.extend("const", function() {
+ test("calling non-const method with const handle is error", function() {
+ var vh = cm.ValHolder.makeConst({});
+ var e = assert.throws(cm.BindingError, function() {
+ vh.setVal({});
+ });
+ assert.equal('Cannot convert argument of type ValHolder const* to parameter type ValHolder*', e.message);
+ vh.delete();
+ });
+
+ test("passing const pointer to non-const pointer is error", function() {
+ var vh = new cm.ValHolder.makeConst({});
+ var e = assert.throws(cm.BindingError, function() {
+ cm.ValHolder.set_via_raw_pointer(vh, {});
+ });
+ assert.equal('Cannot convert argument of type ValHolder const* to parameter type ValHolder*', e.message);
+ vh.delete();
+ });
+ });
+
+ BaseFixture.extend("smart pointers", function() {
+ test("constructor can return smart pointer", function() {
+ var e = new cm.HeldBySmartPtr(10, "foo");
+ assert.instanceof(e, cm.HeldBySmartPtr);
+ assert.equal(10, e.i);
+ assert.equal("foo", e.s);
+ var f = cm.takesHeldBySmartPtr(e);
+ f.delete();
+ e.delete();
+ });
+
+ test("cannot pass incorrect smart pointer type", function() {
+ var e = cm.emval_test_return_shared_ptr();
+ assert.throws(cm.BindingError, function() {
+ cm.takesHeldBySmartPtr(e);
+ });
+ e.delete();
+ });
+
+ test("smart pointer object has no object keys", function() {
+ var e = new cm.HeldBySmartPtr(10, "foo");
+ assert.deepEqual([], Object.keys(e));
+
+ var f = e.clone();
+ e.delete();
+
+ assert.deepEqual([], Object.keys(f));
+ f.delete();
+ });
+
+ test("smart pointer object has correct constructor name", function() {
+ var e = new cm.HeldBySmartPtr(10, "foo");
+ assert.equal('HeldBySmartPtr', e.constructor.name);
+ e.delete();
+ });
+
+ test("constructor can return smart pointer", function() {
+ var e = new cm.HeldBySmartPtr(10, "foo");
+ assert.instanceof(e, cm.HeldBySmartPtr);
+ assert.equal(10, e.i);
+ assert.equal("foo", e.s);
+ var f = cm.takesHeldBySmartPtr(e);
+ assert.instanceof(f, cm.HeldBySmartPtr);
+ f.delete();
+ e.delete();
+ });
+
+ test("custom smart pointer", function() {
+ var e = new cm.HeldByCustomSmartPtr(20, "bar");
+ assert.instanceof(e, cm.HeldByCustomSmartPtr);
+ assert.equal(20, e.i);
+ assert.equal("bar", e.s);
+ e.delete();
+ });
+
+ test("custom smart pointer passed through wiretype", function() {
+ var e = new cm.HeldByCustomSmartPtr(20, "bar");
+ var f = cm.passThroughCustomSmartPtr(e);
+ e.delete();
+
+ assert.instanceof(f, cm.HeldByCustomSmartPtr);
+ assert.equal(20, f.i);
+ assert.equal("bar", f.s);
+
+ f.delete();
+ });
+
+ test("cannot give null to by-value argument", function() {
+ var e = assert.throws(cm.BindingError, function() {
+ cm.takesHeldBySmartPtr(null);
+ });
+ assert.equal('null is not a valid HeldBySmartPtr', e.message);
+ });
+
+ test("raw pointer can take and give null", function() {
+ assert.equal(null, cm.passThroughRawPtr(null));
+ });
+
+ test("custom smart pointer can take and give null", function() {
+ assert.equal(null, cm.passThroughCustomSmartPtr(null));
+ });
+
+ test("cannot pass shared_ptr to CustomSmartPtr", function() {
+ var o = cm.HeldByCustomSmartPtr.createSharedPtr(10, "foo");
+ var e = assert.throws(cm.BindingError, function() {
+ cm.passThroughCustomSmartPtr(o);
+ });
+ assert.equal('Cannot convert argument of type NSt3__110shared_ptrI20HeldByCustomSmartPtrEE to parameter type 14CustomSmartPtrI20HeldByCustomSmartPtrE', e.message);
+ o.delete();
+ });
+
+ test("custom smart pointers can be passed to shared_ptr parameter", function() {
+ var e = cm.HeldBySmartPtr.newCustomPtr(10, "abc");
+ assert.equal(10, e.i);
+ assert.equal("abc", e.s);
+
+ cm.takesHeldBySmartPtrSharedPtr(e).delete();
+ e.delete();
+ });
+
+ test("can call non-member functions as methods", function() {
+ var e = new cm.HeldBySmartPtr(20, "bar");
+ var f = e.returnThis();
+ e.delete();
+ assert.equal(20, f.i);
+ assert.equal("bar", f.s);
+ f.delete();
+ });
+ });
+
+ BaseFixture.extend("enumerations", function() {
+ test("can compare enumeration values", function() {
+ assert.equal(cm.Enum.ONE, cm.Enum.ONE);
+ assert.notEqual(cm.Enum.ONE, cm.Enum.TWO);
+ });
+
+ if (typeof INVOKED_FROM_EMSCRIPTEN_TEST_RUNNER === "undefined") { // TODO: Enable this to work in Emscripten runner as well!
+ test("repr includes enum value", function() {
+ assert.equal('<#Enum_ONE {}>', IMVU.repr(cm.Enum.ONE));
+ assert.equal('<#Enum_TWO {}>', IMVU.repr(cm.Enum.TWO));
+ });
+ }
+
+ test("instanceof", function() {
+ assert.instanceof(cm.Enum.ONE, cm.Enum);
+ });
+
+ test("can pass and return enumeration values to functions", function() {
+ assert.equal(cm.Enum.TWO, cm.emval_test_take_and_return_Enum(cm.Enum.TWO));
+ });
+ });
+
+ BaseFixture.extend("C++11 enum class", function() {
+ test("can compare enumeration values", function() {
+ assert.equal(cm.EnumClass.ONE, cm.EnumClass.ONE);
+ assert.notEqual(cm.EnumClass.ONE, cm.EnumClass.TWO);
+ });
+
+ if (typeof INVOKED_FROM_EMSCRIPTEN_TEST_RUNNER === "undefined") { // TODO: Enable this to work in Emscripten runner as well!
+ test("repr includes enum value", function() {
+ assert.equal('<#EnumClass_ONE {}>', IMVU.repr(cm.EnumClass.ONE));
+ assert.equal('<#EnumClass_TWO {}>', IMVU.repr(cm.EnumClass.TWO));
+ });
+ }
+
+ test("instanceof", function() {
+ assert.instanceof(cm.EnumClass.ONE, cm.EnumClass);
+ });
+
+ test("can pass and return enumeration values to functions", function() {
+ assert.equal(cm.EnumClass.TWO, cm.emval_test_take_and_return_EnumClass(cm.EnumClass.TWO));
+ });
+ });
+
+ BaseFixture.extend("emval call tests", function() {
+ test("can call functions from C++", function() {
+ var called = false;
+ cm.emval_test_call_function(function(i, f, tv, sv) {
+ called = true;
+ assert.equal(10, i);
+ assert.equal(1.5, f);
+ assert.deepEqual([1.25, 2.5, 3.75, 4], tv);
+ assert.deepEqual({x: 1.25, y: 2.5, z: 3.75, w:4}, sv);
+ }, 10, 1.5, [1.25, 2.5, 3.75, 4], {x: 1.25, y: 2.5, z: 3.75, w:4});
+ assert.true(called);
+ });
+ });
+
+ BaseFixture.extend("extending built-in classes", function() {
+ // cm.ValHolder.prototype.patched = 10; // this sets instanceCounts.patched inside of Deletable module !?!
+
+ test("can access patched value on new instances", function() {
+ cm.ValHolder.prototype.patched = 10;
+ var c = new cm.ValHolder(undefined);
+ assert.equal(10, c.patched);
+ c.delete();
+ cm.ValHolder.prototype.patched = undefined;
+ });
+
+ test("can access patched value on returned instances", function() {
+ cm.ValHolder.prototype.patched = 10;
+ var c = cm.emval_test_return_ValHolder();
+ assert.equal(10, c.patched);
+ c.delete();
+ cm.ValHolder.prototype.patched = undefined;
+ });
+ });
+
+ BaseFixture.extend("raw pointers", function() {
+ test("can pass raw pointers into functions if explicitly allowed", function() {
+ var vh = new cm.ValHolder({});
+ cm.ValHolder.set_via_raw_pointer(vh, 10);
+ assert.equal(10, cm.ValHolder.get_via_raw_pointer(vh));
+ vh.delete();
+ });
+
+ test("can return raw pointers from functions if explicitly allowed", function() {
+ var p = cm.embind_test_return_raw_base_ptr();
+ assert.equal("Base", p.getClassName());
+ p.delete();
+ });
+
+ test("can pass multiple raw pointers to functions", function() {
+ var target = new cm.ValHolder(undefined);
+ var source = new cm.ValHolder("hi");
+ cm.ValHolder.transfer_via_raw_pointer(target, source);
+ assert.equal("hi", target.getVal());
+ target.delete();
+ source.delete();
+ });
+ });
+
+ BaseFixture.extend("JavaScript interface", function() {
+ this.setUp(function() {
+ this.testobj = {
+ "method1": function() { return 111; },
+ "method2": function() { return 222; }
+ };
+ });
+
+ test("pass js object to c++ and call its method", function() {
+ var obj = new cm.JSInterfaceHolder(this.testobj);
+ assert.equal(111, obj.callMethod("method1"));
+ assert.equal(222, obj.callMethod("method2"));
+ assert.equal(111, obj.callMethodUsingSharedPtr("method1"));
+ assert.equal(222, obj.callMethodUsingSharedPtr("method2"));
+ obj.delete();
+ });
+ });
+
+ BaseFixture.extend("abstract methods", function() {
+ test("can call abstract methods", function() {
+ var obj = cm.getAbstractClass();
+ assert.equal("from concrete", obj.abstractMethod());
+ obj.delete();
+ });
+
+ test("can implement abstract methods in JavaScript", function() {
+ var expected = "my JS string";
+ function MyImplementation() {
+ this.rv = expected;
+ }
+ MyImplementation.prototype.abstractMethod = function() {
+ return this.rv;
+ };
+
+ var impl = cm.AbstractClass.implement(new MyImplementation);
+ assert.equal(expected, impl.abstractMethod());
+ assert.equal(expected, cm.callAbstractMethod(impl));
+ impl.delete();
+ });
+ });
+
+ BaseFixture.extend("registration order", function() {
+ test("registration of tuple elements out of order leaves them in order", function() {
+ var ot = cm.getOrderedTuple();
+ assert.instanceof(ot[0], cm.FirstElement);
+ assert.instanceof(ot[1], cm.SecondElement);
+ ot[0].delete();
+ ot[1].delete();
+ });
+
+ test("registration of struct elements out of order", function() {
+ var os = cm.getOrderedStruct();
+ assert.instanceof(os.first, cm.FirstElement);
+ assert.instanceof(os.second, cm.SecondElement);
+ os.first.delete();
+ os.second.delete();
+ });
+ });
+
+ if (typeof INVOKED_FROM_EMSCRIPTEN_TEST_RUNNER === "undefined") { // TODO: Enable this to work in Emscripten runner as well!
+
+ BaseFixture.extend("unbound types", function() {
+ function assertMessage(fn, message) {
+ var e = assert.throws(cm.UnboundTypeError, fn);
+ assert.equal(message, e.message);
+ }
+
+ test("calling function with unbound types produces error", function() {
+ assertMessage(
+ function() {
+ cm.getUnboundClass();
+ },
+ 'Cannot call getUnboundClass due to unbound types: 12UnboundClass');
+ });
+
+ test("unbound base class produces error", function() {
+ assertMessage(
+ function() {
+ cm.getHasUnboundBase();
+ },
+ 'Cannot call getHasUnboundBase due to unbound types: 12UnboundClass');
+ });
+
+ test("construct of class with unbound base", function() {
+ assertMessage(
+ function() {
+ new cm.HasUnboundBase;
+ }, 'Cannot construct HasUnboundBase due to unbound types: 12UnboundClass');
+ });
+
+ test("unbound constructor argument", function() {
+ assertMessage(
+ function() {
+ new cm.HasConstructorUsingUnboundArgument(1);
+ },
+ 'Cannot construct HasConstructorUsingUnboundArgument due to unbound types: 12UnboundClass');
+ });
+
+ test("unbound constructor argument of class with unbound base", function() {
+ assertMessage(
+ function() {
+ new cm.HasConstructorUsingUnboundArgumentAndUnboundBase;
+ },
+ 'Cannot construct HasConstructorUsingUnboundArgumentAndUnboundBase due to unbound types: 18SecondUnboundClass');
+ });
+
+ test('class function with unbound argument', function() {
+ var x = new cm.BoundClass;
+ assertMessage(
+ function() {
+ x.method();
+ }, 'Cannot call BoundClass.method due to unbound types: 12UnboundClass');
+ x.delete();
+ });
+
+ test('class class function with unbound argument', function() {
+ assertMessage(
+ function() {
+ cm.BoundClass.classfunction();
+ }, 'Cannot call BoundClass.classfunction due to unbound types: 12UnboundClass');
+ });
+
+ test('class property of unbound type', function() {
+ var x = new cm.BoundClass;
+ var y;
+ assertMessage(
+ function() {
+ y = x.property;
+ }, 'Cannot access BoundClass.property due to unbound types: 12UnboundClass');
+ assertMessage(
+ function() {
+ x.property = 10;
+ }, 'Cannot access BoundClass.property due to unbound types: 12UnboundClass');
+ x.delete();
+ });
+
+ // todo: tuple elements
+ // todo: tuple element accessors
+ // todo: struct fields
+ });
+
+ }
+
+ BaseFixture.extend("noncopyable", function() {
+ test('can call method on noncopyable object', function() {
+ var x = new cm.Noncopyable;
+ assert.equal('foo', x.method());
+ x.delete();
+ });
+ });
+
+ BaseFixture.extend("function names", function() {
+ assert.equal('ValHolder', cm.ValHolder.name);
+ assert.equal('ValHolder$setVal', cm.ValHolder.prototype.setVal.name);
+ assert.equal('ValHolder$makeConst', cm.ValHolder.makeConst.name);
+ });
+
+ BaseFixture.extend("constants", function() {
+ assert.equal(10, cm.INT_CONSTANT);
+ assert.equal("some string", cm.STRING_CONSTANT);
+ assert.deepEqual([1, 2, 3, 4], cm.VALUE_TUPLE_CONSTANT);
+ assert.deepEqual({x:1,y:2,z:3,w:4}, cm.VALUE_STRUCT_CONSTANT);
+ });
+});
+
+/* global run_all_tests */
+// If running as part of the emscripten test runner suite, and not as part of the IMVU suite,
+// we launch the test execution from here. IMVU suite uses its own dedicated mechanism instead of this.
+if (typeof run_all_tests !== "undefined")
+ run_all_tests();
diff --git a/tests/embind/embind_test.cpp b/tests/embind/embind_test.cpp
index dc052d1a..1384406a 100644
--- a/tests/embind/embind_test.cpp
+++ b/tests/embind/embind_test.cpp
@@ -1,5 +1,6 @@
#include <string>
#include <malloc.h>
+#include <functional>
#include <emscripten/bind.h>
using namespace emscripten;
@@ -27,6 +28,10 @@ val emval_test_new_string() {
return val("Hello everyone");
}
+std::string emval_test_get_string_from_val(val v) {
+ return v["key"].as<std::string>();
+}
+
val emval_test_new_object() {
val rv(val::object());
rv.set("foo", val("bar"));
@@ -54,18 +59,22 @@ unsigned emval_test_as_unsigned(val v) {
}
unsigned emval_test_get_length(val v) {
- return v.get("length").as<unsigned>();
+ return v["length"].as<unsigned>();
}
double emval_test_add(char c, signed char sc, unsigned char uc, signed short ss, unsigned short us, signed int si, unsigned int ui, signed long sl, unsigned long ul, float f, double d) {
return c + sc + uc + ss + us + si + ui + sl + ul + f + d;
}
+float const_ref_adder(const int& i, const float& f) {
+ return i + f;
+}
+
unsigned emval_test_sum(val v) {
- unsigned length = v.get("length").as<unsigned>();
+ unsigned length = v["length"].as<unsigned>();
double rv = 0;
for (unsigned i = 0; i < length; ++i) {
- rv += v.get(i).as<double>();
+ rv += v[i].as<double>();
}
return rv;
}
@@ -82,30 +91,70 @@ std::string emval_test_take_and_return_std_string_const_ref(const std::string& s
return str;
}
+std::function<std::string (std::string)> emval_test_get_function_ptr() {
+ return emval_test_take_and_return_std_string;
+}
+
+std::string emval_test_take_and_call_functor(std::function<std::string(std::string)> func) {
+ return func("asdf");
+}
+
class ValHolder {
public:
ValHolder(val v)
- : v(v)
+ : v_(v)
{}
val getVal() const {
- return v;
+ return v_;
}
- void setVal(val v) {
- this->v = v;
+ val getValNonConst() {
+ return v_;
}
- int returnIntPlusFive( int x ) {
- return x + 5;
+ const val getConstVal() const {
+ return v_;
+ }
+
+ const val& getValConstRef() const {
+ return v_;
+ }
+
+ void setVal(val v) {
+ this->v_ = v;
}
static int some_class_method(int i) {
return i;
}
+ static const ValHolder* makeConst(val v) {
+ return new ValHolder(v);
+ }
+
+ static ValHolder makeValHolder(val v) {
+ return ValHolder(v);
+ }
+
+ static void set_via_raw_pointer(ValHolder* vh, val v) {
+ vh->setVal(v);
+ }
+
+ static val get_via_raw_pointer(const ValHolder* vh) {
+ return vh->getVal();
+ }
+
+ static void transfer_via_raw_pointer(ValHolder* target, const ValHolder* source) {
+ target->setVal(source->getVal());
+ }
+
+ static val getValNonMember(const ValHolder& target) {
+ return target.getVal();
+ }
+
private:
- val v;
+ val v_;
};
ValHolder emval_test_return_ValHolder() {
@@ -119,42 +168,614 @@ void emval_test_set_ValHolder_to_empty_object(ValHolder& vh) {
class StringHolder {
public:
StringHolder(const std::string& s)
- : str(s)
+ : str_(s)
{}
void set(const std::string& s) {
- str = s;
+ str_ = s;
}
+
std::string get() const {
- return str;
+ return str_;
+ }
+
+ std::string& get_ref() {
+ return str_;
+ }
+
+ const std::string& get_const_ref() const {
+ return str_;
+ }
+
+private:
+ std::string str_;
+};
+
+class SharedPtrHolder {
+public:
+ SharedPtrHolder()
+ : ptr_(new StringHolder("a string"))
+ {}
+
+ std::shared_ptr<StringHolder> get() const {
+ return ptr_;
+ }
+
+ void set(std::shared_ptr<StringHolder> p) {
+ ptr_ = p;
+ }
+private:
+ std::shared_ptr<StringHolder> ptr_;
+};
+
+class VectorHolder {
+public:
+ VectorHolder() {
+ v_.push_back(StringHolder("string #1"));
+ v_.push_back(StringHolder("string #2"));
+ }
+
+ std::vector<StringHolder> get() const {
+ return v_;
+ }
+
+ void set(std::vector<StringHolder> vec) {
+ v_ = vec;
+ }
+
+private:
+ std::vector<StringHolder> v_;
+};
+
+class SmallClass {
+public:
+ SmallClass(): member(7) {};
+ int member;
+};
+
+class BigClass {
+public:
+ BigClass(): member(11) {};
+ int member;
+ int otherMember;
+ int yetAnotherMember;
+
+ int getMember() {
+ return member;
+ }
+};
+
+class ParentClass {
+public:
+ ParentClass(): bigClass() {};
+
+ BigClass bigClass;
+
+ const BigClass& getBigClass() {
+ return bigClass;
+ };
+};
+
+template<typename T>
+class TemplateClass {
+public:
+ TemplateClass(T a, T b, T c) {
+ members[0] = a;
+ members[1] = b;
+ members[2] = c;
+ };
+
+ const T getMember(int n) {
+ return members[n];
+ }
+
+protected:
+ T members[3];
+};
+
+class ContainsTemplatedMemberClass {
+public:
+ ContainsTemplatedMemberClass(): testTemplate(86, 87, 88) {};
+
+ TemplateClass<int> testTemplate;
+
+ const TemplateClass<int>& getTestTemplate() {
+ return testTemplate;
+ };
+};
+
+// Begin Inheritance Hierarchy Class Definitions
+
+class Base {
+public:
+ Base(): name("Base"),
+ member(0),
+ baseMember(0)
+ {}
+
+ std::string getClassName() const {
+ return name;
+ }
+ std::string getClassNameFromBase() const {
+ return name;
+ }
+ std::string getClassNameNotAvailableInDerivedClasses() {
+ // but wait -- if you act now we will throw in a SECOND base class method ABSOLUTELY FREE!!
+ return name;
+ }
+ void setMember(int value) {
+ member = value;
+ }
+ int getMember() {
+ return member;
+ }
+ void setBaseMember(int value) {
+ baseMember = value;
+ }
+ int getBaseMember() {
+ return baseMember;
+ }
+ std::string name;
+ int member;
+ int baseMember;
+};
+
+class SecondBase {
+public:
+ SecondBase()
+ : name("SecondBase"),
+ member(0),
+ secondBaseMember(0)
+ {}
+
+ std::string getClassName() const {
+ return name;
+ }
+ std::string getClassNameNotAvailableInDerivedClasses() {
+ return name;
+ }
+ std::string getClassNameFromSecondBase() const {
+ return name;
+ }
+ void setMember(int value) {
+ member = value;
+ }
+ int getMember() {
+ return member;
+ }
+ void setSecondBaseMember(int value) {
+ secondBaseMember = value;
+ }
+ int getSecondBaseMember() {
+ return secondBaseMember;
+ }
+ std::string name;
+ int member;
+ int secondBaseMember;
+};
+
+class Derived : public Base{
+public:
+ Derived()
+ : Base()
+ , member(0)
+ , name_("Derived")
+ {}
+
+ std::string getClassName() const {
+ return name_;
+ }
+ void setMember(int value) {
+ member = value;
+ }
+ int getMember() {
+ return member;
+ }
+ int member;
+private:
+ std::string name_;
+};
+
+class DerivedHolder {
+public:
+ DerivedHolder() {
+ derived_.reset();
+ }
+ void newDerived() {
+ deleteDerived();
+ derived_ = std::shared_ptr<Derived>(new Derived());
+ }
+ void deleteDerived() {
+ derived_.reset();
+ }
+ std::shared_ptr<Derived> getDerived() {
+ return derived_;
+ }
+ std::string getDerivedClassName() {
+ return derived_->getClassName();
+ }
+private:
+ std::shared_ptr<Derived> derived_;
+};
+
+class SiblingDerived : public Base {
+public:
+ SiblingDerived()
+ : Base(),
+ name_("SiblingDerived")
+ {}
+
+ std::string getClassName() const {
+ return name_;
+ }
+
+private:
+ std::string name_;
+};
+
+class MultiplyDerived : public Base, public SecondBase {
+public:
+ MultiplyDerived()
+ : Base(), SecondBase(),
+ name_("MultiplyDerived")
+ { instanceCount_ ++; }
+
+ ~MultiplyDerived()
+ { instanceCount_ --; }
+
+ std::string getClassName() const {
+ return name_;
+ }
+
+ static int getInstanceCount() {
+ return instanceCount_;
+ }
+private:
+ std::string name_;
+ static int instanceCount_;
+};
+int MultiplyDerived::instanceCount_ = 0;
+
+class DerivedTwice : public Derived {
+public:
+ DerivedTwice()
+ : Derived(),
+ name_("DerivedTwice")
+ {}
+
+ std::string getClassName() const {
+ return name_;
+ }
+private:
+ std::string name_;
+};
+
+class DerivedTwiceNotBound : public Derived {
+public:
+ DerivedTwiceNotBound()
+ : Derived(),
+ name_("DerivedTwiceNotBound")
+ {}
+
+ std::string getClassName() const {
+ return name_;
+ }
+private:
+ std::string name_;
+};
+
+class DerivedThrice: public DerivedTwiceNotBound {
+public:
+ DerivedThrice()
+ : DerivedTwiceNotBound(),
+ name_("DerivedThrice")
+ {}
+
+ std::string getClassName() const {
+ return name_;
+ }
+private:
+ std::string name_;
+};
+
+class DerivedFourTimesNotBound: public DerivedThrice {
+public:
+ DerivedFourTimesNotBound()
+ : DerivedThrice(),
+ name_("DerivedFourTimesNotBound")
+ {}
+
+ std::string getClassName() const {
+ return name_;
+ }
+private:
+ std::string name_;
+};
+
+class PolyBase {
+public:
+ PolyBase(const std::string& s)
+ : str_(s),
+ name_("PolyBase")
+ {}
+
+ PolyBase(): name_("PolyBase") {}
+
+ virtual ~PolyBase() {}
+
+ virtual std::string virtualGetClassName() const {
+ return name_;
+ }
+
+ std::string getClassName() const {
+ return name_;
+ }
+
+private:
+ std::string str_;
+ std::string name_;
+};
+
+class PolySecondBase {
+public:
+ PolySecondBase(): name_("PolySecondBase")
+ {}
+
+ virtual ~PolySecondBase() {}
+
+ std::string getClassName() const {
+ return name_;
+ }
+private:
+ std::string name_;
+};
+
+class PolyDerived : public PolyBase{
+public:
+ PolyDerived()
+ : PolyBase("PolyDerived"),
+ name_("PolyDerived")
+ {}
+
+ std::string virtualGetClassName() const {
+ return name_;
+ }
+
+ std::string getClassName() const {
+ return name_;
+ }
+
+ static void setPtrDerived() {
+ ptr_ = std::shared_ptr<PolyDerived>(new PolyDerived());
+ }
+
+ static void releasePtr() {
+ ptr_.reset();
+ }
+
+ static std::string getPtrClassName() {
+ return ptr_->getClassName();
+ }
+
+ static std::shared_ptr<PolyBase> getPtr() {
+ return ptr_;
+ }
+
+private:
+ std::string name_;
+ static std::shared_ptr<PolyBase> ptr_;
+};
+std::shared_ptr<PolyBase> PolyDerived::ptr_;
+
+class PolySiblingDerived : public PolyBase {
+public:
+ PolySiblingDerived()
+ : PolyBase(),
+ name_("PolySiblingDerived")
+ {}
+
+ std::string getClassName() const {
+ return name_;
+ }
+private:
+ std::string name_;
+};
+
+class PolyMultiplyDerived : public PolyBase, public PolySecondBase {
+public:
+ PolyMultiplyDerived()
+ : PolyBase(), PolySecondBase(),
+ name_("PolyMultiplyDerived")
+ {}
+
+ std::string getClassName() const {
+ return name_;
+ }
+private:
+ std::string name_;
+};
+
+class PolyDerivedTwiceWithoutSmartPointer: public PolyDerived {
+public:
+ PolyDerivedTwiceWithoutSmartPointer()
+ : PolyDerived(),
+ name_("PolyDerivedTwiceWithoutSmartPointer")
+ {}
+
+ std::string getClassName() const {
+ return name_;
+ }
+private:
+ std::string name_;
+};
+
+class PolyDerivedTwiceNotBound : public PolyDerived {
+public:
+ PolyDerivedTwiceNotBound()
+ : PolyDerived(),
+ name_("PolyDerivedTwiceNotBound")
+ {}
+
+ std::string getClassName() const {
+ return name_;
+ }
+private:
+ std::string name_;
+};
+
+class PolyDerivedThrice: public PolyDerivedTwiceNotBound {
+public:
+ PolyDerivedThrice()
+ : PolyDerivedTwiceNotBound(),
+ name_("PolyDerivedThrice")
+ {}
+
+ std::string getClassName() const {
+ return name_;
+ }
+private:
+ std::string name_;
+};
+
+class PolyDerivedFourTimesNotBound: public PolyDerivedThrice {
+public:
+ PolyDerivedFourTimesNotBound()
+ : PolyDerivedThrice(),
+ name_("PolyDerivedFourTimesNotBound")
+ {}
+
+ std::string getClassName() const {
+ return name_;
+ }
+private:
+ std::string name_;
+};
+
+// todo: does it need to be polymorphic?
+// todo: virtual diamond pattern
+class PolyDiamondBase {
+public:
+ PolyDiamondBase():
+ name_("PolyBase")
+ {}
+ ~PolyDiamondBase() {}
+
+ std::string getClassName() const {
+ return name_;
}
+private:
+ std::string name_;
+};
+
+class PolyDiamondDerived: public PolyDiamondBase {
+public:
+ PolyDiamondDerived()
+ : PolyDiamondBase(),
+ name_("PolyDiamondDerived")
+ {}
+ std::string getClassName() const {
+ return name_;
+ }
private:
- std::string str;
+ std::string name_;
};
-struct TupleVector {
- float x, y, z;
+class PolyDiamondSiblingDerived: public PolyDiamondBase {
+public:
+ PolyDiamondSiblingDerived()
+ : PolyDiamondBase(),
+ name_("PolyDiamondSiblingDerived")
+ {}
+
+ std::string getClassName() const {
+ return name_;
+ }
+private:
+ std::string name_;
+};
+
+class PolyDiamondMultiplyDerived: public PolyDiamondDerived, public PolyDiamondSiblingDerived {
+public:
+ PolyDiamondMultiplyDerived()
+ : PolyDiamondDerived(), PolyDiamondSiblingDerived(),
+ name_("PolyDiamondMultiplyDerived")
+ {}
+
+ std::string getClassName() const {
+ return name_;
+ }
+private:
+ std::string name_;
+};
+
+// End Inheritance Hierarchy Class Definitions
+
+std::map<std::string, int> embind_test_get_string_int_map() {
+ std::map<std::string, int> m;
+
+ m["one"] = 1;
+ m["two"] = 2;
+
+ return m;
};
-float readTupleVectorZ(const TupleVector& v) {
+struct Vector {
+ Vector() = delete;
+
+ Vector(float x_, float y_, float z_, float w_)
+ : x(x_)
+ , y(y_)
+ , z(z_)
+ , w(w_)
+ {}
+
+ float x, y, z, w;
+
+ float& operator[](int i) {
+ return (&x)[i];
+ }
+
+ const float& operator[](int i) const {
+ return (&x)[i];
+ }
+
+ float getY() const {
+ return y;
+ }
+ void setY(float _y) {
+ y = _y;
+ }
+};
+
+struct DummyDataToTestPointerAdjustment {
+ std::string dummy;
+};
+
+struct TupleVector : DummyDataToTestPointerAdjustment, Vector {
+ TupleVector(): Vector(0, 0, 0, 0) {}
+ TupleVector(float x, float y, float z, float w): Vector(x, y, z, w) {}
+};
+
+struct StructVector : DummyDataToTestPointerAdjustment, Vector {
+ StructVector(): Vector(0, 0, 0, 0) {}
+ StructVector(float x, float y, float z, float w): Vector(x, y, z, w) {}
+};
+
+float readVectorZ(const Vector& v) {
return v.z;
}
-void writeTupleVectorZ(TupleVector& v, float z) {
+void writeVectorZ(Vector& v, float z) {
v.z = z;
}
struct TupleVectorTuple {
- TupleVector v;
+ TupleVector v = TupleVector(0, 0, 0, 0);
};
TupleVector emval_test_return_TupleVector() {
- TupleVector cv;
- cv.x = 1;
- cv.y = 2;
- cv.z = 3;
- return cv;
+ return TupleVector(1, 2, 3, 4);
}
TupleVector emval_test_take_and_return_TupleVector(TupleVector v) {
@@ -167,16 +788,8 @@ TupleVectorTuple emval_test_return_TupleVectorTuple() {
return cvt;
}
-struct StructVector {
- float x, y, z;
-};
-
StructVector emval_test_return_StructVector() {
- StructVector v;
- v.x = 1;
- v.y = 2;
- v.z = 3;
- return v;
+ return StructVector(1, 2, 3, 4);
}
StructVector emval_test_take_and_return_StructVector(StructVector v) {
@@ -187,6 +800,11 @@ struct CustomStruct {
CustomStruct()
: field(10)
{}
+
+ const int& getField() const {
+ return field;
+ }
+
int field;
};
@@ -210,34 +828,647 @@ EnumClass emval_test_take_and_return_EnumClass(EnumClass e) {
return e;
}
-class Interface {
+void emval_test_call_function(val v, int i, float f, TupleVector tv, StructVector sv) {
+ v(i, f, tv, sv);
+}
+
+std::unique_ptr<ValHolder> emval_test_return_unique_ptr() {
+ return std::unique_ptr<ValHolder>(new ValHolder(val::object()));
+}
+
+std::shared_ptr<ValHolder> emval_test_return_shared_ptr() {
+ return std::shared_ptr<ValHolder>(new ValHolder(val::object()));
+}
+
+std::shared_ptr<ValHolder> emval_test_return_empty_shared_ptr() {
+ return std::shared_ptr<ValHolder>();
+}
+
+bool emval_test_is_shared_ptr_null(std::shared_ptr<ValHolder> p) {
+ return !p;
+}
+
+static SmallClass smallClass;
+static BigClass bigClass;
+
+SmallClass embind_test_return_small_class_instance() {
+ return smallClass;
+}
+
+BigClass embind_test_return_big_class_instance() {
+ return bigClass;
+}
+
+int embind_test_accept_small_class_instance(SmallClass c) {
+ return c.member;
+}
+
+int embind_test_accept_big_class_instance(BigClass c) {
+ return c.member;
+}
+
+// Begin Inheritance Hierarchy Test Wrappers
+
+Base* embind_test_return_raw_base_ptr() {
+ return new Base();
+}
+
+Base* embind_test_return_raw_derived_ptr_as_base() {
+ return new Derived();
+}
+
+Base* embind_test_return_raw_sibling_derived_ptr_as_base() {
+ return new SiblingDerived();
+}
+
+PolyBase* embind_test_return_raw_polymorphic_derived_ptr_as_base() {
+ return new PolyDerived();
+}
+
+PolyBase* embind_test_return_raw_polymorphic_sibling_derived_ptr_as_base() {
+ return new PolySiblingDerived();
+}
+
+PolyBase* embind_test_return_raw_polymorphic_multiply_derived_ptr_as_base() {
+ return new PolyMultiplyDerived();
+}
+
+PolySecondBase* embind_test_return_raw_polymorphic_multiply_derived_ptr_as_second_base() {
+ return new PolyMultiplyDerived();
+}
+
+PolyBase* embind_test_return_raw_polymorphic_derived_four_times_not_bound_as_base() {
+ return new PolyDerivedFourTimesNotBound();
+}
+
+std::shared_ptr<Base> embind_test_return_smart_base_ptr() {
+ return std::shared_ptr<Base>(new Base());
+}
+
+std::shared_ptr<PolyBase> embind_test_return_smart_polymorphic_base_ptr() {
+ return std::shared_ptr<PolyBase>(new PolyBase("PolyBase"));
+}
+
+std::shared_ptr<Derived> embind_test_return_smart_derived_ptr() {
+ return std::shared_ptr<Derived>(new Derived());
+}
+
+std::shared_ptr<SiblingDerived> embind_test_return_smart_sibling_derived_ptr() {
+ return std::shared_ptr<SiblingDerived>(new SiblingDerived());
+}
+
+std::shared_ptr<MultiplyDerived> embind_test_return_smart_multiply_derived_ptr() {
+ return std::shared_ptr<MultiplyDerived>(new MultiplyDerived());
+}
+
+std::shared_ptr<DerivedThrice> embind_test_return_smart_derived_thrice_ptr() {
+ return std::shared_ptr<DerivedThrice>(new DerivedThrice());
+}
+
+std::shared_ptr<PolyDerived> embind_test_return_smart_polymorphic_derived_ptr() {
+ return std::shared_ptr<PolyDerived>(new PolyDerived());
+}
+
+std::shared_ptr<PolySiblingDerived> embind_test_return_smart_polymorphic_sibling_derived_ptr() {
+ return std::shared_ptr<PolySiblingDerived>(new PolySiblingDerived());
+}
+
+std::shared_ptr<PolyMultiplyDerived> embind_test_return_smart_polymorphic_multiply_derived_ptr() {
+ return std::shared_ptr<PolyMultiplyDerived>(new PolyMultiplyDerived());
+}
+
+std::shared_ptr<PolyBase> embind_test_return_poly_derived_twice_without_smart_pointer_as_poly_base() {
+ return std::shared_ptr<PolyBase>(new PolyDerivedTwiceWithoutSmartPointer());
+}
+
+std::shared_ptr<PolyDerivedThrice> embind_test_return_smart_poly_derived_thrice_ptr() {
+ return std::shared_ptr<PolyDerivedThrice>(new PolyDerivedThrice());
+}
+
+std::shared_ptr<PolyBase> embind_test_return_smart_derived_ptr_as_base() {
+ return std::shared_ptr<PolyBase>(new PolyDerived());
+}
+
+val embind_test_return_smart_derived_ptr_as_val() {
+ return val(std::shared_ptr<PolyBase>(new PolyDerived()));
+}
+
+std::shared_ptr<PolyBase> embind_test_return_smart_polymorphic_derived_ptr_as_base() {
+ return std::shared_ptr<PolyBase>(new PolyDerived());
+}
+
+std::shared_ptr<PolyBase> embind_test_return_smart_polymorphic_sibling_derived_ptr_as_base() {
+ return std::shared_ptr<PolyBase>(new PolySiblingDerived());
+}
+
+std::string embind_test_get_class_name_via_base_ptr(Base *p) {
+ return p->getClassName();
+}
+
+std::string embind_test_get_class_name_via_second_base_ptr(SecondBase *p) {
+ return p->getClassName();
+}
+
+std::string embind_test_get_class_name_via_polymorphic_base_ptr(PolyBase *p) {
+ return p->getClassName();
+}
+
+std::string embind_test_get_class_name_via_polymorphic_second_base_ptr(PolySecondBase *p) {
+ return p->getClassName();
+}
+
+std::string embind_test_get_class_name_via_smart_base_ptr(std::shared_ptr<Base> p) {
+ return p->getClassName();
+}
+
+std::string embind_test_get_class_name_via_reference_to_smart_base_ptr(std::shared_ptr<Base>& p) {
+ return p->getClassName();
+}
+
+std::string embind_test_get_class_name_via_smart_second_base_ptr(std::shared_ptr<SecondBase> p) {
+ return p->getClassName();
+}
+
+std::string embind_test_get_class_name_via_smart_polymorphic_base_ptr(std::shared_ptr<PolyBase> p) {
+ return p->getClassName();
+}
+
+std::string embind_test_get_virtual_class_name_via_smart_polymorphic_base_ptr(std::shared_ptr<PolyBase> p) {
+ return p->virtualGetClassName();
+}
+
+std::string embind_test_get_class_name_via_smart_polymorphic_second_base_ptr(std::shared_ptr<PolySecondBase> p) {
+ return p->getClassName();
+}
+
+void embind_modify_smart_pointer_passed_by_reference(std::shared_ptr<Base>& p) {
+ p = std::shared_ptr<Base>(new Base());
+ p->name = "Changed";
+}
+
+void embind_attempt_to_modify_smart_pointer_when_passed_by_value(std::shared_ptr<Base> p) {
+ p = std::shared_ptr<Base>(new Base());
+ p->name = "Changed";
+}
+
+static std::shared_ptr<Base> savedBasePointer;
+
+void embind_save_smart_base_pointer(std::shared_ptr<Base> p) {
+ savedBasePointer = p;
+}
+
+// End Inheritance Hierarchy Test Wrappers
+
+std::vector<int> emval_test_return_vector() {
+ int myints[] = { 10, 20, 30 };
+ return std::vector<int>(myints, myints + sizeof(myints) / sizeof(int));
+}
+
+std::vector<std::vector<int> > emval_test_return_vector_of_vectors() {
+ int myints1[] = { 10, 20, 30 };
+ int myints2[] = { 40, 50, 60 };
+ std::vector<int> vec1(myints1, myints1 + sizeof(myints1) / sizeof(int));
+ std::vector<int> vec2(myints2, myints2 + sizeof(myints2) / sizeof(int));
+ std::vector<std::vector<int>>vec3;
+ vec3.emplace_back(vec1);
+ vec3.emplace_back(vec2);
+ return vec3;
+}
+
+std::vector<std::shared_ptr<StringHolder>> emval_test_return_shared_ptr_vector() {
+ std::vector<std::shared_ptr<StringHolder>> sharedStrVector;
+ sharedStrVector.push_back(std::shared_ptr<StringHolder>(new StringHolder("string #1")));
+ sharedStrVector.push_back(std::shared_ptr<StringHolder>(new StringHolder("string #2")));
+
+ return sharedStrVector;
+}
+
+class JSInterfaceHolder {
public:
- virtual int method() = 0;
- virtual TupleInStruct method2(const TupleInStruct& arg1, float arg2) = 0;
- virtual void method3() = 0;
+ JSInterfaceHolder(JSInterface &jsobj) : jsobj_(jsobj) {
+ ptr_ = JSInterface::cloneToSharedPtr(jsobj_);
+ }
+
+ int callMethod(std::string method) { return jsobj_.call<int>(method.c_str()); }
+ int callMethodUsingSharedPtr(std::string method) { return ptr_->call<int>(method.c_str()); }
+
+private:
+ JSInterface jsobj_;
+ std::shared_ptr<JSInterface> ptr_;
};
-int emval_test_call_method(Interface& i) {
- return i.method();
+void test_string_with_vec(const std::string& p1, std::vector<std::string>& v1) {
+ // THIS DOES NOT WORK -- need to get as val and then call vecFromJSArray
+ printf("%s\n", p1.c_str());
}
-TupleInStruct emval_test_call_method2(Interface& i, const TupleInStruct& arg1, float arg2) {
- return i.method2(arg1, arg2);
+val embind_test_new_Object() {
+ return val::global("Object").new_();
}
-void emval_test_call_method3(Interface& i) {
- i.method3();
+val embind_test_new_factory(val factory, val argument) {
+ return factory.new_(10, std::string("hello"), argument);
}
-void emval_test_call_function(val v, int i, float f, TupleVector tv, StructVector sv) {
- v(i, f, tv, sv);
+class AbstractClass {
+public:
+ virtual ~AbstractClass() {}
+ virtual std::string abstractMethod() const = 0;
+};
+
+class AbstractClassWrapper : public wrapper<AbstractClass> {
+public:
+ EMSCRIPTEN_WRAPPER(AbstractClassWrapper);
+
+ std::string abstractMethod() const {
+ return call<std::string>("abstractMethod");
+ }
+};
+
+class ConcreteClass : public AbstractClass {
+ std::string abstractMethod() const {
+ return "from concrete";
+ }
+};
+
+std::shared_ptr<AbstractClass> getAbstractClass() {
+ return std::make_shared<ConcreteClass>();
}
-EMSCRIPTEN_BINDINGS(([]() {
- function("mallinfo", &emval_test_mallinfo);
+std::string callAbstractMethod(AbstractClass& ac) {
+ return ac.abstractMethod();
+}
+
+class HasExternalConstructor {
+public:
+ HasExternalConstructor(const std::string& str)
+ : m(str)
+ {}
+
+ std::string getString() const {
+ return m;
+ }
+
+ std::string m;
+};
+
+HasExternalConstructor* createHasExternalConstructor(const std::string& str) {
+ return new HasExternalConstructor(str);
+}
+
+template<typename T>
+class CustomSmartPtr {
+public:
+ CustomSmartPtr()
+ : CustomSmartPtr(nullptr)
+ {
+ std::fill(d_, d_ + N_, Valid);
+ }
+
+ explicit CustomSmartPtr(T* t)
+ : ptr_(t)
+ {
+ std::fill(d_, d_ + N_, Valid);
+ }
+
+ CustomSmartPtr(const CustomSmartPtr& other)
+ : ptr_(other.ptr_)
+ {
+ other.verify();
+ std::fill(d_, d_ + N_, Valid);
+ if (ptr_) {
+ ++(ptr_->refcount);
+ }
+ }
+
+ ~CustomSmartPtr() {
+ verify();
+ std::fill(d_, d_ + N_, Deleted);
+
+ if (ptr_ && --ptr_->refcount == 0) {
+ delete ptr_;
+ }
+ }
+
+ T* get_raw() const {
+ return ptr_;
+ }
+
+private:
+ void verify() const {
+ for (size_t i = 0; i < N_; ++i) {
+ if (d_[i] != Valid) {
+ abort();
+ }
+ }
+ }
+
+ enum {
+ Valid = 255,
+ Deleted = 127,
+ };
+ static constexpr size_t N_ = 1000000;
+ unsigned char d_[N_];
+ T* ptr_;
+
+ CustomSmartPtr& operator=(const CustomSmartPtr&) = delete;
+};
+
+class HeldBySmartPtr {
+public:
+ HeldBySmartPtr(int i, const std::string& s)
+ : i(i)
+ , s(s)
+ {}
+
+ static CustomSmartPtr<HeldBySmartPtr> newCustomPtr(int i, const std::string& s) {
+ return CustomSmartPtr<HeldBySmartPtr>(new HeldBySmartPtr(i, s));
+ }
+
+ int refcount = 1;
+ int i;
+ std::string s;
+};
+HeldBySmartPtr takesHeldBySmartPtr(HeldBySmartPtr p) {
+ return p;
+}
+std::shared_ptr<HeldBySmartPtr> takesHeldBySmartPtrSharedPtr(std::shared_ptr<HeldBySmartPtr> p) {
+ return p;
+}
+
+namespace emscripten {
+ template<typename T>
+ struct smart_ptr_trait<CustomSmartPtr<T>> {
+ typedef T element_type;
+
+ static sharing_policy get_sharing_policy() {
+ return sharing_policy::NONE;
+ }
+
+ static T* get(const CustomSmartPtr<T>& p) {
+ return p.get_raw();
+ }
+
+ static CustomSmartPtr<T> share(const CustomSmartPtr<T>& r, T* ptr) {
+ ++ptr->refcount; // implement an adopt API?
+ return CustomSmartPtr<T>(ptr);
+ }
+ };
+}
+
+typedef CustomSmartPtr<class HeldByCustomSmartPtr> HeldByCustomSmartPtrPtr;
+
+class HeldByCustomSmartPtr {
+public:
+ HeldByCustomSmartPtr(int i, const std::string& s)
+ : i(i)
+ , s(s)
+ {}
+
+ static HeldByCustomSmartPtrPtr create(int i, const std::string& s) {
+ return HeldByCustomSmartPtrPtr(new HeldByCustomSmartPtr(i, s));
+ }
+
+ static std::shared_ptr<HeldByCustomSmartPtr> createSharedPtr(int i, const std::string& s) {
+ return std::make_shared<HeldByCustomSmartPtr>(i, s);
+ };
+
+ int refcount = 1;
+ int i;
+ std::string s;
+};
+
+HeldByCustomSmartPtr* passThroughRawPtr(HeldByCustomSmartPtr* p) {
+ return p;
+}
+HeldByCustomSmartPtrPtr passThroughCustomSmartPtr(HeldByCustomSmartPtrPtr p) {
+ return p;
+}
+
+struct Base1 {
+public:
+ Base1(): field1("Base1") {}
+ std::string field1;
+
+ std::string getField() const {
+ return field1;
+ }
+};
+
+struct Base2 {
+public:
+ Base2(): field2("Base2") {}
+ std::string field2;
+
+ std::string getField() const {
+ return field2;
+ }
+};
+
+struct HasTwoBases : public Base1, public Base2 {
+};
+
+val get_module_property(const std::string& s) {
+ return val::module_property(s.c_str());
+}
+
+std::string char_to_string(char ch) {
+ char str[256];
+ sprintf(str, "%d", (int)ch);
+ return str;
+}
+
+std::string signed_char_to_string(signed char ch) {
+ char str[256];
+ sprintf(str, "%hhd", ch);
+ return str;
+}
+
+std::string unsigned_char_to_string(unsigned char ch) {
+ char str[256];
+ sprintf(str, "%hhu", ch);
+ return str;
+}
+
+std::string short_to_string(short val) {
+ char str[256];
+ sprintf(str, "%hd", val);
+ return str;
+}
+
+std::string unsigned_short_to_string(unsigned short val) {
+ char str[256];
+ sprintf(str, "%hu", val);
+ return str;
+}
+
+std::string int_to_string(int val) {
+ char str[256];
+ sprintf(str, "%d", val);
+ return str;
+}
+
+std::string unsigned_int_to_string(unsigned int val) {
+ char str[256];
+ sprintf(str, "%u", val);
+ return str;
+}
+
+std::string long_to_string(long val) {
+ char str[256];
+ sprintf(str, "%ld", val);
+ return str;
+}
+
+std::string unsigned_long_to_string(unsigned long val) {
+ char str[256];
+ sprintf(str, "%lu", val);
+ return str;
+}
+
+class MultipleCtors {
+public:
+ int value;
+
+ MultipleCtors(int i) {
+ value = 1;
+ assert(i == 10);
+ }
+ MultipleCtors(int i, int j) {
+ value = 2;
+ assert(i == 20);
+ assert(j == 20);
+ }
+ MultipleCtors(int i, int j, int k) {
+ value = 3;
+ assert(i == 30);
+ assert(j == 30);
+ assert(k == 30);
+ }
+
+ int WhichCtorCalled() const {
+ return value;
+ }
+};
+
+class MultipleOverloads {
+public:
+ MultipleOverloads() {}
+
+ int value;
+ static int staticValue;
+
+ int Func(int i) {
+ assert(i == 10);
+ value = 1;
+ return 1;
+ }
+ int Func(int i, int j) {
+ assert(i == 20);
+ assert(j == 20);
+ value = 2;
+ return 2;
+ }
+
+ int WhichFuncCalled() const {
+ return value;
+ }
+
+ static int StaticFunc(int i) {
+ assert(i == 10);
+ staticValue = 1;
+ return 1;
+ }
+ static int StaticFunc(int i, int j) {
+ assert(i == 20);
+ assert(j == 20);
+ staticValue = 2;
+ return 2;
+ }
+
+ static int WhichStaticFuncCalled() {
+ return staticValue;
+ }
+};
+
+class MultipleOverloadsDerived : public MultipleOverloads {
+public:
+ MultipleOverloadsDerived() {}
+
+ int Func(int i, int j, int k) {
+ assert(i == 30);
+ assert(j == 30);
+ assert(k == 30);
+ value = 3;
+ return 3;
+ }
+ int Func(int i, int j, int k, int l) {
+ assert(i == 40);
+ assert(j == 40);
+ assert(k == 40);
+ assert(l == 40);
+ value = 4;
+ return 4;
+ }
+
+ static int StaticFunc(int i, int j, int k) {
+ assert(i == 30);
+ assert(j == 30);
+ assert(k == 30);
+ staticValue = 3;
+ return 3;
+ }
+ static int StaticFunc(int i, int j, int k, int l) {
+ assert(i == 40);
+ assert(j == 40);
+ assert(k == 40);
+ assert(l == 40);
+ staticValue = 4;
+ return 4;
+ }
+};
+
+int overloaded_function(int i)
+{
+ assert(i == 10);
+ return 1;
+}
+
+int overloaded_function(int i, int j)
+{
+ assert(i == 20);
+ assert(j == 20);
+ return 2;
+}
+
+EMSCRIPTEN_BINDINGS(constants) {
+ constant("INT_CONSTANT", 10);
+ constant("STRING_CONSTANT", std::string("some string"));
+
+ TupleVector tv(1, 2, 3, 4);
+ constant("VALUE_TUPLE_CONSTANT", tv);
+
+ StructVector sv(1, 2, 3, 4);
+ constant("VALUE_STRUCT_CONSTANT", sv);
+}
+
+EMSCRIPTEN_BINDINGS(tests) {
+ register_js_interface();
+
+ register_vector<int>("IntegerVector");
+ register_vector<char>("CharVector");
+ register_vector<unsigned>("VectorUnsigned");
+ register_vector<unsigned char>("VectorUnsignedChar");
+ register_vector<std::string>("StringVector");
+ register_vector<emscripten::val>("EmValVector");
+ register_vector<float>("FloatVector");
+ register_vector<std::vector<int>>("IntegerVectorVector");
+
+ function("mallinfo", &emval_test_mallinfo);
function("emval_test_new_integer", &emval_test_new_integer);
function("emval_test_new_string", &emval_test_new_string);
+ function("emval_test_get_string_from_val", &emval_test_get_string_from_val);
function("emval_test_new_object", &emval_test_new_object);
function("emval_test_passthrough_unsigned", &emval_test_passthrough_unsigned);
function("emval_test_passthrough", &emval_test_passthrough);
@@ -247,6 +1478,7 @@ EMSCRIPTEN_BINDINGS(([]() {
function("emval_test_as_unsigned", &emval_test_as_unsigned);
function("emval_test_get_length", &emval_test_get_length);
function("emval_test_add", &emval_test_add);
+ function("const_ref_adder", &const_ref_adder);
function("emval_test_sum", &emval_test_sum);
//function("emval_test_take_and_return_const_char_star", &emval_test_take_and_return_const_char_star);
@@ -257,9 +1489,9 @@ EMSCRIPTEN_BINDINGS(([]() {
value_tuple<TupleVector>("TupleVector")
.element(&TupleVector::x)
- .element(&TupleVector::y)
- //.element(&TupleVector::z)
- .element(&readTupleVectorZ, &writeTupleVectorZ)
+ .element(&Vector::getY, &Vector::setY)
+ .element(&readVectorZ, &writeVectorZ)
+ .element(index<3>())
;
function("emval_test_return_TupleVector", &emval_test_return_TupleVector);
@@ -273,8 +1505,9 @@ EMSCRIPTEN_BINDINGS(([]() {
value_struct<StructVector>("StructVector")
.field("x", &StructVector::x)
- .field("y", &StructVector::y)
- .field("z", &StructVector::z)
+ .field("y", &Vector::getY, &Vector::setY)
+ .field("z", &readVectorZ, &writeVectorZ)
+ .field("w", index<3>())
;
function("emval_test_return_StructVector", &emval_test_return_StructVector);
@@ -287,24 +1520,289 @@ EMSCRIPTEN_BINDINGS(([]() {
function("emval_test_take_and_return_TupleInStruct", &emval_test_take_and_return_TupleInStruct);
class_<ValHolder>("ValHolder")
+ .smart_ptr<std::shared_ptr<ValHolder>>()
.constructor<val>()
- .method("getVal", &ValHolder::getVal)
- .method("setVal", &ValHolder::setVal)
- .method("returnIntPlusFive", &ValHolder::returnIntPlusFive)
- .classmethod("some_class_method", &ValHolder::some_class_method)
+ .function("getVal", &ValHolder::getVal)
+ .function("getValNonConst", &ValHolder::getValNonConst)
+ .function("getConstVal", &ValHolder::getConstVal)
+ .function("getValConstRef", &ValHolder::getValConstRef)
+ .function("setVal", &ValHolder::setVal)
+ .class_function("makeConst", &ValHolder::makeConst, allow_raw_pointer<ret_val>())
+ .class_function("makeValHolder", &ValHolder::makeValHolder)
+ .class_function("some_class_method", &ValHolder::some_class_method)
+ .class_function("set_via_raw_pointer",
+ &ValHolder::set_via_raw_pointer,
+ allow_raw_pointer<arg<0>>())
+ .class_function("get_via_raw_pointer",
+ &ValHolder::get_via_raw_pointer,
+ allow_raw_pointer<arg<0>>())
+ .class_function("transfer_via_raw_pointer",
+ &ValHolder::transfer_via_raw_pointer,
+ allow_raw_pointers())
+
+ // non-member method
+ .function("setEmpty", &emval_test_set_ValHolder_to_empty_object)
+ .function("getValNonMember", &ValHolder::getValNonMember)
;
+
function("emval_test_return_ValHolder", &emval_test_return_ValHolder);
function("emval_test_set_ValHolder_to_empty_object", &emval_test_set_ValHolder_to_empty_object);
+ class_<std::function<std::string(std::string)>>("StringFunctorString")
+ .constructor<>()
+ .calloperator<std::string, std::string>("opcall")
+ ;
+
+ function("emval_test_get_function_ptr", &emval_test_get_function_ptr);
+ function("emval_test_take_and_call_functor", &emval_test_take_and_call_functor);
+
class_<StringHolder>("StringHolder")
+ .smart_ptr<std::shared_ptr<StringHolder>>()
.constructor<std::string>()
- .method("set", &StringHolder::set)
- .method("get", &StringHolder::get)
+ .function("set", &StringHolder::set)
+ .function("get", &StringHolder::get)
+ .function("get_const_ref", &StringHolder::get_const_ref)
+ ;
+
+ class_<SharedPtrHolder>("SharedPtrHolder")
+ .constructor<>()
+ .function("get", &SharedPtrHolder::get)
+ .function("set", &SharedPtrHolder::set)
+ ;
+
+ class_<SmallClass>("SmallClass")
+ .constructor<>()
+ .property("member", &SmallClass::member)
+ ;
+
+ class_<BigClass>("BigClass")
+ .constructor<>()
+ .property("member", &BigClass::member)
+ .property("otherMember", &BigClass::otherMember)
+ .property("yetAnotherMember", &BigClass::yetAnotherMember)
+ .function("getMember", &BigClass::getMember)
+ ;
+
+ class_<ParentClass>("ParentClass")
+ .constructor<>()
+ .function("getBigClass", &ParentClass::getBigClass)
+ ;
+
+ class_<TemplateClass<int>>("IntTemplateClass")
+ .constructor<int, int, int>()
+ .function("getMember", &TemplateClass<int>::getMember)
+ ;
+
+ class_<ContainsTemplatedMemberClass>("ContainsTemplatedMemberClass")
+ .constructor<>()
+ .function("getTestTemplate", &ContainsTemplatedMemberClass::getTestTemplate)
+ ;
+
+ // register Derived before Base as a test that it's possible to
+ // register base classes afterwards
+ class_<Derived, base<Base>>("Derived")
+ .smart_ptr<std::shared_ptr<Derived>>()
+ .constructor<>()
+ .function("getClassName", &Derived::getClassName)
+ .function("getMember", &Derived::getMember)
+ .function("setMember", &Derived::setMember)
+ .property("member", &Derived::member)
+ ;
+
+ class_<Base>("Base")
+ .smart_ptr<std::shared_ptr<Base>>()
+ .constructor<>()
+ .function("getClassName", &Base::getClassName)
+ .function("getClassNameFromBase", &Base::getClassNameFromBase)
+ .function("getClassNameNotAvailableInDerivedClasses", &Base::getClassNameNotAvailableInDerivedClasses)
+ .function("getMember", &Base::getMember)
+ .function("setMember", &Base::setMember)
+ .function("getBaseMember", &Base::getBaseMember)
+ .function("setBaseMember", &Base::setBaseMember)
+ .property("member", &Base::member)
+ .property("baseMember", &Base::baseMember)
+ ;
+
+ class_<SecondBase>("SecondBase")
+ .smart_ptr<std::shared_ptr<SecondBase>>()
+ .constructor<>()
+ .function("getClassName", &SecondBase::getClassName)
+ .function("getClassNameFromSecondBase", &SecondBase::getClassNameFromSecondBase)
+ .function("getClassNameNotAvailableInDerivedClasses", &SecondBase::getClassNameNotAvailableInDerivedClasses)
+ .function("getMember", &SecondBase::getMember)
+ .function("setMember", &SecondBase::setMember)
+ .function("getSecondBaseMember", &SecondBase::getSecondBaseMember)
+ .function("setSecondBaseMember", &SecondBase::setSecondBaseMember)
+ .property("member", &SecondBase::member)
+ .property("secondBaseMember", &SecondBase::secondBaseMember)
+ ;
+
+
+ class_<DerivedHolder>("DerivedHolder")
+ .constructor<>()
+ .function("newDerived", &DerivedHolder::newDerived)
+ .function("deleteDerived", &DerivedHolder::deleteDerived)
+ .function("getDerived", &DerivedHolder::getDerived)
+ .function("getDerivedClassName", &DerivedHolder::getDerivedClassName)
+ ;
+
+ class_<SiblingDerived>("SiblingDerived")
+ .smart_ptr<std::shared_ptr<SiblingDerived>>()
+ .constructor<>()
+ .function("getClassName", &SiblingDerived::getClassName)
+ ;
+
+ class_<MultiplyDerived, base<Base>>("MultiplyDerived")
+ .smart_ptr<std::shared_ptr<MultiplyDerived>>()
+ .constructor<>()
+ .function("getClassName", &MultiplyDerived::getClassName)
+ .class_function("getInstanceCount", &MultiplyDerived::getInstanceCount)
+ ;
+
+ class_<DerivedTwice, base<Derived> >("DerivedTwice")
+ .constructor<>()
+ .function("getClassName", &DerivedTwice::getClassName)
+ ;
+
+ class_<DerivedThrice, base<Derived> >("DerivedThrice")
+ .smart_ptr<std::shared_ptr<DerivedThrice>>()
+ .constructor<>()
+ .function("getClassName", &DerivedThrice::getClassName)
+ ;
+
+ class_<PolyBase>("PolyBase")
+ .smart_ptr<std::shared_ptr<PolyBase>>()
+ .constructor<>()
+ .function("virtualGetClassName", &PolyBase::virtualGetClassName)
+ .function("getClassName", &PolyBase::getClassName)
+ ;
+
+ class_<PolySecondBase>("PolySecondBase")
+ .smart_ptr<std::shared_ptr<PolySecondBase>>()
+ .constructor<>()
+ .function("getClassName", &PolySecondBase::getClassName)
+ ;
+
+ class_<PolyDerived, base<PolyBase>>("PolyDerived")
+ .smart_ptr<std::shared_ptr<PolyDerived>>()
+ .constructor<>()
+ .function("virtualGetClassName", &PolyDerived::virtualGetClassName)
+ .function("getClassName", &PolyDerived::getClassName)
+ .class_function("setPtrDerived", &PolyDerived::setPtrDerived)
+ .class_function("releasePtr", &PolyDerived::releasePtr)
+ .class_function("getPtrClassName", &PolyDerived::getPtrClassName)
+ .class_function("getPtr", &PolyDerived::getPtr)
+ ;
+// static void setPtrDerived() {
+// ptr = std::shared_ptr<PolyDerived>(new PolyDerived());
+// }
+//
+// static std::string getPtrClassName() {
+// return ptr->getClassName();
+// }
+//
+// static std::shared_ptr<PolyBase> getPtr() {
+// return ptr;
+// }
+
+ class_<PolySiblingDerived, base<PolyBase>>("PolySiblingDerived")
+ .smart_ptr<std::shared_ptr<PolySiblingDerived>>()
+ .constructor<>()
+ .function("getClassName", &PolySiblingDerived::getClassName)
+ ;
+
+ class_<PolyMultiplyDerived, base<PolyBase>>("PolyMultiplyDerived")
+ .smart_ptr<std::shared_ptr<PolyMultiplyDerived>>()
+ .constructor<>()
+ .function("getClassName", &PolyMultiplyDerived::getClassName)
+ ;
+
+ class_<PolyDerivedThrice, base<PolyDerived>>("PolyDerivedThrice")
+ .smart_ptr<std::shared_ptr<PolyDerivedThrice>>()
+ .constructor<>()
+ .function("getClassName", &PolyDerivedThrice::getClassName)
+ ;
+
+ class_<PolyDiamondBase>("PolyDiamondBase")
+ .smart_ptr<std::shared_ptr<PolyDiamondBase>>()
+ .constructor<>()
+ .function("getClassName", &PolyDiamondBase::getClassName)
+ ;
+
+ class_<PolyDiamondDerived>("PolyDiamondDerived")
+ .smart_ptr<std::shared_ptr<PolyDiamondDerived>>()
+ .constructor<>()
+ .function("getClassName", &PolyDiamondDerived::getClassName)
+ ;
+
+ class_<PolyDiamondSiblingDerived>("PolyDiamondSiblingDerived")
+ .smart_ptr<std::shared_ptr<PolyDiamondSiblingDerived>>()
+ .constructor<>()
+ .function("getClassName", &PolyDiamondSiblingDerived::getClassName)
+ ;
+
+ class_<PolyDiamondMultiplyDerived>("PolyDiamondMultiplyDerived")
+ .smart_ptr<std::shared_ptr<PolyDiamondMultiplyDerived>>()
+ .constructor<>()
+ .function("getClassName", &PolyDiamondMultiplyDerived::getClassName)
+ ;
+
+ function("embind_test_return_small_class_instance", &embind_test_return_small_class_instance);
+ function("embind_test_return_big_class_instance", &embind_test_return_big_class_instance);
+ function("embind_test_accept_small_class_instance", &embind_test_accept_small_class_instance);
+ function("embind_test_accept_big_class_instance", &embind_test_accept_big_class_instance);
+
+ function("embind_test_return_raw_base_ptr", embind_test_return_raw_base_ptr, allow_raw_pointer<ret_val>());
+ function("embind_test_return_raw_derived_ptr_as_base", embind_test_return_raw_derived_ptr_as_base, allow_raw_pointer<ret_val>());
+ function("embind_test_return_raw_sibling_derived_ptr_as_base", embind_test_return_raw_sibling_derived_ptr_as_base, allow_raw_pointer<ret_val>());
+ function("embind_test_return_raw_polymorphic_derived_ptr_as_base", embind_test_return_raw_polymorphic_derived_ptr_as_base, allow_raw_pointer<ret_val>());
+ function("embind_test_return_raw_polymorphic_sibling_derived_ptr_as_base", embind_test_return_raw_polymorphic_sibling_derived_ptr_as_base, allow_raw_pointer<ret_val>());
+ function("embind_test_return_raw_polymorphic_multiply_derived_ptr_as_base", embind_test_return_raw_polymorphic_multiply_derived_ptr_as_base, allow_raw_pointer<ret_val>());
+ function("embind_test_return_raw_polymorphic_multiply_derived_ptr_as_second_base", embind_test_return_raw_polymorphic_multiply_derived_ptr_as_second_base, allow_raw_pointer<ret_val>());
+ function("embind_test_return_raw_polymorphic_derived_four_times_not_bound_as_base", embind_test_return_raw_polymorphic_derived_four_times_not_bound_as_base, allow_raw_pointer<ret_val>());
+ function("embind_test_return_smart_derived_ptr", embind_test_return_smart_derived_ptr);
+ function("embind_test_return_smart_sibling_derived_ptr", embind_test_return_smart_sibling_derived_ptr);
+ function("embind_test_return_smart_multiply_derived_ptr", embind_test_return_smart_multiply_derived_ptr);
+ function("embind_test_return_smart_derived_thrice_ptr", embind_test_return_smart_derived_thrice_ptr);
+ function("embind_test_return_smart_base_ptr", embind_test_return_smart_base_ptr);
+ function("embind_test_return_smart_polymorphic_base_ptr", embind_test_return_smart_polymorphic_base_ptr);
+ function("embind_test_return_smart_polymorphic_derived_ptr", embind_test_return_smart_polymorphic_derived_ptr);
+ function("embind_test_return_smart_polymorphic_sibling_derived_ptr", embind_test_return_smart_polymorphic_sibling_derived_ptr);
+ function("embind_test_return_smart_polymorphic_multiply_derived_ptr", embind_test_return_smart_polymorphic_multiply_derived_ptr);
+ function("embind_test_return_poly_derived_twice_without_smart_pointer_as_poly_base", embind_test_return_poly_derived_twice_without_smart_pointer_as_poly_base);
+ function("embind_test_return_smart_poly_derived_thrice_ptr", embind_test_return_smart_poly_derived_thrice_ptr);
+ function("embind_test_return_smart_derived_ptr_as_base", embind_test_return_smart_derived_ptr_as_base);
+ function("embind_test_return_smart_derived_ptr_as_val", embind_test_return_smart_derived_ptr_as_val);
+ function("embind_test_return_smart_polymorphic_derived_ptr_as_base", embind_test_return_smart_polymorphic_derived_ptr_as_base);
+ function("embind_test_return_smart_polymorphic_sibling_derived_ptr_as_base", embind_test_return_smart_polymorphic_sibling_derived_ptr_as_base);
+ function("embind_test_get_class_name_via_base_ptr", embind_test_get_class_name_via_base_ptr, allow_raw_pointer<arg<0>>());
+ function("embind_test_get_class_name_via_second_base_ptr", embind_test_get_class_name_via_second_base_ptr, allow_raw_pointer<arg<0>>());
+ function("embind_test_get_class_name_via_polymorphic_base_ptr", embind_test_get_class_name_via_polymorphic_base_ptr, allow_raw_pointer<arg<0>>());
+ function("embind_test_get_class_name_via_polymorphic_second_base_ptr", embind_test_get_class_name_via_polymorphic_second_base_ptr, allow_raw_pointer<arg<0>>());
+ // todo: allow_raw_pointer should fail earlier if argument is not a pointer
+ function("embind_test_get_class_name_via_smart_base_ptr", embind_test_get_class_name_via_smart_base_ptr);
+ function("embind_test_get_class_name_via_reference_to_smart_base_ptr", embind_test_get_class_name_via_reference_to_smart_base_ptr);
+ function("embind_test_get_class_name_via_smart_second_base_ptr", embind_test_get_class_name_via_smart_second_base_ptr);
+ function("embind_test_get_class_name_via_smart_polymorphic_base_ptr", embind_test_get_class_name_via_smart_polymorphic_base_ptr);
+ function("embind_test_get_virtual_class_name_via_smart_polymorphic_base_ptr", embind_test_get_virtual_class_name_via_smart_polymorphic_base_ptr);
+ function("embind_test_get_class_name_via_smart_polymorphic_second_base_ptr", embind_test_get_class_name_via_smart_polymorphic_second_base_ptr);
+ function("embind_modify_smart_pointer_passed_by_reference", embind_modify_smart_pointer_passed_by_reference);
+ function("embind_attempt_to_modify_smart_pointer_when_passed_by_value", embind_attempt_to_modify_smart_pointer_when_passed_by_value);
+ function("embind_save_smart_base_pointer", embind_save_smart_base_pointer);
+
+ class_<Base2>("Base2")
+ .function("getField", &Base2::getField)
+ .property("field", &Base2::field2)
+ ;
+
+ class_<HasTwoBases, base<Base2>>("HasTwoBases")
+ .constructor()
;
class_<CustomStruct>("CustomStruct")
.constructor<>()
- .field("field", &CustomStruct::field)
+ .property("field", &CustomStruct::field)
+ .function("getField", &CustomStruct::getField)
;
enum_<Enum>("Enum")
@@ -319,22 +1817,246 @@ EMSCRIPTEN_BINDINGS(([]() {
;
function("emval_test_take_and_return_EnumClass", &emval_test_take_and_return_EnumClass);
- class InterfaceWrapper : public wrapper<Interface> {
- int method() {
- return call<int>("method");
- }
- TupleInStruct method2(const TupleInStruct& arg1, float arg2) {
- return call<TupleInStruct>("method2", arg1, arg2);
- }
- void method3() {
- return call<void>("method3");
- }
- };
- interface<InterfaceWrapper>("Interface")
+ function("emval_test_call_function", &emval_test_call_function);
+
+ function("emval_test_return_unique_ptr", &emval_test_return_unique_ptr);
+
+ function("emval_test_return_shared_ptr", &emval_test_return_shared_ptr);
+ function("emval_test_return_empty_shared_ptr", &emval_test_return_empty_shared_ptr);
+ function("emval_test_is_shared_ptr_null", &emval_test_is_shared_ptr_null);
+
+ function("emval_test_return_vector", &emval_test_return_vector);
+ function("emval_test_return_vector_of_vectors", &emval_test_return_vector_of_vectors);
+
+ register_vector<std::shared_ptr<StringHolder>>("SharedPtrVector");
+ function("emval_test_return_shared_ptr_vector", &emval_test_return_shared_ptr_vector);
+
+ function("get_module_property", &get_module_property);
+
+ register_vector<StringHolder>("StringHolderVector");
+ class_<VectorHolder>("VectorHolder")
+ .constructor<>()
+ .function("get", &VectorHolder::get)
+ .function("set", &VectorHolder::set)
;
- function("emval_test_call_method", &emval_test_call_method);
- function("emval_test_call_method2", &emval_test_call_method2);
- function("emval_test_call_method3", &emval_test_call_method3);
- function("emval_test_call_function", &emval_test_call_function);
-}));
+ function("test_string_with_vec", &test_string_with_vec);
+
+ register_map<std::string, int>("StringIntMap");
+ function("embind_test_get_string_int_map", embind_test_get_string_int_map);
+
+ class_<JSInterfaceHolder>("JSInterfaceHolder")
+ .constructor<JSInterface&>()
+ .function("callMethod", &JSInterfaceHolder::callMethod)
+ .function("callMethodUsingSharedPtr", &JSInterfaceHolder::callMethodUsingSharedPtr)
+ ;
+
+ function("embind_test_new_Object", &embind_test_new_Object);
+ function("embind_test_new_factory", &embind_test_new_factory);
+
+ class_<AbstractClass>("AbstractClass")
+ .smart_ptr<std::shared_ptr<AbstractClass>>()
+ .allow_subclass<AbstractClassWrapper>()
+ .function("abstractMethod", &AbstractClass::abstractMethod)
+ ;
+
+ function("getAbstractClass", &getAbstractClass);
+ function("callAbstractMethod", &callAbstractMethod);
+
+ class_<HasExternalConstructor>("HasExternalConstructor")
+ .constructor(&createHasExternalConstructor)
+ .function("getString", &HasExternalConstructor::getString)
+ ;
+
+ auto HeldBySmartPtr_class = class_<HeldBySmartPtr>("HeldBySmartPtr");
+ HeldBySmartPtr_class
+ .smart_ptr<CustomSmartPtr<HeldBySmartPtr>>()
+ .smart_ptr_constructor(&std::make_shared<HeldBySmartPtr, int, std::string>)
+ .class_function("newCustomPtr", HeldBySmartPtr::newCustomPtr)
+ .function("returnThis", &takesHeldBySmartPtrSharedPtr)
+ .property("i", &HeldBySmartPtr::i)
+ .property("s", &HeldBySmartPtr::s)
+ ;
+ function("takesHeldBySmartPtr", &takesHeldBySmartPtr);
+ function("takesHeldBySmartPtrSharedPtr", &takesHeldBySmartPtrSharedPtr);
+
+ class_<HeldByCustomSmartPtr>("HeldByCustomSmartPtr")
+ .smart_ptr<std::shared_ptr<HeldByCustomSmartPtr>>()
+ .smart_ptr_constructor(&HeldByCustomSmartPtr::create)
+ .class_function("createSharedPtr", &HeldByCustomSmartPtr::createSharedPtr)
+ .property("i", &HeldByCustomSmartPtr::i)
+ .property("s", &HeldByCustomSmartPtr::s)
+ ;
+
+ function("passThroughRawPtr", &passThroughRawPtr, allow_raw_pointers());
+ function("passThroughCustomSmartPtr", &passThroughCustomSmartPtr);
+
+ function("char_to_string", &char_to_string);
+ function("signed_char_to_string", &signed_char_to_string);
+ function("unsigned_char_to_string", &unsigned_char_to_string);
+ function("short_to_string", &short_to_string);
+ function("unsigned_short_to_string", &unsigned_short_to_string);
+ function("int_to_string", &int_to_string);
+ function("unsigned_int_to_string", &unsigned_int_to_string);
+ function("long_to_string", &long_to_string);
+ function("unsigned_long_to_string", &unsigned_long_to_string);
+
+ function("overloaded_function", (int(*)(int))&overloaded_function);
+ function("overloaded_function", (int(*)(int, int))&overloaded_function);
+
+ class_<MultipleCtors>("MultipleCtors")
+ .constructor<int>()
+ .constructor<int, int>()
+ .constructor<int, int, int>()
+ .function("WhichCtorCalled", &MultipleCtors::WhichCtorCalled);
+
+ class_<MultipleOverloads>("MultipleOverloads")
+ .constructor<>()
+ .function("Func", (int(MultipleOverloads::*)(int))&MultipleOverloads::Func)
+ .function("Func", (int(MultipleOverloads::*)(int,int))&MultipleOverloads::Func)
+ .function("WhichFuncCalled", &MultipleOverloads::WhichFuncCalled)
+ .class_function("StaticFunc", (int(*)(int))&MultipleOverloads::StaticFunc)
+ .class_function("StaticFunc", (int(*)(int,int))&MultipleOverloads::StaticFunc)
+ .class_function("WhichStaticFuncCalled", &MultipleOverloads::WhichStaticFuncCalled);
+
+ class_<MultipleOverloadsDerived, base<MultipleOverloads> >("MultipleOverloadsDerived")
+ .constructor<>()
+ .function("Func", (int(MultipleOverloadsDerived::*)(int,int,int))&MultipleOverloadsDerived::Func)
+ .function("Func", (int(MultipleOverloadsDerived::*)(int,int,int,int))&MultipleOverloadsDerived::Func)
+ .class_function("StaticFunc", (int(*)(int,int,int))&MultipleOverloadsDerived::StaticFunc)
+ .class_function("StaticFunc", (int(*)(int,int,int,int))&MultipleOverloadsDerived::StaticFunc);
+}
+
+// tests for out-of-order registration
+
+class SecondElement {
+};
+
+class FirstElement {
+};
+
+struct OrderedTuple {
+ FirstElement first;
+ SecondElement second;
+};
+
+struct OrderedStruct {
+ FirstElement first;
+ SecondElement second;
+};
+
+OrderedTuple getOrderedTuple() {
+ return OrderedTuple();
+}
+
+OrderedStruct getOrderedStruct() {
+ return OrderedStruct();
+}
+
+EMSCRIPTEN_BINDINGS(order) {
+ value_tuple<OrderedTuple>("OrderedTuple")
+ .element(&OrderedTuple::first)
+ .element(&OrderedTuple::second)
+ ;
+
+ value_struct<OrderedStruct>("OrderedStruct")
+ .field("first", &OrderedStruct::first)
+ .field("second", &OrderedStruct::second)
+ ;
+
+ class_<SecondElement>("SecondElement")
+ ;
+
+ class_<FirstElement>("FirstElement")
+ ;
+
+ function("getOrderedTuple", &getOrderedTuple);
+ function("getOrderedStruct", &getOrderedStruct);
+}
+
+// tests for unbound types
+
+template<typename T>
+T passThrough(T t) {
+ return t;
+}
+
+struct UnboundClass {
+};
+
+struct HasUnboundBase : public UnboundClass {
+ static void noop() {
+ }
+};
+
+HasUnboundBase getHasUnboundBase(HasUnboundBase f) {
+ return f;
+}
+
+struct HasConstructorUsingUnboundArgument {
+ HasConstructorUsingUnboundArgument(UnboundClass) {
+ }
+};
+
+struct SecondUnboundClass {
+};
+
+struct HasConstructorUsingUnboundArgumentAndUnboundBase : public SecondUnboundClass {
+ HasConstructorUsingUnboundArgumentAndUnboundBase(UnboundClass) {
+ }
+};
+
+struct BoundClass {
+ UnboundClass method(UnboundClass t) {
+ return t;
+ }
+
+ static UnboundClass classfunction(UnboundClass t) {
+ return t;
+ }
+
+ UnboundClass property;
+};
+
+EMSCRIPTEN_BINDINGS(incomplete) {
+ function("getUnboundClass", &passThrough<UnboundClass>);
+
+ class_<HasUnboundBase, base<UnboundClass>>("HasUnboundBase")
+ .class_function("noop", &HasUnboundBase::noop)
+ ;
+ function("getHasUnboundBase", &passThrough<HasUnboundBase>);
+
+ class_<HasConstructorUsingUnboundArgument>("HasConstructorUsingUnboundArgument")
+ .constructor<UnboundClass>()
+ ;
+
+ class_<HasConstructorUsingUnboundArgumentAndUnboundBase, base<SecondUnboundClass>>("HasConstructorUsingUnboundArgumentAndUnboundBase")
+ .constructor<UnboundClass>()
+ ;
+
+ class_<BoundClass>("BoundClass")
+ .constructor<>()
+ .function("method", &BoundClass::method)
+ .class_function("classfunction", &BoundClass::classfunction)
+ .property("property", &BoundClass::property)
+ ;
+}
+
+class Noncopyable {
+ Noncopyable(const Noncopyable&) = delete;
+ Noncopyable& operator=(const Noncopyable&) = delete;
+
+public:
+ Noncopyable() {}
+
+ std::string method() const {
+ return "foo";
+ }
+};
+
+EMSCRIPTEN_BINDINGS(noncopyable) {
+ class_<Noncopyable>("Noncopyable")
+ .constructor<>()
+ .function("method", &Noncopyable::method)
+ ;
+}
diff --git a/tests/embind/embind_test.js b/tests/embind/embind_test.js
deleted file mode 100644
index 8c61553b..00000000
--- a/tests/embind/embind_test.js
+++ /dev/null
@@ -1,393 +0,0 @@
-//=== testing glue
-
-function module(ignore, func) {
- func({ Emscripten: Module });
-}
-
-function fixture(name, info) {
- Module.print('fixture: ' + name);
- for (var test in info) {
- var f = info[test];
- if (typeof f != 'function') continue;
- Module.print('--test: ' + test);
- // TODO: Base fixture!
- f();
- }
-}
-
-assert.true = assert;
-
-assert.equal = function(x, y) {
- assert(x == y);
-}
-
-assert.notEqual = function(x, y) {
- assert(x != y);
-}
-
-assert.throws = function(exc, func) {
- var ret;
- try {
- func();
- } catch(e) {
- ret = e;
- }
- assert(ret); // TODO: check exc vs e
- return ret;
-}
-
-assert.instanceof = function(inst, clazz) {
- assert(inst instanceof clazz);
-}
-
-assert.deepEqual = function(x, y) {
- assert(JSON.stringify(x) == JSON.stringify(y));
-}
-
-//===
-
-module({
- Emscripten: '../build/Emscripten.js'
-}, function(imports) {
- var cm = imports.Emscripten;
-
- var checkForLeaks = {
- setUp: function() {
- this.originalBlockCount = cm.mallinfo().uordblks;
- },
- tearDown: function() {
- assert.equal(this.originalBlockCount, cm.mallinfo().uordblks);
- },
- };
-
- fixture("embind", {
- baseFixture: checkForLeaks,
-
- "test value creation": function() {
- assert.equal(15, cm.emval_test_new_integer());
- assert.equal("Hello everyone", cm.emval_test_new_string());
-
- var object = cm.emval_test_new_object();
- assert.equal('bar', object.foo);
- assert.equal(1, object.baz);
- },
-
- "test passthrough": function() {
- var a = {foo: 'bar'};
- var b = cm.emval_test_passthrough(a);
- a.bar = 'baz';
- assert.equal('baz', b.bar);
-
- assert.equal(0, cm.count_emval_handles());
- },
-
- "test void return converts to undefined": function() {
- assert.equal(undefined, cm.emval_test_return_void());
- },
-
- "test booleans can be marshalled": function() {
- assert.equal(false, cm.emval_test_not(true));
- assert.equal(true, cm.emval_test_not(false));
- },
-
- "test convert double to unsigned": function() {
- var rv = cm.emval_test_as_unsigned(1.5);
- assert.equal('number', typeof rv);
- assert.equal(1, rv);
- assert.equal(0, cm.count_emval_handles());
- },
-
- "test get length of array": function() {
- assert.equal(10, cm.emval_test_get_length([0, 1, 2, 3, 4, 5, 'a', 'b', 'c', 'd']));
- assert.equal(0, cm.count_emval_handles());
- },
-
- "test add a bunch of things": function() {
- assert.equal(66.0, cm.emval_test_add(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11));
- assert.equal(0, cm.count_emval_handles());
- },
-
- "test sum array": function() {
- assert.equal(66, cm.emval_test_sum([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]));
- assert.equal(0, cm.count_emval_handles());
- },
-
- "test strings": function() {
- assert.equal("foobar", "foo" + "bar");
- assert.equal("foobar", cm.emval_test_take_and_return_std_string("foobar"));
-
- assert.equal("foobar", cm.emval_test_take_and_return_std_string_const_ref("foobar"));
- },
-
- "test no memory leak when passing strings in by const reference": function() {
- var original = cm.mallinfo().uordblks;
- cm.emval_test_take_and_return_std_string_const_ref("foobar");
- assert.equal(original, cm.mallinfo().uordblks);
- },
- });
-
- fixture("classes", {
- baseFixture: checkForLeaks,
-
- "test class instance": function() {
- var a = {foo: 'bar'};
- var c = new cm.ValHolder(a);
- assert.equal('bar', c.getVal().foo);
-
- c.setVal('1234');
- assert.equal('1234', c.getVal());
-
- assert.equal(1239, c.returnIntPlusFive(1234));
-
- c.delete();
- assert.equal(0, cm.count_emval_handles());
- },
-
- "test class methods": function() {
- assert.equal(10, cm.ValHolder.some_class_method(10));
- },
-
- "test can't call methods on deleted class instances": function() {
- var c = new cm.ValHolder(undefined);
- c.delete();
- assert.throws(cm.BindingError, function() {
- c.getVal();
- });
- assert.throws(cm.BindingError, function() {
- c.delete();
- });
- },
-
- "test isinstance": function() {
- var c = new cm.ValHolder(undefined);
- assert.instanceof(c, cm.ValHolder);
- c.delete();
- },
-
- "test can return class instances by value": function() {
- var c = cm.emval_test_return_ValHolder();
- assert.deepEqual({}, c.getVal());
- c.delete();
- },
-
- "test can pass class instances to functions by reference": function() {
- var a = {a:1};
- var c = new cm.ValHolder(a);
- cm.emval_test_set_ValHolder_to_empty_object(c);
- assert.deepEqual({}, c.getVal());
- c.delete();
- },
-
- "test can access struct fields": function() {
- var c = new cm.CustomStruct();
- assert.equal(10, c.field);
- c.delete();
- },
-
- "test can set struct fields": function() {
- var c = new cm.CustomStruct();
- c.field = 15;
- assert.equal(15, c.field);
- c.delete();
- },
-
- "test assignment returns value": function() {
- var c = new cm.CustomStruct();
- assert.equal(15, c.field = 15);
- c.delete();
- },
-
- "test assigning string to integer raises TypeError": function() {
- var c = new cm.CustomStruct();
-
- var e = assert.throws(TypeError, function() {
- c.field = "hi";
- });
- assert.equal('Cannot convert "hi" to int', e.message);
-
- var e = assert.throws(TypeError, function() {
- c.field = {foo:'bar'};
- });
- assert.equal('Cannot convert "[object Object]" to int', e.message);
-
- c.delete();
- },
-
- "test can return tuples by value": function() {
- var c = cm.emval_test_return_TupleVector();
- assert.deepEqual([1, 2, 3], c);
- },
-
- "test tuples can contain tuples": function() {
- var c = cm.emval_test_return_TupleVectorTuple();
- assert.deepEqual([[1, 2, 3]], c);
- },
-
- "test can pass tuples by value": function() {
- var c = cm.emval_test_take_and_return_TupleVector([4, 5, 6]);
- assert.deepEqual([4, 5, 6], c);
- },
-
- "test can return structs by value": function() {
- var c = cm.emval_test_return_StructVector();
- assert.deepEqual({x: 1, y: 2, z: 3}, c);
- },
-
- "test can pass structs by value": function() {
- var c = cm.emval_test_take_and_return_StructVector({x: 4, y: 5, z: 6});
- assert.deepEqual({x: 4, y: 5, z: 6}, c);
- },
-
- "test can pass and return tuples in structs": function() {
- var d = cm.emval_test_take_and_return_TupleInStruct({field: [1, 2, 3]});
- assert.deepEqual({field: [1, 2, 3]}, d);
- },
-
- "test can clone handles": function() {
- assert.equal(0, cm.count_emval_handles());
-
- var a = new cm.ValHolder({});
- var b = a.clone();
- a.delete();
-
- assert.equal(1, cm.count_emval_handles());
-
- assert.throws(cm.BindingError, function() {
- a.delete();
- });
- b.delete();
-
- assert.equal(0, cm.count_emval_handles());
- },
-
- "test can't clone if already deleted": function() {
- var a = new cm.ValHolder({});
- a.delete();
- assert.throws(cm.BindingError, function() {
- a.clone();
- });
- },
-
- "test moving handles is a clone+delete": function() {
- var a = new cm.ValHolder({});
- var b = a.move();
- assert.throws(cm.BindingError, function() {
- a.delete();
- });
- assert.equal(1, cm.count_emval_handles());
- b.delete();
- assert.equal(0, cm.count_emval_handles());
- },
-
- "test StringHolder": function() {
- var a = new cm.StringHolder("foobar");
- assert.equal("foobar", a.get());
-
- a.set("barfoo");
- assert.equal("barfoo", a.get());
- a.delete();
- },
- });
-
- fixture("embind enumerations", {
- baseFixture: checkForLeaks,
-
- "test can compare enumeration values": function() {
- assert.equal(cm.Enum.ONE, cm.Enum.ONE);
- assert.notEqual(cm.Enum.ONE, cm.Enum.TWO);
- },
-
- "test repr includes enum value": function() {
- return; // XXX IMVU?
- assert.equal('<#Enum_ONE {}>', IMVU.repr(cm.Enum.ONE));
- assert.equal('<#Enum_TWO {}>', IMVU.repr(cm.Enum.TWO));
- },
-
- "test instanceof": function() {
- assert.instanceof(cm.Enum.ONE, cm.Enum);
- },
-
- "test can pass and return enumeration values to functions": function() {
- assert.equal(cm.Enum.TWO, cm.emval_test_take_and_return_Enum(cm.Enum.TWO));
- },
- });
-
- fixture("C++11 enum class", {
- baseFixture: checkForLeaks,
-
- "test can compare enumeration values": function() {
- assert.equal(cm.EnumClass.ONE, cm.EnumClass.ONE);
- assert.notEqual(cm.EnumClass.ONE, cm.EnumClass.TWO);
- },
-
- "test repr includes enum value": function() {
- return; // XXX IMVU?
- assert.equal('<#EnumClass_ONE {}>', IMVU.repr(cm.EnumClass.ONE));
- assert.equal('<#EnumClass_TWO {}>', IMVU.repr(cm.EnumClass.TWO));
- },
-
- "test instanceof": function() {
- assert.instanceof(cm.EnumClass.ONE, cm.EnumClass);
- },
-
- "test can pass and return enumeration values to functions": function() {
- assert.equal(cm.EnumClass.TWO, cm.emval_test_take_and_return_EnumClass(cm.EnumClass.TWO));
- },
- });
-
- fixture("emval call tests", {
- "test can call functions from C++": function() {
- var called = false;
- cm.emval_test_call_function(function(i, f, tv, sv) {
- called = true;
- assert.equal(10, i);
- assert.equal(1.5, f);
- assert.deepEqual([1.25, 2.5, 3.75], tv);
- assert.deepEqual({x: 1.25, y: 2.5, z: 3.75}, sv);
- }, 10, 1.5, [1.25, 2.5, 3.75], {x: 1.25, y: 2.5, z: 3.75});
- assert.true(called);
- },
- });
-
- fixture("interfaces", {
- baseFixture: checkForLeaks,
-
- "test can wrap JS object in native interface": function() {
- var foo = {
- calls: [],
- method: function() {
- this.calls.push('called');
- return 10;
- }
- };
-
- assert.equal(10, cm.emval_test_call_method(foo));
- assert.deepEqual(['called'], foo.calls);
- },
-
- "test can pass arguments and return complicated values": function() {
- var calls = [];
- var foo = {
- method2: function(arg1, arg2) {
- calls.push([arg1, arg2]);
- return arg1;
- }
- };
-
- var result = cm.emval_test_call_method2(foo, {field: [1, 2, 3]}, 7);
- assert.deepEqual({field: [1, 2, 3]}, result);
- assert.deepEqual([[{field: [1, 2, 3]}, 7]], calls);
- },
-
- "test can call interface methods that return nothing": function() {
- var calls = [];
- var foo = {
- method3: function() {
- calls.push('called');
- }
- };
- cm.emval_test_call_method3(foo);
- assert.deepEqual(['called'], calls);
- },
- });
-});
diff --git a/tests/embind/imvu_test_adapter.js b/tests/embind/imvu_test_adapter.js
new file mode 100755
index 00000000..421e86c8
--- /dev/null
+++ b/tests/embind/imvu_test_adapter.js
@@ -0,0 +1,616 @@
+/* The embind test suite (embind.test.js) is configured to be runnable in two different testing engines:
+ - The Emscripten python test runner (open-source in emscripten repository) and
+ - The IMVU test runner (closed-source in IMVU repository)
+
+ Embind (and its tests) were originally developed in IMVU repository, which is the reason for two testing architectures.
+ This adapter file is used when the embind tests are run as part of the Emscripten test runner, to provide the necessary glue code to adapt the tests to Emscripten runner.
+
+ To run the Embind tests using the Emscripten test runner, invoke 'python tests/runner.py other.test_embind' in the Emscripten root directory.
+*/
+
+/* global Module, console, global, process */
+
+//=== testing glue
+
+function module(ignore, func) {
+ func({ Emscripten: Module });
+}
+
+/*global IMVU:true, TEST_MAX_OUTPUT_SIZE*/
+//(function() {
+// "use strict";
+
+ // { beforeTest: function,
+ // afterTest: function }
+ var superFixtures = [];
+
+ function registerSuperFixture(superFixture) {
+ superFixtures.push(superFixture);
+ }
+
+ // { fixture: Fixture instance,
+ // name: string,
+ // body: function() }
+ var allTests = [];
+
+ function test(name, fn) {
+ if (arguments.length !== 2) {
+ throw new TypeError("test requires 2 arguments");
+ }
+
+ if (undefined !== activeFixture && activeFixture.abstract) {
+ activeFixture.abstractTests.push({
+ name: name,
+ body: fn });
+ } else {
+ var fixtureName = (undefined !== activeFixture)? activeFixture.name + ': ' : '';
+ allTests.push({
+ name: fixtureName + name,
+ body: fn,
+ fixture: activeFixture });
+ }
+ }
+
+ function runTest(test, continuation) {
+ try {
+ var afterTests = [];
+
+ for (var i = 0; i < superFixtures.length; ++i) {
+ var superFixture = superFixtures[i];
+
+ var superScope = {};
+ superFixture.beforeTest.call(superScope);
+ afterTests.push(superFixture.afterTest.bind(superScope));
+ }
+
+ var testScope = test.fixture ?
+ Object.create(test.fixture.scope) :
+ {};
+
+ var runSetUp = function(fixtureObject) {
+ if (undefined === fixtureObject) {
+ return;
+ }
+ runSetUp(fixtureObject.parent);
+ fixtureObject.setUp.call(testScope);
+ afterTests.push(fixtureObject.tearDown.bind(testScope));
+ };
+ runSetUp(test.fixture);
+
+ test.body.call(testScope);
+ while (afterTests.length) {
+ afterTests.pop()();
+ }
+ return false;
+ } catch (e) {
+ console.error('error:', e);
+ return {stack: e.stack, e: e};
+ }
+ }
+
+ function run_all(reporter) {
+ for (var i = 0; i < allTests.length; ++i) {
+ var test = allTests[i];
+ reporter({
+ type: 'test-start',
+ name: test.name
+ });
+
+ var failed = runTest(test);
+ if (failed) {
+ reporter({
+ type: 'test-complete',
+ name: test.name,
+ verdict: 'FAIL',
+ stack: failed.stack,
+ e: failed.e
+ });
+ return false;
+ } else {
+ reporter({
+ type: 'test-complete',
+ name: test.name,
+ verdict: 'PASS'
+ });
+ }
+ }
+
+ reporter({
+ type: 'all-tests-complete'
+ });
+
+ allTests = [];
+ return true;
+ }
+
+ var activeFixture;
+
+ function Fixture(parent, name, definition, abstract_) {
+ if (!(definition instanceof Function)) {
+ throw new TypeError("fixture's 2nd argument must be a function");
+ }
+
+ this.name = name;
+ this.parent = parent;
+ this.abstract = abstract_;
+ if (this.abstract) {
+ // { name: string,
+ // body: function }
+ this.abstractTests = [];
+ }
+
+ if (this.parent !== undefined) {
+ this.parent.addAbstractTests(this);
+ }
+
+ this.scope = (this.parent === undefined ? {} : Object.create(this.parent.scope));
+ this.scope.setUp = function(setUp) {
+ this.setUp = setUp;
+ }.bind(this);
+ this.scope.tearDown = function(tearDown) {
+ this.tearDown = tearDown;
+ }.bind(this);
+
+ if (undefined !== activeFixture) {
+ throw new TypeError("Cannot define a fixture within another fixture");
+ }
+
+ activeFixture = this;
+ try {
+ definition.call(this.scope);
+ }
+ finally {
+ activeFixture = undefined;
+ }
+ }
+ Fixture.prototype.setUp = function defaultSetUp() {
+ };
+ Fixture.prototype.tearDown = function defaultTearDown() {
+ };
+ Fixture.prototype.addAbstractTests = function(concreteFixture) {
+ if (this.abstract) {
+ for (var i = 0; i < this.abstractTests.length; ++i) {
+ var test = this.abstractTests[i];
+ allTests.push({
+ name: concreteFixture.name + ': ' + test.name,
+ body: test.body,
+ fixture: concreteFixture});
+ }
+ }
+ if (this.parent) {
+ this.parent.addAbstractTests(concreteFixture);
+ }
+ };
+
+ Fixture.prototype.extend = function(fixtureName, definition) {
+ return new Fixture(this, fixtureName, definition, false);
+ };
+
+ function fixture(fixtureName, definition) {
+ return new Fixture(undefined, fixtureName, definition, false);
+ }
+ fixture.abstract = function(fixtureName, definition) {
+ return new Fixture(undefined, fixtureName, definition, true);
+ };
+
+ var AssertionError = Error;
+
+ function fail(exception, info) {
+ exception.info = info;
+ throw exception;
+ }
+
+ var formatTestValue = function(v) {
+ return v.toString();
+ /*
+ var s = IMVU.repr(v, TEST_MAX_OUTPUT_SIZE + 1);
+ if (s.length <= TEST_MAX_OUTPUT_SIZE) {
+ return s;
+ }
+ return s.substring(0, TEST_MAX_OUTPUT_SIZE) + '...';
+ */
+ };
+
+// var assert = {
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // GENERAL STATUS
+
+ assert.fail = function(info) {
+ info = info || "assert.fail()";
+ fail(new AssertionError(info));
+ },
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // BOOLEAN TESTS
+
+ assert['true'] = function(value) {
+ if (!value) {
+ fail(new AssertionError("expected truthy, actual " + formatTestValue(value)),
+ {Value: value});
+ }
+ },
+
+ assert['false'] = function(value) {
+ if (value) {
+ fail(new AssertionError("expected falsy, actual " + formatTestValue(value)),
+ {Value: value});
+ }
+ },
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // SCALAR COMPARISON
+
+ assert.equal = function(expected, actual) {
+ if (expected !== actual) {
+ fail(new AssertionError('expected: ' + formatTestValue(expected) + ', actual: ' + formatTestValue(actual)),
+ {Expected: expected, Actual: actual});
+ }
+ },
+
+ assert.notEqual = function(expected, actual) {
+ if (expected === actual) {
+ fail(new AssertionError('actual was equal to: ' + formatTestValue(expected)));
+ }
+ },
+
+ assert.greater = function(lhs, rhs) {
+ if (lhs <= rhs) {
+ fail(new AssertionError(formatTestValue(lhs) + ' not greater than ' + formatTestValue(rhs)));
+ }
+ },
+
+ assert.less = function(lhs, rhs) {
+ if (lhs >= rhs) {
+ fail(new AssertionError(formatTestValue(lhs) + ' not less than ' + formatTestValue(rhs)));
+ }
+ },
+
+ assert.greaterOrEqual = function(lhs, rhs) {
+ if (lhs < rhs) {
+ fail(new AssertionError(formatTestValue(lhs) + ' not greater than or equal to ' + formatTestValue(rhs)));
+ }
+ },
+
+ assert.lessOrEqual = function(lhs, rhs) {
+ if (lhs > rhs) {
+ fail(new AssertionError(formatTestValue(lhs) + ' not less than or equal to ' + formatTestValue(rhs)));
+ }
+ },
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // DEEP COMPARISON
+
+ assert.deepEqual = function(expected, actual) {
+ if (!_.isEqual(expected, actual)) {
+ fail(new AssertionError('expected: ' + formatTestValue(expected) + ', actual: ' + formatTestValue(actual)),
+ {Expected: expected, Actual: actual});
+ }
+ },
+
+ assert.notDeepEqual = function(expected, actual) {
+ if (_.isEqual(expected, actual)) {
+ fail(new AssertionError('expected: ' + formatTestValue(expected) + ' and actual: ' + formatTestValue(actual) + ' were equal'));
+ }
+ },
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // FLOATING POINT
+
+ assert.nearEqual = function( expected, actual, tolerance ) {
+ if( tolerance === undefined ) {
+ tolerance = 0.0;
+ }
+ if( expected instanceof Array && actual instanceof Array ) {
+ assert.equal(expected.length, actual.length);
+ for( var i=0; i<expected.length; ++i ) {
+ assert.nearEqual(expected[i], actual[i], tolerance);
+ }
+ return;
+ }
+ if( Math.abs(expected - actual) > tolerance ) {
+ fail( new AssertionError('expected: ' + formatTestValue(expected) + ', actual: ' + formatTestValue(actual) +
+ ', tolerance: ' + formatTestValue(tolerance) + ', diff: ' + formatTestValue(actual-expected) ),
+ { Expected:expected, Actual:actual, Tolerance:tolerance } );
+ }
+ },
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // STRING
+
+ assert.inString = function(expected, string){
+ if (-1 === string.indexOf(expected)){
+ fail(new AssertionError('expected: ' + formatTestValue(expected) + ' not in string: ' + formatTestValue(string)),
+ {Expected: expected, 'String': string});
+ }
+ },
+
+ assert.notInString = function(expected, string){
+ if (-1 !== string.indexOf(expected)){
+ fail(new AssertionError('unexpected: ' + formatTestValue(expected) + ' in string: ' + formatTestValue(string)),
+ {Expected: expected, 'String': string});
+ }
+ },
+
+ assert.matches = function(re, string) {
+ if (!re.test(string)) {
+ fail(new AssertionError('regexp ' + re + ' does not match: ' + string));
+ }
+ },
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // ARRAY
+
+ assert.inArray = function(expected, array) {
+ var found = false;
+ _.each(array, function(element){
+ if (_.isEqual(expected, element)){
+ found = true;
+ }
+ });
+ if (!found){
+ fail(new AssertionError('expected: ' + formatTestValue(expected) + ' not found in array: ' + formatTestValue(array)),
+ {Expected: expected, 'Array': array});
+ }
+ },
+
+ assert.notInArray = function(expected, array) {
+ var found = false;
+ _.each(array, function(element){
+ if (_.isEqual(expected, element)){
+ found = true;
+ }
+ });
+ if (found){
+ fail(new AssertionError('unexpected: ' + formatTestValue(expected) + ' found in array: ' + formatTestValue(array)),
+ {Expected: expected, 'Array': array});
+ }
+ },
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // OBJECTS
+
+ assert.hasKey = function (key, object) {
+ if (!(key in object)) {
+ fail(new AssertionError('Key ' + formatTestValue(key) + ' is not in object: ' + formatTestValue(object)));
+ }
+ },
+
+ assert.notHasKey = function (key, object) {
+ if (key in object) {
+ fail(new AssertionError('Unexpected key ' + formatTestValue(key) + ' is found in object: ' + formatTestValue(object)));
+ }
+ },
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // EXCEPTIONS
+
+ assert.throws = function(exception, fn) {
+ try {
+ fn();
+ } catch (e) {
+ if (e instanceof exception) {
+ return e;
+ }
+ fail(new AssertionError('Expected to throw "' + exception.name + '", actually threw: ' + formatTestValue(e) + ': ' + e.message),
+ {Expected: exception, Actual: e});
+ }
+ throw new AssertionError('did not throw');
+ },
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // TYPE
+
+ assert['instanceof'] = function(actual, type) {
+ if(!(actual instanceof type)) {
+ fail(new AssertionError(formatTestValue(actual) + ' not instance of ' + formatTestValue(type)),
+ {Type: type, Actual: actual});
+ }
+ },
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // DOM ASSERTIONS
+
+ // TODO: lift into separate file?
+ assert.dom = {
+ present: function(domElement){
+ if (!$(domElement).length) {
+ fail(new AssertionError(decipherDomElement(domElement) + ' should be present'));
+ }
+ },
+
+ notPresent: function(selector){
+ assert.equal(0, $(selector).length);
+ },
+
+ hasTag: function(tag, domElement) {
+ var elementTag = $(domElement)[0].tagName.toLowerCase();
+ if (elementTag !== tag.toLowerCase()) {
+ fail(new AssertionError(decipherDomElement(domElement) + ' expected to have tag name ' + formatTestValue(tag) + ', was ' + formatTestValue(elementTag) + ' instead'));
+ }
+ },
+
+ hasClass: function(className, domElement) {
+ if (!$(domElement).hasClass(className)){
+ fail(new AssertionError(decipherDomElement(domElement) + ' expected to have class '+ formatTestValue(className) + ', has ' + formatTestValue($(domElement).attr('class')) + ' instead'));
+ }
+ },
+
+ notHasClass: function(className, domElement) {
+ assert.dom.present(domElement); // if domElement is empty, .hasClass will always return false
+ if ($(domElement).hasClass(className)){
+ fail(new AssertionError(decipherDomElement(domElement) + ' expected NOT to have class '+ formatTestValue(className)));
+ }
+ },
+
+ hasAttribute: function(attributeName, selector) {
+ assert['true']($(selector).is('[' + attributeName + ']'));
+ },
+
+ notHasAttribute: function(attributeName, selector) {
+ assert.dom.present(selector);
+ assert['false']($(selector).is('[' + attributeName + ']'));
+ },
+
+ attr: function(value, attributeName, selector) {
+ assert.equal(value, $(selector).attr(attributeName));
+ },
+
+ attributeValues: function (values, selector) {
+ var $el = $(selector);
+ _(values).each(function (val, key) {
+ assert.equal(val, $el.attr(key));
+ });
+ },
+
+ text: function(expected, selector) {
+ assert.equal(expected, $(selector).text());
+ },
+
+ value: function(expected, selector) {
+ assert.equal(expected, $(selector).val());
+ },
+
+ count: function(elementCount, selector) {
+ assert.equal(elementCount, $(selector).length);
+ },
+
+ visible: function(domElement) {
+ if (!$(domElement).is(':visible')) {
+ fail(new AssertionError(decipherDomElement(domElement) + ' expected to be visible'));
+ }
+ },
+
+ notVisible: function(domElement) {
+ assert.dom.present(domElement);
+ if ($(domElement).is(':visible')) {
+ fail(new AssertionError(decipherDomElement(domElement) + ' expected to be NOT visible'));
+ }
+ },
+
+ disabled: function(domElement) {
+ if (!$(domElement).is(':disabled')) {
+ fail(new AssertionError(decipherDomElement(domElement) + ' expected to be disabled'));
+ }
+ },
+
+ enabled: function(domElement) {
+ if (!$(domElement).is(':enabled')) {
+ fail(new AssertionError(decipherDomElement(domElement) + ' expected to be enabled'));
+ }
+ },
+
+ focused: function(selector) {
+ var expected = $(selector)[0];
+ var actual = document.activeElement;
+ if (expected !== actual) {
+ throw new AssertionError(actual.outerHTML + ' has focus. expected: ' + expected.outerHTML);
+ }
+ },
+
+ notFocused: function(selector) {
+ var expected = $(selector)[0];
+ var actual = document.activeElement;
+ if (expected === actual) {
+ throw new AssertionError(expected.outerHTML + ' expected not to have focus.');
+ }
+ },
+
+ html: function(expected, selector) {
+ assert.equal(expected, $(selector).html());
+ },
+
+ css: function(expected, propertyName, selector) {
+ assert.equal(expected, $(selector).css(propertyName));
+ },
+
+ empty: function(selectorOrJQueryObject) {
+ var el = selectorOrJQueryObject;
+ assert.dom.present(el);
+ if (!$(el).is(':empty')) {
+ fail(new AssertionError(decipherDomElement(el) + ' expected to be empty'));
+ }
+ },
+
+ notEmpty: function(selectorOrJQueryObject) {
+ var el = selectorOrJQueryObject;
+ assert.dom.present(el);
+ if ($(el).is(':empty')) {
+ fail(new AssertionError(decipherDomElement(el) + ' expected NOT to be empty'));
+ }
+ }
+ };
+// };
+
+ function decipherDomElement(selectorOrJQueryObject) {
+ if (typeof selectorOrJQueryObject === 'string') {
+ return 'Selector ' + formatTestValue(selectorOrJQueryObject);
+ } else if (typeof selectorOrJQueryObject === 'object') {
+ return "'" + selectorOrJQueryObject[0] + "'";
+ }
+ }
+
+ var g = 'undefined' === typeof window ? global : window;
+
+ // synonyms
+ assert.equals = assert.equal;
+ assert.notEquals = assert.notEqual;
+ assert['null'] = assert.equal.bind(null, null);
+ assert.notNull = assert.notEqual.bind(null, null);
+ assert['undefined'] = assert.equal.bind(null, undefined);
+ assert.notUndefined = assert.notEqual.bind(null, undefined);
+
+ // ES3 synonyms
+ assert.false_ = assert['false'];
+ assert.true_ = assert['true'];
+
+ g.registerSuperFixture = registerSuperFixture;
+ g.test = test;
+ g.run_all = run_all;
+ g.fixture = fixture;
+// g.repr = IMVU.repr;
+ g.AssertionError = AssertionError;
+ g.assert = assert;
+ g.test = test;
+ g.TEST_MAX_OUTPUT_SIZE = 1024;
+
+ g.setTimeout = function(fn, time) {
+ if (time === 1 || time === 0){
+ fn();
+ return 0;
+ }
+ throw new AssertionError("Don't call setTimeout in tests. Use fakes.");
+ };
+
+ g.setInterval = function() {
+ throw new AssertionError("Don't call setInterval in tests. Use fakes.");
+ };
+
+ if (typeof process !== 'undefined') {
+ process.nextTick = function() {
+ throw new AssertionError("Don't call process.nextTick in tests. Use fakes.");
+ };
+ }
+
+ Math.random = function() {
+ throw new AssertionError("Don't call Math.random in tests. Use fakes.");
+ };
+
+ g.requestAnimationFrame = function() {
+ throw new AssertionError("Don't call requestAnimationFrame in tests. Use fakes.");
+ };
+//})();
+
+// Emscripten runner starts all tests from this function.
+// IMVU runner uses a separate runner & reporting mechanism.
+function run_all_tests() {
+ function report_to_stdout(msg) {
+ if (msg.type === "test-complete")
+ console.log(msg.name + ": " + msg.verdict);
+ }
+ run_all(report_to_stdout);
+}
+
+// Signal the embind test suite that it is being run from the Emscripten python test runner and not the
+// IMVU test runner.
+var INVOKED_FROM_EMSCRIPTEN_TEST_RUNNER = 1;
diff --git a/tests/embind/underscore-1.4.2.js b/tests/embind/underscore-1.4.2.js
new file mode 100755
index 00000000..dd274095
--- /dev/null
+++ b/tests/embind/underscore-1.4.2.js
@@ -0,0 +1,1200 @@
+// Underscore.js 1.4.2
+// http://underscorejs.org
+// (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc.
+// Underscore may be freely distributed under the MIT license.
+
+//(function() {
+
+ // Baseline setup
+ // --------------
+
+ // Establish the root object, `window` in the browser, or `global` on the server.
+ var root = this;
+
+ // Save the previous value of the `_` variable.
+ var previousUnderscore = root._;
+
+ // Establish the object that gets returned to break out of a loop iteration.
+ var breaker = {};
+
+ // Save bytes in the minified (but not gzipped) version:
+ var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;
+
+ // Create quick reference variables for speed access to core prototypes.
+ var push = ArrayProto.push,
+ slice = ArrayProto.slice,
+ concat = ArrayProto.concat,
+ unshift = ArrayProto.unshift,
+ toString = ObjProto.toString,
+ hasOwnProperty = ObjProto.hasOwnProperty;
+
+ // All **ECMAScript 5** native function implementations that we hope to use
+ // are declared here.
+ var
+ nativeForEach = ArrayProto.forEach,
+ nativeMap = ArrayProto.map,
+ nativeReduce = ArrayProto.reduce,
+ nativeReduceRight = ArrayProto.reduceRight,
+ nativeFilter = ArrayProto.filter,
+ nativeEvery = ArrayProto.every,
+ nativeSome = ArrayProto.some,
+ nativeIndexOf = ArrayProto.indexOf,
+ nativeLastIndexOf = ArrayProto.lastIndexOf,
+ nativeIsArray = Array.isArray,
+ nativeKeys = Object.keys,
+ nativeBind = FuncProto.bind;
+
+ // Create a safe reference to the Underscore object for use below.
+ var _ = function(obj) {
+ if (obj instanceof _) return obj;
+ if (!(this instanceof _)) return new _(obj);
+ this._wrapped = obj;
+ };
+
+ // Export the Underscore object for **Node.js**, with
+ // backwards-compatibility for the old `require()` API. If we're in
+ // the browser, add `_` as a global object via a string identifier,
+ // for Closure Compiler "advanced" mode.
+ if (typeof exports !== 'undefined') {
+ if (typeof module !== 'undefined' && module.exports) {
+ exports = module.exports = _;
+ }
+ exports._ = _;
+ } else {
+ root['_'] = _;
+ }
+
+ // Current version.
+ _.VERSION = '1.4.2';
+
+ // Collection Functions
+ // --------------------
+
+ // The cornerstone, an `each` implementation, aka `forEach`.
+ // Handles objects with the built-in `forEach`, arrays, and raw objects.
+ // Delegates to **ECMAScript 5**'s native `forEach` if available.
+ var each = _.each = _.forEach = function(obj, iterator, context) {
+ if (obj == null) return;
+ if (nativeForEach && obj.forEach === nativeForEach) {
+ obj.forEach(iterator, context);
+ } else if (obj.length === +obj.length) {
+ for (var i = 0, l = obj.length; i < l; i++) {
+ if (iterator.call(context, obj[i], i, obj) === breaker) return;
+ }
+ } else {
+ for (var key in obj) {
+ if (_.has(obj, key)) {
+ if (iterator.call(context, obj[key], key, obj) === breaker) return;
+ }
+ }
+ }
+ };
+
+ // Return the results of applying the iterator to each element.
+ // Delegates to **ECMAScript 5**'s native `map` if available.
+ _.map = _.collect = function(obj, iterator, context) {
+ var results = [];
+ if (obj == null) return results;
+ if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
+ each(obj, function(value, index, list) {
+ results[results.length] = iterator.call(context, value, index, list);
+ });
+ return results;
+ };
+
+ // **Reduce** builds up a single result from a list of values, aka `inject`,
+ // or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available.
+ _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) {
+ var initial = arguments.length > 2;
+ if (obj == null) obj = [];
+ if (nativeReduce && obj.reduce === nativeReduce) {
+ if (context) iterator = _.bind(iterator, context);
+ return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator);
+ }
+ each(obj, function(value, index, list) {
+ if (!initial) {
+ memo = value;
+ initial = true;
+ } else {
+ memo = iterator.call(context, memo, value, index, list);
+ }
+ });
+ if (!initial) throw new TypeError('Reduce of empty array with no initial value');
+ return memo;
+ };
+
+ // The right-associative version of reduce, also known as `foldr`.
+ // Delegates to **ECMAScript 5**'s native `reduceRight` if available.
+ _.reduceRight = _.foldr = function(obj, iterator, memo, context) {
+ var initial = arguments.length > 2;
+ if (obj == null) obj = [];
+ if (nativeReduceRight && obj.reduceRight === nativeReduceRight) {
+ if (context) iterator = _.bind(iterator, context);
+ return arguments.length > 2 ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
+ }
+ var length = obj.length;
+ if (length !== +length) {
+ var keys = _.keys(obj);
+ length = keys.length;
+ }
+ each(obj, function(value, index, list) {
+ index = keys ? keys[--length] : --length;
+ if (!initial) {
+ memo = obj[index];
+ initial = true;
+ } else {
+ memo = iterator.call(context, memo, obj[index], index, list);
+ }
+ });
+ if (!initial) throw new TypeError('Reduce of empty array with no initial value');
+ return memo;
+ };
+
+ // Return the first value which passes a truth test. Aliased as `detect`.
+ _.find = _.detect = function(obj, iterator, context) {
+ var result;
+ any(obj, function(value, index, list) {
+ if (iterator.call(context, value, index, list)) {
+ result = value;
+ return true;
+ }
+ });
+ return result;
+ };
+
+ // Return all the elements that pass a truth test.
+ // Delegates to **ECMAScript 5**'s native `filter` if available.
+ // Aliased as `select`.
+ _.filter = _.select = function(obj, iterator, context) {
+ var results = [];
+ if (obj == null) return results;
+ if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context);
+ each(obj, function(value, index, list) {
+ if (iterator.call(context, value, index, list)) results[results.length] = value;
+ });
+ return results;
+ };
+
+ // Return all the elements for which a truth test fails.
+ _.reject = function(obj, iterator, context) {
+ var results = [];
+ if (obj == null) return results;
+ each(obj, function(value, index, list) {
+ if (!iterator.call(context, value, index, list)) results[results.length] = value;
+ });
+ return results;
+ };
+
+ // Determine whether all of the elements match a truth test.
+ // Delegates to **ECMAScript 5**'s native `every` if available.
+ // Aliased as `all`.
+ _.every = _.all = function(obj, iterator, context) {
+ iterator || (iterator = _.identity);
+ var result = true;
+ if (obj == null) return result;
+ if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context);
+ each(obj, function(value, index, list) {
+ if (!(result = result && iterator.call(context, value, index, list))) return breaker;
+ });
+ return !!result;
+ };
+
+ // Determine if at least one element in the object matches a truth test.
+ // Delegates to **ECMAScript 5**'s native `some` if available.
+ // Aliased as `any`.
+ var any = _.some = _.any = function(obj, iterator, context) {
+ iterator || (iterator = _.identity);
+ var result = false;
+ if (obj == null) return result;
+ if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context);
+ each(obj, function(value, index, list) {
+ if (result || (result = iterator.call(context, value, index, list))) return breaker;
+ });
+ return !!result;
+ };
+
+ // Determine if the array or object contains a given value (using `===`).
+ // Aliased as `include`.
+ _.contains = _.include = function(obj, target) {
+ var found = false;
+ if (obj == null) return found;
+ if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
+ found = any(obj, function(value) {
+ return value === target;
+ });
+ return found;
+ };
+
+ // Invoke a method (with arguments) on every item in a collection.
+ _.invoke = function(obj, method) {
+ var args = slice.call(arguments, 2);
+ return _.map(obj, function(value) {
+ return (_.isFunction(method) ? method : value[method]).apply(value, args);
+ });
+ };
+
+ // Convenience version of a common use case of `map`: fetching a property.
+ _.pluck = function(obj, key) {
+ return _.map(obj, function(value){ return value[key]; });
+ };
+
+ // Convenience version of a common use case of `filter`: selecting only objects
+ // with specific `key:value` pairs.
+ _.where = function(obj, attrs) {
+ if (_.isEmpty(attrs)) return [];
+ return _.filter(obj, function(value) {
+ for (var key in attrs) {
+ if (attrs[key] !== value[key]) return false;
+ }
+ return true;
+ });
+ };
+
+ // Return the maximum element or (element-based computation).
+ // Can't optimize arrays of integers longer than 65,535 elements.
+ // See: https://bugs.webkit.org/show_bug.cgi?id=80797
+ _.max = function(obj, iterator, context) {
+ if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) {
+ return Math.max.apply(Math, obj);
+ }
+ if (!iterator && _.isEmpty(obj)) return -Infinity;
+ var result = {computed : -Infinity};
+ each(obj, function(value, index, list) {
+ var computed = iterator ? iterator.call(context, value, index, list) : value;
+ computed >= result.computed && (result = {value : value, computed : computed});
+ });
+ return result.value;
+ };
+
+ // Return the minimum element (or element-based computation).
+ _.min = function(obj, iterator, context) {
+ if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) {
+ return Math.min.apply(Math, obj);
+ }
+ if (!iterator && _.isEmpty(obj)) return Infinity;
+ var result = {computed : Infinity};
+ each(obj, function(value, index, list) {
+ var computed = iterator ? iterator.call(context, value, index, list) : value;
+ computed < result.computed && (result = {value : value, computed : computed});
+ });
+ return result.value;
+ };
+
+ // Shuffle an array.
+ _.shuffle = function(obj) {
+ var rand;
+ var index = 0;
+ var shuffled = [];
+ each(obj, function(value) {
+ rand = _.random(index++);
+ shuffled[index - 1] = shuffled[rand];
+ shuffled[rand] = value;
+ });
+ return shuffled;
+ };
+
+ // An internal function to generate lookup iterators.
+ var lookupIterator = function(value) {
+ return _.isFunction(value) ? value : function(obj){ return obj[value]; };
+ };
+
+ // Sort the object's values by a criterion produced by an iterator.
+ _.sortBy = function(obj, value, context) {
+ var iterator = lookupIterator(value);
+ return _.pluck(_.map(obj, function(value, index, list) {
+ return {
+ value : value,
+ index : index,
+ criteria : iterator.call(context, value, index, list)
+ };
+ }).sort(function(left, right) {
+ var a = left.criteria;
+ var b = right.criteria;
+ if (a !== b) {
+ if (a > b || a === void 0) return 1;
+ if (a < b || b === void 0) return -1;
+ }
+ return left.index < right.index ? -1 : 1;
+ }), 'value');
+ };
+
+ // An internal function used for aggregate "group by" operations.
+ var group = function(obj, value, context, behavior) {
+ var result = {};
+ var iterator = lookupIterator(value);
+ each(obj, function(value, index) {
+ var key = iterator.call(context, value, index, obj);
+ behavior(result, key, value);
+ });
+ return result;
+ };
+
+ // Groups the object's values by a criterion. Pass either a string attribute
+ // to group by, or a function that returns the criterion.
+ _.groupBy = function(obj, value, context) {
+ return group(obj, value, context, function(result, key, value) {
+ (_.has(result, key) ? result[key] : (result[key] = [])).push(value);
+ });
+ };
+
+ // Counts instances of an object that group by a certain criterion. Pass
+ // either a string attribute to count by, or a function that returns the
+ // criterion.
+ _.countBy = function(obj, value, context) {
+ return group(obj, value, context, function(result, key, value) {
+ if (!_.has(result, key)) result[key] = 0;
+ result[key]++;
+ });
+ };
+
+ // Use a comparator function to figure out the smallest index at which
+ // an object should be inserted so as to maintain order. Uses binary search.
+ _.sortedIndex = function(array, obj, iterator, context) {
+ iterator = iterator == null ? _.identity : lookupIterator(iterator);
+ var value = iterator.call(context, obj);
+ var low = 0, high = array.length;
+ while (low < high) {
+ var mid = (low + high) >>> 1;
+ iterator.call(context, array[mid]) < value ? low = mid + 1 : high = mid;
+ }
+ return low;
+ };
+
+ // Safely convert anything iterable into a real, live array.
+ _.toArray = function(obj) {
+ if (!obj) return [];
+ if (obj.length === +obj.length) return slice.call(obj);
+ return _.values(obj);
+ };
+
+ // Return the number of elements in an object.
+ _.size = function(obj) {
+ return (obj.length === +obj.length) ? obj.length : _.keys(obj).length;
+ };
+
+ // Array Functions
+ // ---------------
+
+ // Get the first element of an array. Passing **n** will return the first N
+ // values in the array. Aliased as `head` and `take`. The **guard** check
+ // allows it to work with `_.map`.
+ _.first = _.head = _.take = function(array, n, guard) {
+ return (n != null) && !guard ? slice.call(array, 0, n) : array[0];
+ };
+
+ // Returns everything but the last entry of the array. Especially useful on
+ // the arguments object. Passing **n** will return all the values in
+ // the array, excluding the last N. The **guard** check allows it to work with
+ // `_.map`.
+ _.initial = function(array, n, guard) {
+ return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n));
+ };
+
+ // Get the last element of an array. Passing **n** will return the last N
+ // values in the array. The **guard** check allows it to work with `_.map`.
+ _.last = function(array, n, guard) {
+ if ((n != null) && !guard) {
+ return slice.call(array, Math.max(array.length - n, 0));
+ } else {
+ return array[array.length - 1];
+ }
+ };
+
+ // Returns everything but the first entry of the array. Aliased as `tail` and `drop`.
+ // Especially useful on the arguments object. Passing an **n** will return
+ // the rest N values in the array. The **guard**
+ // check allows it to work with `_.map`.
+ _.rest = _.tail = _.drop = function(array, n, guard) {
+ return slice.call(array, (n == null) || guard ? 1 : n);
+ };
+
+ // Trim out all falsy values from an array.
+ _.compact = function(array) {
+ return _.filter(array, function(value){ return !!value; });
+ };
+
+ // Internal implementation of a recursive `flatten` function.
+ var flatten = function(input, shallow, output) {
+ each(input, function(value) {
+ if (_.isArray(value)) {
+ shallow ? push.apply(output, value) : flatten(value, shallow, output);
+ } else {
+ output.push(value);
+ }
+ });
+ return output;
+ };
+
+ // Return a completely flattened version of an array.
+ _.flatten = function(array, shallow) {
+ return flatten(array, shallow, []);
+ };
+
+ // Return a version of the array that does not contain the specified value(s).
+ _.without = function(array) {
+ return _.difference(array, slice.call(arguments, 1));
+ };
+
+ // Produce a duplicate-free version of the array. If the array has already
+ // been sorted, you have the option of using a faster algorithm.
+ // Aliased as `unique`.
+ _.uniq = _.unique = function(array, isSorted, iterator, context) {
+ var initial = iterator ? _.map(array, iterator, context) : array;
+ var results = [];
+ var seen = [];
+ each(initial, function(value, index) {
+ if (isSorted ? (!index || seen[seen.length - 1] !== value) : !_.contains(seen, value)) {
+ seen.push(value);
+ results.push(array[index]);
+ }
+ });
+ return results;
+ };
+
+ // Produce an array that contains the union: each distinct element from all of
+ // the passed-in arrays.
+ _.union = function() {
+ return _.uniq(concat.apply(ArrayProto, arguments));
+ };
+
+ // Produce an array that contains every item shared between all the
+ // passed-in arrays.
+ _.intersection = function(array) {
+ var rest = slice.call(arguments, 1);
+ return _.filter(_.uniq(array), function(item) {
+ return _.every(rest, function(other) {
+ return _.indexOf(other, item) >= 0;
+ });
+ });
+ };
+
+ // Take the difference between one array and a number of other arrays.
+ // Only the elements present in just the first array will remain.
+ _.difference = function(array) {
+ var rest = concat.apply(ArrayProto, slice.call(arguments, 1));
+ return _.filter(array, function(value){ return !_.contains(rest, value); });
+ };
+
+ // Zip together multiple lists into a single array -- elements that share
+ // an index go together.
+ _.zip = function() {
+ var args = slice.call(arguments);
+ var length = _.max(_.pluck(args, 'length'));
+ var results = new Array(length);
+ for (var i = 0; i < length; i++) {
+ results[i] = _.pluck(args, "" + i);
+ }
+ return results;
+ };
+
+ // Converts lists into objects. Pass either a single array of `[key, value]`
+ // pairs, or two parallel arrays of the same length -- one of keys, and one of
+ // the corresponding values.
+ _.object = function(list, values) {
+ var result = {};
+ for (var i = 0, l = list.length; i < l; i++) {
+ if (values) {
+ result[list[i]] = values[i];
+ } else {
+ result[list[i][0]] = list[i][1];
+ }
+ }
+ return result;
+ };
+
+ // If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**),
+ // we need this function. Return the position of the first occurrence of an
+ // item in an array, or -1 if the item is not included in the array.
+ // Delegates to **ECMAScript 5**'s native `indexOf` if available.
+ // If the array is large and already in sort order, pass `true`
+ // for **isSorted** to use binary search.
+ _.indexOf = function(array, item, isSorted) {
+ if (array == null) return -1;
+ var i = 0, l = array.length;
+ if (isSorted) {
+ if (typeof isSorted == 'number') {
+ i = (isSorted < 0 ? Math.max(0, l + isSorted) : isSorted);
+ } else {
+ i = _.sortedIndex(array, item);
+ return array[i] === item ? i : -1;
+ }
+ }
+ if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item, isSorted);
+ for (; i < l; i++) if (array[i] === item) return i;
+ return -1;
+ };
+
+ // Delegates to **ECMAScript 5**'s native `lastIndexOf` if available.
+ _.lastIndexOf = function(array, item, from) {
+ if (array == null) return -1;
+ var hasIndex = from != null;
+ if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) {
+ return hasIndex ? array.lastIndexOf(item, from) : array.lastIndexOf(item);
+ }
+ var i = (hasIndex ? from : array.length);
+ while (i--) if (array[i] === item) return i;
+ return -1;
+ };
+
+ // Generate an integer Array containing an arithmetic progression. A port of
+ // the native Python `range()` function. See
+ // [the Python documentation](http://docs.python.org/library/functions.html#range).
+ _.range = function(start, stop, step) {
+ if (arguments.length <= 1) {
+ stop = start || 0;
+ start = 0;
+ }
+ step = arguments[2] || 1;
+
+ var len = Math.max(Math.ceil((stop - start) / step), 0);
+ var idx = 0;
+ var range = new Array(len);
+
+ while(idx < len) {
+ range[idx++] = start;
+ start += step;
+ }
+
+ return range;
+ };
+
+ // Function (ahem) Functions
+ // ------------------
+
+ // Reusable constructor function for prototype setting.
+ var ctor = function(){};
+
+ // Create a function bound to a given object (assigning `this`, and arguments,
+ // optionally). Binding with arguments is also known as `curry`.
+ // Delegates to **ECMAScript 5**'s native `Function.bind` if available.
+ // We check for `func.bind` first, to fail fast when `func` is undefined.
+ _.bind = function bind(func, context) {
+ var bound, args;
+ if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
+ if (!_.isFunction(func)) throw new TypeError;
+ args = slice.call(arguments, 2);
+ return bound = function() {
+ if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments)));
+ ctor.prototype = func.prototype;
+ var self = new ctor;
+ var result = func.apply(self, args.concat(slice.call(arguments)));
+ if (Object(result) === result) return result;
+ return self;
+ };
+ };
+
+ // Bind all of an object's methods to that object. Useful for ensuring that
+ // all callbacks defined on an object belong to it.
+ _.bindAll = function(obj) {
+ var funcs = slice.call(arguments, 1);
+ if (funcs.length == 0) funcs = _.functions(obj);
+ each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); });
+ return obj;
+ };
+
+ // Memoize an expensive function by storing its results.
+ _.memoize = function(func, hasher) {
+ var memo = {};
+ hasher || (hasher = _.identity);
+ return function() {
+ var key = hasher.apply(this, arguments);
+ return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments));
+ };
+ };
+
+ // Delays a function for the given number of milliseconds, and then calls
+ // it with the arguments supplied.
+ _.delay = function(func, wait) {
+ var args = slice.call(arguments, 2);
+ return setTimeout(function(){ return func.apply(null, args); }, wait);
+ };
+
+ // Defers a function, scheduling it to run after the current call stack has
+ // cleared.
+ _.defer = function(func) {
+ return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1)));
+ };
+
+ // Returns a function, that, when invoked, will only be triggered at most once
+ // during a given window of time.
+ _.throttle = function(func, wait) {
+ var context, args, timeout, throttling, more, result;
+ var whenDone = _.debounce(function(){ more = throttling = false; }, wait);
+ return function() {
+ context = this; args = arguments;
+ var later = function() {
+ timeout = null;
+ if (more) {
+ result = func.apply(context, args);
+ }
+ whenDone();
+ };
+ if (!timeout) timeout = setTimeout(later, wait);
+ if (throttling) {
+ more = true;
+ } else {
+ throttling = true;
+ result = func.apply(context, args);
+ }
+ whenDone();
+ return result;
+ };
+ };
+
+ // Returns a function, that, as long as it continues to be invoked, will not
+ // be triggered. The function will be called after it stops being called for
+ // N milliseconds. If `immediate` is passed, trigger the function on the
+ // leading edge, instead of the trailing.
+ _.debounce = function(func, wait, immediate) {
+ var timeout, result;
+ return function() {
+ var context = this, args = arguments;
+ var later = function() {
+ timeout = null;
+ if (!immediate) result = func.apply(context, args);
+ };
+ var callNow = immediate && !timeout;
+ clearTimeout(timeout);
+ timeout = setTimeout(later, wait);
+ if (callNow) result = func.apply(context, args);
+ return result;
+ };
+ };
+
+ // Returns a function that will be executed at most one time, no matter how
+ // often you call it. Useful for lazy initialization.
+ _.once = function(func) {
+ var ran = false, memo;
+ return function() {
+ if (ran) return memo;
+ ran = true;
+ memo = func.apply(this, arguments);
+ func = null;
+ return memo;
+ };
+ };
+
+ // Returns the first function passed as an argument to the second,
+ // allowing you to adjust arguments, run code before and after, and
+ // conditionally execute the original function.
+ _.wrap = function(func, wrapper) {
+ return function() {
+ var args = [func];
+ push.apply(args, arguments);
+ return wrapper.apply(this, args);
+ };
+ };
+
+ // Returns a function that is the composition of a list of functions, each
+ // consuming the return value of the function that follows.
+ _.compose = function() {
+ var funcs = arguments;
+ return function() {
+ var args = arguments;
+ for (var i = funcs.length - 1; i >= 0; i--) {
+ args = [funcs[i].apply(this, args)];
+ }
+ return args[0];
+ };
+ };
+
+ // Returns a function that will only be executed after being called N times.
+ _.after = function(times, func) {
+ if (times <= 0) return func();
+ return function() {
+ if (--times < 1) {
+ return func.apply(this, arguments);
+ }
+ };
+ };
+
+ // Object Functions
+ // ----------------
+
+ // Retrieve the names of an object's properties.
+ // Delegates to **ECMAScript 5**'s native `Object.keys`
+ _.keys = nativeKeys || function(obj) {
+ if (obj !== Object(obj)) throw new TypeError('Invalid object');
+ var keys = [];
+ for (var key in obj) if (_.has(obj, key)) keys[keys.length] = key;
+ return keys;
+ };
+
+ // Retrieve the values of an object's properties.
+ _.values = function(obj) {
+ var values = [];
+ for (var key in obj) if (_.has(obj, key)) values.push(obj[key]);
+ return values;
+ };
+
+ // Convert an object into a list of `[key, value]` pairs.
+ _.pairs = function(obj) {
+ var pairs = [];
+ for (var key in obj) if (_.has(obj, key)) pairs.push([key, obj[key]]);
+ return pairs;
+ };
+
+ // Invert the keys and values of an object. The values must be serializable.
+ _.invert = function(obj) {
+ var result = {};
+ for (var key in obj) if (_.has(obj, key)) result[obj[key]] = key;
+ return result;
+ };
+
+ // Return a sorted list of the function names available on the object.
+ // Aliased as `methods`
+ _.functions = _.methods = function(obj) {
+ var names = [];
+ for (var key in obj) {
+ if (_.isFunction(obj[key])) names.push(key);
+ }
+ return names.sort();
+ };
+
+ // Extend a given object with all the properties in passed-in object(s).
+ _.extend = function(obj) {
+ each(slice.call(arguments, 1), function(source) {
+ for (var prop in source) {
+ obj[prop] = source[prop];
+ }
+ });
+ return obj;
+ };
+
+ // Return a copy of the object only containing the whitelisted properties.
+ _.pick = function(obj) {
+ var copy = {};
+ var keys = concat.apply(ArrayProto, slice.call(arguments, 1));
+ each(keys, function(key) {
+ if (key in obj) copy[key] = obj[key];
+ });
+ return copy;
+ };
+
+ // Return a copy of the object without the blacklisted properties.
+ _.omit = function(obj) {
+ var copy = {};
+ var keys = concat.apply(ArrayProto, slice.call(arguments, 1));
+ for (var key in obj) {
+ if (!_.contains(keys, key)) copy[key] = obj[key];
+ }
+ return copy;
+ };
+
+ // Fill in a given object with default properties.
+ _.defaults = function(obj) {
+ each(slice.call(arguments, 1), function(source) {
+ for (var prop in source) {
+ if (obj[prop] == null) obj[prop] = source[prop];
+ }
+ });
+ return obj;
+ };
+
+ // Create a (shallow-cloned) duplicate of an object.
+ _.clone = function(obj) {
+ if (!_.isObject(obj)) return obj;
+ return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
+ };
+
+ // Invokes interceptor with the obj, and then returns obj.
+ // The primary purpose of this method is to "tap into" a method chain, in
+ // order to perform operations on intermediate results within the chain.
+ _.tap = function(obj, interceptor) {
+ interceptor(obj);
+ return obj;
+ };
+
+ // Internal recursive comparison function for `isEqual`.
+ var eq = function(a, b, aStack, bStack) {
+ // Identical objects are equal. `0 === -0`, but they aren't identical.
+ // See the Harmony `egal` proposal: http://wiki.ecmascript.org/doku.php?id=harmony:egal.
+ if (a === b) return a !== 0 || 1 / a == 1 / b;
+ // A strict comparison is necessary because `null == undefined`.
+ if (a == null || b == null) return a === b;
+ // Unwrap any wrapped objects.
+ if (a instanceof _) a = a._wrapped;
+ if (b instanceof _) b = b._wrapped;
+ // Compare `[[Class]]` names.
+ var className = toString.call(a);
+ if (className != toString.call(b)) return false;
+ switch (className) {
+ // Strings, numbers, dates, and booleans are compared by value.
+ case '[object String]':
+ // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
+ // equivalent to `new String("5")`.
+ return a == String(b);
+ case '[object Number]':
+ // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for
+ // other numeric values.
+ return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b);
+ case '[object Date]':
+ case '[object Boolean]':
+ // Coerce dates and booleans to numeric primitive values. Dates are compared by their
+ // millisecond representations. Note that invalid dates with millisecond representations
+ // of `NaN` are not equivalent.
+ return +a == +b;
+ // RegExps are compared by their source patterns and flags.
+ case '[object RegExp]':
+ return a.source == b.source &&
+ a.global == b.global &&
+ a.multiline == b.multiline &&
+ a.ignoreCase == b.ignoreCase;
+ }
+ if (typeof a != 'object' || typeof b != 'object') return false;
+ // Assume equality for cyclic structures. The algorithm for detecting cyclic
+ // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
+ var length = aStack.length;
+ while (length--) {
+ // Linear search. Performance is inversely proportional to the number of
+ // unique nested structures.
+ if (aStack[length] == a) return bStack[length] == b;
+ }
+ // Add the first object to the stack of traversed objects.
+ aStack.push(a);
+ bStack.push(b);
+ var size = 0, result = true;
+ // Recursively compare objects and arrays.
+ if (className == '[object Array]') {
+ // Compare array lengths to determine if a deep comparison is necessary.
+ size = a.length;
+ result = size == b.length;
+ if (result) {
+ // Deep compare the contents, ignoring non-numeric properties.
+ while (size--) {
+ if (!(result = eq(a[size], b[size], aStack, bStack))) break;
+ }
+ }
+ } else {
+ // Objects with different constructors are not equivalent, but `Object`s
+ // from different frames are.
+ var aCtor = a.constructor, bCtor = b.constructor;
+ if (aCtor !== bCtor && !(_.isFunction(aCtor) && (aCtor instanceof aCtor) &&
+ _.isFunction(bCtor) && (bCtor instanceof bCtor))) {
+ return false;
+ }
+ // Deep compare objects.
+ for (var key in a) {
+ if (_.has(a, key)) {
+ // Count the expected number of properties.
+ size++;
+ // Deep compare each member.
+ if (!(result = _.has(b, key) && eq(a[key], b[key], aStack, bStack))) break;
+ }
+ }
+ // Ensure that both objects contain the same number of properties.
+ if (result) {
+ for (key in b) {
+ if (_.has(b, key) && !(size--)) break;
+ }
+ result = !size;
+ }
+ }
+ // Remove the first object from the stack of traversed objects.
+ aStack.pop();
+ bStack.pop();
+ return result;
+ };
+
+ // Perform a deep comparison to check if two objects are equal.
+ _.isEqual = function(a, b) {
+ return eq(a, b, [], []);
+ };
+
+ // Is a given array, string, or object empty?
+ // An "empty" object has no enumerable own-properties.
+ _.isEmpty = function(obj) {
+ if (obj == null) return true;
+ if (_.isArray(obj) || _.isString(obj)) return obj.length === 0;
+ for (var key in obj) if (_.has(obj, key)) return false;
+ return true;
+ };
+
+ // Is a given value a DOM element?
+ _.isElement = function(obj) {
+ return !!(obj && obj.nodeType === 1);
+ };
+
+ // Is a given value an array?
+ // Delegates to ECMA5's native Array.isArray
+ _.isArray = nativeIsArray || function(obj) {
+ return toString.call(obj) == '[object Array]';
+ };
+
+ // Is a given variable an object?
+ _.isObject = function(obj) {
+ return obj === Object(obj);
+ };
+
+ // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp.
+ each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function(name) {
+ _['is' + name] = function(obj) {
+ return toString.call(obj) == '[object ' + name + ']';
+ };
+ });
+
+ // Define a fallback version of the method in browsers (ahem, IE), where
+ // there isn't any inspectable "Arguments" type.
+ if (!_.isArguments(arguments)) {
+ _.isArguments = function(obj) {
+ return !!(obj && _.has(obj, 'callee'));
+ };
+ }
+
+ // Optimize `isFunction` if appropriate.
+ if (typeof (/./) !== 'function') {
+ _.isFunction = function(obj) {
+ return typeof obj === 'function';
+ };
+ }
+
+ // Is a given object a finite number?
+ _.isFinite = function(obj) {
+ return _.isNumber(obj) && isFinite(obj);
+ };
+
+ // Is the given value `NaN`? (NaN is the only number which does not equal itself).
+ _.isNaN = function(obj) {
+ return _.isNumber(obj) && obj != +obj;
+ };
+
+ // Is a given value a boolean?
+ _.isBoolean = function(obj) {
+ return obj === true || obj === false || toString.call(obj) == '[object Boolean]';
+ };
+
+ // Is a given value equal to null?
+ _.isNull = function(obj) {
+ return obj === null;
+ };
+
+ // Is a given variable undefined?
+ _.isUndefined = function(obj) {
+ return obj === void 0;
+ };
+
+ // Shortcut function for checking if an object has a given property directly
+ // on itself (in other words, not on a prototype).
+ _.has = function(obj, key) {
+ return hasOwnProperty.call(obj, key);
+ };
+
+ // Utility Functions
+ // -----------------
+
+ // Run Underscore.js in *noConflict* mode, returning the `_` variable to its
+ // previous owner. Returns a reference to the Underscore object.
+ _.noConflict = function() {
+ root._ = previousUnderscore;
+ return this;
+ };
+
+ // Keep the identity function around for default iterators.
+ _.identity = function(value) {
+ return value;
+ };
+
+ // Run a function **n** times.
+ _.times = function(n, iterator, context) {
+ for (var i = 0; i < n; i++) iterator.call(context, i);
+ };
+
+ // Return a random integer between min and max (inclusive).
+ _.random = function(min, max) {
+ if (max == null) {
+ max = min;
+ min = 0;
+ }
+ return min + (0 | Math.random() * (max - min + 1));
+ };
+
+ // List of HTML entities for escaping.
+ var entityMap = {
+ escape: {
+ '&': '&amp;',
+ '<': '&lt;',
+ '>': '&gt;',
+ '"': '&quot;',
+ "'": '&#x27;',
+ '/': '&#x2F;'
+ }
+ };
+ entityMap.unescape = _.invert(entityMap.escape);
+
+ // Regexes containing the keys and values listed immediately above.
+ var entityRegexes = {
+ escape: new RegExp('[' + _.keys(entityMap.escape).join('') + ']', 'g'),
+ unescape: new RegExp('(' + _.keys(entityMap.unescape).join('|') + ')', 'g')
+ };
+
+ // Functions for escaping and unescaping strings to/from HTML interpolation.
+ _.each(['escape', 'unescape'], function(method) {
+ _[method] = function(string) {
+ if (string == null) return '';
+ return ('' + string).replace(entityRegexes[method], function(match) {
+ return entityMap[method][match];
+ });
+ };
+ });
+
+ // If the value of the named property is a function then invoke it;
+ // otherwise, return it.
+ _.result = function(object, property) {
+ if (object == null) return null;
+ var value = object[property];
+ return _.isFunction(value) ? value.call(object) : value;
+ };
+
+ // Add your own custom functions to the Underscore object.
+ _.mixin = function(obj) {
+ each(_.functions(obj), function(name){
+ var func = _[name] = obj[name];
+ _.prototype[name] = function() {
+ var args = [this._wrapped];
+ push.apply(args, arguments);
+ return result.call(this, func.apply(_, args));
+ };
+ });
+ };
+
+ // Generate a unique integer id (unique within the entire client session).
+ // Useful for temporary DOM ids.
+ var idCounter = 0;
+ _.uniqueId = function(prefix) {
+ var id = idCounter++;
+ return prefix ? prefix + id : id;
+ };
+
+ // By default, Underscore uses ERB-style template delimiters, change the
+ // following template settings to use alternative delimiters.
+ _.templateSettings = {
+ evaluate : /<%([\s\S]+?)%>/g,
+ interpolate : /<%=([\s\S]+?)%>/g,
+ escape : /<%-([\s\S]+?)%>/g
+ };
+
+ // When customizing `templateSettings`, if you don't want to define an
+ // interpolation, evaluation or escaping regex, we need one that is
+ // guaranteed not to match.
+ var noMatch = /(.)^/;
+
+ // Certain characters need to be escaped so that they can be put into a
+ // string literal.
+ var escapes = {
+ "'": "'",
+ '\\': '\\',
+ '\r': 'r',
+ '\n': 'n',
+ '\t': 't',
+ '\u2028': 'u2028',
+ '\u2029': 'u2029'
+ };
+
+ var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g;
+
+ // JavaScript micro-templating, similar to John Resig's implementation.
+ // Underscore templating handles arbitrary delimiters, preserves whitespace,
+ // and correctly escapes quotes within interpolated code.
+ _.template = function(text, data, settings) {
+ settings = _.defaults({}, settings, _.templateSettings);
+
+ // Combine delimiters into one regular expression via alternation.
+ var matcher = new RegExp([
+ (settings.escape || noMatch).source,
+ (settings.interpolate || noMatch).source,
+ (settings.evaluate || noMatch).source
+ ].join('|') + '|$', 'g');
+
+ // Compile the template source, escaping string literals appropriately.
+ var index = 0;
+ var source = "__p+='";
+ text.replace(matcher, function(match, escape, interpolate, evaluate, offset) {
+ source += text.slice(index, offset)
+ .replace(escaper, function(match) { return '\\' + escapes[match]; });
+ source +=
+ escape ? "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'" :
+ interpolate ? "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'" :
+ evaluate ? "';\n" + evaluate + "\n__p+='" : '';
+ index = offset + match.length;
+ });
+ source += "';\n";
+
+ // If a variable is not specified, place data values in local scope.
+ if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n';
+
+ source = "var __t,__p='',__j=Array.prototype.join," +
+ "print=function(){__p+=__j.call(arguments,'');};\n" +
+ source + "return __p;\n";
+
+ try {
+ var render = new Function(settings.variable || 'obj', '_', source);
+ } catch (e) {
+ e.source = source;
+ throw e;
+ }
+
+ if (data) return render(data, _);
+ var template = function(data) {
+ return render.call(this, data, _);
+ };
+
+ // Provide the compiled function source as a convenience for precompilation.
+ template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}';
+
+ return template;
+ };
+
+ // Add a "chain" function, which will delegate to the wrapper.
+ _.chain = function(obj) {
+ return _(obj).chain();
+ };
+
+ // OOP
+ // ---------------
+ // If Underscore is called as a function, it returns a wrapped object that
+ // can be used OO-style. This wrapper holds altered versions of all the
+ // underscore functions. Wrapped objects may be chained.
+
+ // Helper function to continue chaining intermediate results.
+ var result = function(obj) {
+ return this._chain ? _(obj).chain() : obj;
+ };
+
+ // Add all of the Underscore functions to the wrapper object.
+ _.mixin(_);
+
+ // Add all mutator Array functions to the wrapper.
+ each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
+ var method = ArrayProto[name];
+ _.prototype[name] = function() {
+ var obj = this._wrapped;
+ method.apply(obj, arguments);
+ if ((name == 'shift' || name == 'splice') && obj.length === 0) delete obj[0];
+ return result.call(this, obj);
+ };
+ });
+
+ // Add all accessor Array functions to the wrapper.
+ each(['concat', 'join', 'slice'], function(name) {
+ var method = ArrayProto[name];
+ _.prototype[name] = function() {
+ return result.call(this, method.apply(this._wrapped, arguments));
+ };
+ });
+
+ _.extend(_.prototype, {
+
+ // Start chaining a wrapped Underscore object.
+ chain: function() {
+ this._chain = true;
+ return this;
+ },
+
+ // Extracts the result from a wrapped and chained object.
+ value: function() {
+ return this._wrapped;
+ }
+
+ });
+
+//}).call(this);
diff --git a/tests/runner.py b/tests/runner.py
index 6753e534..db5ba108 100755
--- a/tests/runner.py
+++ b/tests/runner.py
@@ -10247,58 +10247,12 @@ f.close()
print args, fail
self.clear()
try_delete(self.in_dir('a.out.js'))
- Popen([PYTHON, EMCC, path_from_root('tests', 'embind', 'embind_test.cpp'), '--post-js', path_from_root('tests', 'embind', 'embind_test.js')] + args, stderr=PIPE if fail else None).communicate()
+ Popen([PYTHON, EMCC, path_from_root('tests', 'embind', 'embind_test.cpp'), '--post-js', path_from_root('tests', 'embind', 'underscore-1.4.2.js'), '--post-js', path_from_root('tests', 'embind', 'imvu_test_adapter.js'), '--post-js', path_from_root('tests', 'embind', 'embind.test.js')] + args, stderr=PIPE if fail else None).communicate()
assert os.path.exists(self.in_dir('a.out.js')) == (not fail)
if not fail:
- output = run_js(self.in_dir('a.out.js'))
- self.assertContained('''fixture: embind
---test: test value creation
---test: test passthrough
---test: test void return converts to undefined
---test: test booleans can be marshalled
---test: test convert double to unsigned
---test: test get length of array
---test: test add a bunch of things
---test: test sum array
---test: test strings
---test: test no memory leak when passing strings in by const reference
-fixture: classes
---test: test class instance
---test: test class methods
---test: test can't call methods on deleted class instances
---test: test isinstance
---test: test can return class instances by value
---test: test can pass class instances to functions by reference
---test: test can access struct fields
---test: test can set struct fields
---test: test assignment returns value
---test: test assigning string to integer raises TypeError
---test: test can return tuples by value
---test: test tuples can contain tuples
---test: test can pass tuples by value
---test: test can return structs by value
---test: test can pass structs by value
---test: test can pass and return tuples in structs
---test: test can clone handles
---test: test can't clone if already deleted
---test: test moving handles is a clone+delete
---test: test StringHolder
-fixture: embind enumerations
---test: test can compare enumeration values
---test: test repr includes enum value
---test: test instanceof
---test: test can pass and return enumeration values to functions
-fixture: C++11 enum class
---test: test can compare enumeration values
---test: test repr includes enum value
---test: test instanceof
---test: test can pass and return enumeration values to functions
-fixture: emval call tests
---test: test can call functions from C++
-fixture: interfaces
---test: test can wrap JS object in native interface
---test: test can pass arguments and return complicated values
---test: test can call interface methods that return nothing''', output)
+ output = run_js(self.in_dir('a.out.js'), stdout=PIPE, stderr=PIPE, full_output=True)
+ print >> sys.stderr, output
+ assert "FAIL" not in output
def test_llvm_nativizer(self):
try: