summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlon Zakai <alonzakai@gmail.com>2012-10-01 10:48:28 -0700
committerAlon Zakai <alonzakai@gmail.com>2012-10-01 10:48:28 -0700
commit375982d9c18ec64764c9ab14406e5712493e2cb3 (patch)
tree809a0bc40c172aa6fdd5c6312a2e1ba72b5fbb7f
parent7eaa78060c34489c7e56193c725641303d520f31 (diff)
parent58056b5383b1cdcd4f537fad82f9d4a03fb2556e (diff)
Merge pull request #592 from imvu/embind-pull-request
Embind pull request
-rw-r--r--src/embind/embind.js619
-rw-r--r--src/embind/emval.js111
-rw-r--r--system/include/emscripten/bind.h645
-rw-r--r--system/include/emscripten/val.h177
-rw-r--r--system/include/emscripten/wire.h223
-rwxr-xr-xsystem/lib/embind/bind.cpp34
-rw-r--r--tests/embind/embind_test.cpp335
-rw-r--r--tests/embind/embind_test.js341
8 files changed, 2485 insertions, 0 deletions
diff --git a/src/embind/embind.js b/src/embind/embind.js
new file mode 100644
index 00000000..fff19d86
--- /dev/null
+++ b/src/embind/embind.js
@@ -0,0 +1,619 @@
+/*global Module*/
+/*global _malloc, _free, _memcpy*/
+/*global FUNCTION_TABLE, HEAP32*/
+/*global Pointer_stringify, writeStringToMemory*/
+/*global __emval_register, _emval_handle_array, __emval_decref*/
+
+function createNamedFunction(name, body) {
+ /*jshint evil:true*/
+ return new Function(
+ "body",
+ "return function " + name + "() {\n" +
+ " return body.apply(this, arguments);\n" +
+ "};\n"
+ )(body);
+}
+
+function _embind_repr(v) {
+ var t = typeof v;
+ if (t === 'object' || t === 'array' || t === 'function') {
+ return v.toString();
+ } else {
+ return '' + v;
+ }
+}
+
+var typeRegistry = {};
+
+function validateType(type, name) {
+ if (!type) {
+ throw new BindingError('type "' + name + '" must have a positive integer typeid pointer');
+ }
+ if (undefined !== typeRegistry[type]) {
+ throw new BindingError('cannot register type "' + name + '" twice');
+ }
+}
+
+function __embind_register_void(voidType, name) {
+ name = Pointer_stringify(name);
+ validateType(voidType, name);
+ typeRegistry[voidType] = {
+ name: name,
+ fromWireType: function() {
+ return undefined;
+ }
+ };
+}
+
+function __embind_register_bool(boolType, name, trueValue, falseValue) {
+ name = Pointer_stringify(name);
+ validateType(boolType, name);
+ typeRegistry[boolType] = {
+ name: name,
+ toWireType: function(destructors, o) {
+ return o ? trueValue : falseValue;
+ },
+ fromWireType: function(wt) {
+ return wt === trueValue;
+ },
+ };
+}
+
+function __embind_register_integer(primitiveType, name) {
+ name = Pointer_stringify(name);
+ validateType(primitiveType, name);
+ typeRegistry[primitiveType] = {
+ name: name,
+ toWireType: function(destructors, value) {
+ if (typeof value !== "number") {
+ throw new TypeError('Cannot convert "' + _embind_repr(value) + '" to ' + name);
+ }
+ return value | 0;
+ },
+ fromWireType: function(value) {
+ return value;
+ }
+ };
+}
+
+function __embind_register_float(primitiveType, name) {
+ name = Pointer_stringify(name);
+ validateType(primitiveType, name);
+ typeRegistry[primitiveType] = {
+ name: name,
+ toWireType: function(destructors, value) {
+ if (typeof value !== "number") {
+ throw new TypeError('Cannot convert "' + _embind_repr(value) + '" to ' + name);
+ }
+ return value;
+ },
+ fromWireType: function(value) {
+ return value;
+ }
+ };
+}
+
+function __embind_register_cstring(stringType, name) {
+ name = Pointer_stringify(name);
+ validateType(stringType, name);
+ typeRegistry[stringType] = {
+ name: name,
+ toWireType: function(destructors, value) {
+ var ptr = _malloc(value.length + 1);
+ writeStringToMemory(value, ptr);
+ destructors.push(_free);
+ destructors.push(ptr);
+ return ptr;
+ },
+ fromWireType: function(value) {
+ var rv = Pointer_stringify(value);
+ _free(value);
+ return rv;
+ }
+ };
+}
+
+function __embind_register_emval(emvalType, name) {
+ name = Pointer_stringify(name);
+ validateType(emvalType, name);
+ typeRegistry[emvalType] = {
+ 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;
+}
+
+function runDestructors(destructors) {
+ while (destructors.length) {
+ var ptr = destructors.pop();
+ var del = destructors.pop();
+ del(ptr);
+ }
+}
+
+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);
+ }
+ var destructors = [];
+ var args = new Array(argCount + 1);
+ args[0] = fn;
+ for (var i = 0; i < argCount; ++i) {
+ args[i + 1] = argTypes[i].toWireType(destructors, arguments[i]);
+ }
+ var rv = returnType.fromWireType(invoker.apply(null, args));
+ runDestructors(destructors);
+ return rv;
+ };
+}
+
+function __embind_register_tuple(tupleType, name, constructor, destructor) {
+ name = Pointer_stringify(name);
+ constructor = FUNCTION_TABLE[constructor];
+ destructor = FUNCTION_TABLE[destructor];
+
+ var elements = [];
+
+ 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;
+ }
+ };
+}
+
+function copyMemberPointer(memberPointer, memberPointerSize) {
+ var copy = _malloc(memberPointerSize);
+ if (!copy) {
+ throw new Error('Failed to allocate member pointer copy');
+ }
+ _memcpy(copy, memberPointer, memberPointerSize);
+ return copy;
+}
+
+function __embind_register_tuple_element(
+ tupleType,
+ elementType,
+ getter,
+ setter,
+ memberPointerSize,
+ memberPointer
+) {
+ 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);
+ }
+ });
+}
+
+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_register_struct(
+ structType,
+ name,
+ constructor,
+ destructor
+) {
+ 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;
+ }
+ };
+}
+
+function __embind_register_struct_field(
+ structType,
+ fieldName,
+ fieldType,
+ getter,
+ setter,
+ memberPointerSize,
+ memberPointer
+) {
+ structType = requireRegisteredType(structType, 'struct');
+ fieldName = Pointer_stringify(fieldName);
+ fieldType = requireRegisteredType(fieldType, 'field "' + structType.name + '.' + fieldName + '"');
+ getter = FUNCTION_TABLE[getter];
+ setter = FUNCTION_TABLE[setter];
+ memberPointer = copyMemberPointer(memberPointer, memberPointerSize);
+
+ structType.fields[fieldName] = {
+ read: function(ptr) {
+ return fieldType.fromWireType(getter(ptr, memberPointer));
+ },
+ write: function(ptr, o) {
+ var destructors = [];
+ setter(ptr, memberPointer, fieldType.toWireType(destructors, o));
+ runDestructors(destructors);
+ }
+ };
+}
+
+function __embind_register_class(
+ classType,
+ name,
+ destructor
+) {
+ name = Pointer_stringify(name);
+ destructor = FUNCTION_TABLE[destructor];
+
+ var Handle = createNamedFunction(name, function(ptr) {
+ this.count = {value: 1};
+ this.ptr = ptr;
+ });
+
+ Handle.prototype.clone = function() {
+ if (!this.ptr) {
+ throw new BindingError(classType.name + ' instance already deleted');
+ }
+
+ var clone = Object.create(Handle.prototype);
+ clone.count = this.count;
+ clone.ptr = this.ptr;
+
+ clone.count.value += 1;
+ return clone;
+ };
+
+ Handle.prototype.move = function() {
+ var rv = this.clone();
+ this.delete();
+ return rv;
+ };
+
+ Handle.prototype['delete'] = function() {
+ if (!this.ptr) {
+ throw new BindingError(classType.name + ' instance already deleted');
+ }
+
+ this.count.value -= 1;
+ if (0 === this.count.value) {
+ destructor(this.ptr);
+ }
+ this.ptr = undefined;
+ };
+
+ 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;
+ }
+ };
+
+ Module[name] = constructor;
+}
+
+function __embind_register_class_constructor(
+ classType,
+ argCount,
+ argTypes,
+ constructor
+) {
+ 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 destructors = [];
+ var args = new Array(argCount);
+ for (var i = 0; i < argCount; ++i) {
+ args[i] = argTypes[i].toWireType(destructors, arguments[i]);
+ }
+
+ var ptr = constructor.apply(null, args);
+ runDestructors(destructors);
+ classType.Handle.call(this, ptr);
+ };
+}
+
+function __embind_register_class_method(
+ classType,
+ methodName,
+ returnType,
+ argCount,
+ argTypes,
+ invoker,
+ memberFunctionSize,
+ memberFunction
+) {
+ classType = requireRegisteredType(classType, 'class');
+ methodName = Pointer_stringify(methodName);
+ var humanName = classType.name + '.' + methodName;
+ returnType = requireRegisteredType(returnType, 'method ' + humanName + ' return value');
+ argTypes = requireArgumentTypes(argCount, argTypes, 'method ' + humanName);
+ invoker = FUNCTION_TABLE[invoker];
+ memberFunction = copyMemberPointer(memberFunction, memberFunctionSize);
+
+ 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]);
+ }
+
+ var rv = returnType.fromWireType(invoker.apply(null, args));
+ runDestructors(destructors);
+ return rv;
+ };
+}
+
+function __embind_register_class_classmethod(
+ classType,
+ methodName,
+ returnType,
+ argCount,
+ argTypes,
+ method
+) {
+ classType = requireRegisteredType(classType, 'class');
+ methodName = Pointer_stringify(methodName);
+ var humanName = classType.name + '.' + methodName;
+ returnType = requireRegisteredType(returnType, 'classmethod ' + humanName + ' return value');
+ argTypes = requireArgumentTypes(argCount, argTypes, 'classmethod ' + humanName);
+ method = FUNCTION_TABLE[method];
+
+ classType.constructor[methodName] = function() {
+ if (arguments.length !== argCount) {
+ throw new BindingError('emscripten binding method ' + humanName + ' called with ' + arguments.length + ' arguments, expected ' + argCount);
+ }
+
+ var destructors = [];
+ var args = new Array(argCount);
+ for (var i = 0; i < argCount; ++i) {
+ args[i] = argTypes[i].toWireType(destructors, arguments[i]);
+ }
+
+ var rv = returnType.fromWireType(method.apply(null, args));
+ runDestructors(destructors);
+ return rv;
+ };
+}
+
+function __embind_register_class_field(
+ classType,
+ fieldName,
+ fieldType,
+ getter,
+ setter,
+ memberPointerSize,
+ memberPointer
+) {
+ 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);
+
+ 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
+ });
+}
+
+function __embind_register_enum(
+ enumType,
+ name
+) {
+ name = Pointer_stringify(name);
+
+ function Enum() {
+ }
+ Enum.values = {};
+
+ typeRegistry[enumType] = {
+ name: name,
+ constructor: Enum,
+ toWireType: function(destructors, c) {
+ return c.value;
+ },
+ fromWireType: function(c) {
+ return Enum.values[c];
+ },
+ };
+
+ Module[name] = Enum;
+}
+
+function __embind_register_enum_value(
+ enumType,
+ name,
+ enumValue
+) {
+ enumType = requireRegisteredType(enumType, 'enum');
+ name = Pointer_stringify(name);
+
+ var Enum = enumType.constructor;
+
+ var Value = Object.create(enumType.constructor.prototype, {
+ value: {value: enumValue},
+ constructor: {value: createNamedFunction(enumType.name + '_' + name, function() {})},
+ });
+ Enum.values[enumValue] = Value;
+ Enum[name] = Value;
+}
+
+function __embind_register_interface(
+ interfaceType,
+ name,
+ constructor,
+ destructor
+) {
+ name = Pointer_stringify(name);
+ constructor = FUNCTION_TABLE[constructor];
+ destructor = FUNCTION_TABLE[destructor];
+
+ typeRegistry[interfaceType] = {
+ name: name,
+ toWireType: function(destructors, o) {
+ var handle = __emval_register(o);
+ var ptr = constructor(handle);
+ destructors.push(destructor);
+ destructors.push(ptr);
+ return ptr;
+ },
+ };
+}
diff --git a/src/embind/emval.js b/src/embind/emval.js
new file mode 100644
index 00000000..9574ab37
--- /dev/null
+++ b/src/embind/emval.js
@@ -0,0 +1,111 @@
+/*global Module*/
+/*global HEAP32*/
+/*global Pointer_stringify, writeStringToMemory*/
+/*global requireRegisteredType*/
+
+var _emval_handle_array = [];
+var _emval_free_list = [];
+
+// Public JS API
+
+/** @expose */
+Module.count_emval_handles = function() {
+ return _emval_handle_array.length;
+};
+
+// Private C++ API
+
+function __emval_register(value) {
+ var handle = _emval_free_list.length ?
+ _emval_free_list.pop() :
+ _emval_handle_array.length;
+ _emval_handle_array[handle] = {refcount: 1, value: value};
+ return handle;
+}
+
+function __emval_incref(handle) {
+ _emval_handle_array[handle].refcount += 1;
+}
+
+function __emval_decref(handle) {
+ if (0 === --_emval_handle_array[handle].refcount) {
+ delete _emval_handle_array[handle];
+ _emval_free_list.push(handle);
+
+ var actual_length = _emval_handle_array.length;
+ while (actual_length > 0 && _emval_handle_array[actual_length - 1] === undefined) {
+ --actual_length;
+ }
+ _emval_handle_array.length = actual_length;
+ }
+}
+
+function __emval_new_object() {
+ return __emval_register({});
+}
+
+function __emval_new_long(value) {
+ return __emval_register(value);
+}
+
+function __emval_new_cstring(str) {
+ return __emval_register(Pointer_stringify(str));
+}
+
+function __emval_get_property(handle, k) {
+ k = Pointer_stringify(k);
+ return __emval_register(_emval_handle_array[handle].value[k]);
+}
+
+function __emval_get_property_by_long(handle, k) {
+ return __emval_register(_emval_handle_array[handle].value[k]);
+}
+
+function __emval_get_property_by_unsigned_long(handle, k) {
+ return __emval_register(_emval_handle_array[handle].value[k]);
+}
+
+function __emval_set_property(handle, k, value) {
+ k = Pointer_stringify(k);
+ _emval_handle_array[handle].value[k] = _emval_handle_array[value].value;
+}
+
+function __emval_set_property_by_int(handle, k, value) {
+ _emval_handle_array[handle].value[k] = _emval_handle_array[value].value;
+}
+
+function __emval_as(handle, returnType) {
+ returnType = requireRegisteredType(returnType, 'emval::as');
+ var destructors = [];
+ // caller owns destructing
+ return returnType.toWireType(destructors, _emval_handle_array[handle].value);
+}
+
+function __emval_call(handle, argCount, argTypes) {
+ var args = Array.prototype.slice.call(arguments, 3);
+ var fn = _emval_handle_array[handle].value;
+ 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 = fn.apply(undefined, a);
+ 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 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);
+ return __emval_register(rv);
+}
diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h
new file mode 100644
index 00000000..8f56ff87
--- /dev/null
+++ b/system/include/emscripten/bind.h
@@ -0,0 +1,645 @@
+#pragma once
+
+#include <stddef.h>
+#include <string>
+#include <type_traits>
+#include <emscripten/val.h>
+#include <emscripten/wire.h>
+
+namespace emscripten {
+ namespace internal {
+ typedef void (*GenericFunction)();
+ typedef long GenericEnumValue;
+
+ // Implemented in JavaScript. Don't call these directly.
+ extern "C" {
+ void _embind_fatal_error(
+ const char* name,
+ const char* payload) __attribute__((noreturn));
+
+ void _embind_register_void(
+ TypeID voidType,
+ const char* name);
+
+ void _embind_register_bool(
+ TypeID boolType,
+ const char* name,
+ bool trueValue,
+ bool falseValue);
+
+ void _embind_register_integer(
+ TypeID integerType,
+ const char* name);
+
+ void _embind_register_float(
+ TypeID floatType,
+ const char* name);
+
+ void _embind_register_cstring(
+ TypeID stringType,
+ const char* name);
+
+ void _embind_register_emval(
+ TypeID emvalType,
+ const char* name);
+
+ void _embind_register_function(
+ const char* name,
+ TypeID returnType,
+ unsigned argCount,
+ TypeID argTypes[],
+ GenericFunction invoker,
+ GenericFunction function);
+
+ void _embind_register_tuple(
+ TypeID tupleType,
+ const char* name,
+ GenericFunction constructor,
+ GenericFunction destructor);
+
+ void _embind_register_tuple_element(
+ TypeID tupleType,
+ TypeID elementType,
+ GenericFunction getter,
+ 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 _embind_register_struct(
+ TypeID structType,
+ const char* name,
+ GenericFunction constructor,
+ GenericFunction destructor);
+
+ void _embind_register_struct_field(
+ TypeID structType,
+ const char* name,
+ TypeID fieldType,
+ GenericFunction getter,
+ GenericFunction setter,
+ size_t memberPointerSize,
+ void* memberPointer);
+
+ void _embind_register_class(
+ TypeID classType,
+ const char* className,
+ GenericFunction destructor);
+
+ void _embind_register_class_constructor(
+ TypeID classType,
+ unsigned argCount,
+ TypeID argTypes[],
+ GenericFunction constructor);
+
+ void _embind_register_class_method(
+ TypeID classType,
+ const char* methodName,
+ TypeID returnType,
+ unsigned argCount,
+ TypeID argTypes[],
+ GenericFunction invoker,
+ size_t memberFunctionSize,
+ void* memberFunction);
+
+ void _embind_register_class_field(
+ TypeID classType,
+ const char* fieldName,
+ TypeID fieldType,
+ GenericFunction getter,
+ GenericFunction setter,
+ size_t memberPointerSize,
+ void* memberPointer);
+
+ void _embind_register_class_classmethod(
+ TypeID classType,
+ const char* methodName,
+ TypeID returnType,
+ unsigned argCount,
+ TypeID argTypes[],
+ GenericFunction method);
+
+ void _embind_register_enum(
+ TypeID enumType,
+ const char* name);
+
+ void _embind_register_enum_value(
+ TypeID enumType,
+ const char* valueName,
+ GenericEnumValue value);
+
+ void _embind_register_interface(
+ TypeID interfaceType,
+ const char* name,
+ GenericFunction constructor,
+ GenericFunction destructor);
+ }
+
+ extern void registerStandardTypes();
+
+ class BindingsDefinition {
+ public:
+ template<typename Function>
+ BindingsDefinition(Function fn) {
+ fn();
+ }
+ };
+ }
+}
+
+namespace emscripten {
+ namespace internal {
+ template<typename ReturnType, typename... Args>
+ struct Invoker {
+ static typename internal::BindingType<ReturnType>::WireType invoke(
+ ReturnType (fn)(Args...),
+ typename internal::BindingType<Args>::WireType... args
+ ) {
+ return internal::BindingType<ReturnType>::toWireType(
+ fn(
+ internal::BindingType<Args>::fromWireType(args)...
+ )
+ );
+ }
+ };
+
+ template<typename... Args>
+ struct Invoker<void, Args...> {
+ static void invoke(
+ void (fn)(Args...),
+ typename internal::BindingType<Args>::WireType... args
+ ) {
+ return fn(
+ internal::BindingType<Args>::fromWireType(args)...
+ );
+ }
+ };
+ }
+
+ template<typename ReturnType, typename... Args>
+ void function(const char* name, ReturnType (fn)(Args...)) {
+ internal::registerStandardTypes();
+
+ internal::ArgTypeList<Args...> args;
+ internal::_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));
+ }
+
+ namespace internal {
+ template<typename ClassType, typename... Args>
+ ClassType* raw_constructor(
+ typename internal::BindingType<Args>::WireType... args
+ ) {
+ return new ClassType(
+ internal::BindingType<Args>::fromWireType(args)...
+ );
+ }
+
+ template<typename ClassType>
+ void raw_destructor(ClassType* ptr) {
+ delete ptr;
+ }
+
+ template<typename ClassType, typename ReturnType, typename... Args>
+ struct MethodInvoker {
+ typedef ReturnType (ClassType::*MemberPointer)(Args...);
+ typename internal::BindingType<ReturnType>::WireType invoke(
+ ClassType* ptr,
+ const MemberPointer& method,
+ typename internal::BindingType<Args>::WireType... args
+ ) {
+ return internal::BindingType<ReturnType>::toWireType(
+ (ptr->*method)(
+ internal::BindingType<Args>::fromWireType(args)...
+ )
+ );
+ }
+ };
+
+ template<typename ClassType, typename... Args>
+ struct MethodInvoker<ClassType, void, Args...> {
+ typedef void (ClassType::*MemberPointer)(Args...);
+ static void invoke(
+ ClassType* ptr,
+ const MemberPointer& method,
+ typename internal::BindingType<Args>::WireType... args
+ ) {
+ return (ptr->*method)(
+ internal::BindingType<Args>::fromWireType(args)...
+ );
+ }
+ };
+
+ template<typename ClassType, typename ReturnType, typename... Args>
+ struct ConstMethodInvoker {
+ typedef ReturnType (ClassType::*MemberPointer)(Args...) const;
+ static typename internal::BindingType<ReturnType>::WireType invoke(
+ const ClassType* ptr,
+ const MemberPointer& method,
+ typename internal::BindingType<Args>::WireType... args
+ ) {
+ return internal::BindingType<ReturnType>::toWireType(
+ (ptr->*method)(
+ internal::BindingType<Args>::fromWireType(args)...
+ )
+ );
+ }
+ };
+
+ template<typename ClassType, typename... Args>
+ struct ConstMethodInvoker<ClassType, void, Args...> {
+ typedef void (ClassType::*MemberPointer)(Args...) const;
+ static void invoke(
+ const ClassType* ptr,
+ const MemberPointer& method,
+ typename internal::BindingType<Args>::WireType... args
+ ) {
+ return (ptr->*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;
+
+ static WireType get(
+ ClassType& ptr,
+ const MemberPointer& field
+ ) {
+ return FieldBinding::toWireType(ptr.*field);
+ }
+
+ static void set(
+ ClassType& ptr,
+ const MemberPointer& field,
+ WireType value
+ ) {
+ ptr.*field = FieldBinding::fromWireType(value);
+ }
+
+ template<typename Getter>
+ static WireType propertyGet(
+ ClassType& ptr,
+ const Getter& getter
+ ) {
+ return FieldBinding::toWireType(getter(ptr));
+ }
+
+ template<typename Setter>
+ static void propertySet(
+ ClassType& ptr,
+ const Setter& setter,
+ WireType value
+ ) {
+ setter(ptr, FieldBinding::fromWireType(value));
+ }
+ };
+ }
+
+ template<typename ClassType>
+ class value_tuple {
+ public:
+ value_tuple(const char* name) {
+ internal::registerStandardTypes();
+ internal::_embind_register_tuple(
+ internal::getTypeID<ClassType>(),
+ 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;
+ }
+
+ 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;
+ }
+
+ 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);
+ 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);
+ 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);
+ return *this;
+ }
+ };
+
+ template<typename ClassType>
+ class value_struct {
+ public:
+ value_struct(const char* name) {
+ internal::registerStandardTypes();
+ internal::_embind_register_struct(
+ internal::getTypeID<ClassType>(),
+ name,
+ reinterpret_cast<internal::GenericFunction>(&internal::raw_constructor<ClassType>),
+ reinterpret_cast<internal::GenericFunction>(&internal::raw_destructor<ClassType>));
+ }
+
+ template<typename FieldType>
+ value_struct& field(const char* fieldName, FieldType ClassType::*field) {
+ internal::_embind_register_struct_field(
+ internal::getTypeID<ClassType>(),
+ 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);
+
+ return *this;
+ }
+ };
+
+ // TODO: support class definitions without constructors.
+ // TODO: support external class constructors
+ template<typename ClassType>
+ class class_ {
+ public:
+ class_(const char* name) {
+ internal::registerStandardTypes();
+ internal::_embind_register_class(
+ internal::getTypeID<ClassType>(),
+ name,
+ reinterpret_cast<internal::GenericFunction>(&internal::raw_destructor<ClassType>));
+ }
+
+ template<typename... ConstructorArgs>
+ class_& constructor() {
+ internal::ArgTypeList<ConstructorArgs...> args;
+ internal::_embind_register_class_constructor(
+ internal::getTypeID<ClassType>(),
+ args.count,
+ args.types,
+ reinterpret_cast<internal::GenericFunction>(&internal::raw_constructor<ClassType, ConstructorArgs...>));
+ }
+
+ 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>(),
+ methodName,
+ internal::getTypeID<ReturnType>(),
+ args.count,
+ args.types,
+ reinterpret_cast<internal::GenericFunction>(&internal::MethodInvoker<ClassType, ReturnType, Args...>::invoke),
+ sizeof(memberFunction),
+ &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>(),
+ methodName,
+ internal::getTypeID<ReturnType>(),
+ args.count,
+ args.types,
+ reinterpret_cast<internal::GenericFunction>(&internal::ConstMethodInvoker<ClassType, ReturnType, Args...>::invoke),
+ sizeof(memberFunction),
+ &memberFunction);
+ return *this;
+ }
+
+ template<typename FieldType>
+ class_& field(const char* fieldName, FieldType ClassType::*field) {
+ internal::_embind_register_class_field(
+ internal::getTypeID<ClassType>(),
+ 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);
+ 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>(),
+ methodName,
+ internal::getTypeID<ReturnType>(),
+ args.count,
+ args.types,
+ reinterpret_cast<internal::GenericFunction>(classMethod));
+ return *this;
+ }
+ };
+
+ template<typename EnumType>
+ class enum_ {
+ public:
+ enum_(const char* name) {
+ _embind_register_enum(
+ internal::getTypeID<EnumType>(),
+ name);
+ }
+
+ enum_& value(const char* name, EnumType value) {
+ // TODO: there's still an issue here.
+ // if EnumType is an unsigned long, then JS may receive it as a signed long
+ static_assert(sizeof(value) <= sizeof(internal::GenericEnumValue), "enum type must fit in a GenericEnumValue");
+
+ _embind_register_enum_value(
+ internal::getTypeID<EnumType>(),
+ name,
+ static_cast<internal::GenericEnumValue>(value));
+ return *this;
+ }
+ };
+
+ namespace internal {
+ template<typename T>
+ class optional {
+ public:
+ optional()
+ : initialized(false)
+ {}
+
+ ~optional() {
+ if (initialized) {
+ get()->~T();
+ }
+ }
+
+ optional(const optional&) = delete;
+
+ T& operator*() {
+ assert(initialized);
+ return *get();
+ }
+
+ explicit operator bool() const {
+ return initialized;
+ }
+
+ optional& operator=(const T& v) {
+ if (initialized) {
+ get()->~T();
+ }
+ new(get()) T(v);
+ initialized = true;
+ }
+
+ private:
+ T* get() {
+ return reinterpret_cast<T*>(&data);
+ }
+
+ bool initialized;
+ typename std::aligned_storage<sizeof(T)>::type data;
+ };
+ }
+
+ template<typename InterfaceType>
+ class wrapper : public InterfaceType {
+ public:
+ // Not necessary in any example so far, but appeases a compiler warning.
+ virtual ~wrapper() {}
+
+ typedef InterfaceType interface;
+
+ void initialize(internal::EM_VAL handle) {
+ if (jsobj) {
+ internal::_embind_fatal_error(
+ "Cannot initialize interface wrapper twice",
+ typeid(InterfaceType).name());
+ }
+ 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 {
+ static ReturnType call(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(val& v, const char* name, Args... args) {
+ v.call(name, args...);
+ }
+ };
+
+ void assertInitialized() {
+ if (!jsobj) {
+ internal::_embind_fatal_error(
+ "Cannot invoke call on uninitialized interface wrapper.",
+ typeid(InterfaceType).name());
+ }
+ }
+
+ internal::optional<val> jsobj;
+ };
+
+ namespace internal {
+ template<typename WrapperType>
+ WrapperType* create_interface_wrapper(EM_VAL e) {
+ WrapperType* p = new WrapperType;
+ p->initialize(e);
+ return p;
+ }
+ }
+
+ template<typename WrapperType>
+ class interface {
+ public:
+ typedef typename WrapperType::interface InterfaceType;
+
+ interface(const char* name) {
+ _embind_register_interface(
+ internal::getTypeID<InterfaceType>(),
+ name,
+ reinterpret_cast<internal::GenericFunction>(&internal::create_interface_wrapper<WrapperType>),
+ reinterpret_cast<internal::GenericFunction>(&internal::raw_destructor<WrapperType>));
+ }
+ };
+}
+
+#define EMSCRIPTEN_BINDINGS(fn) static emscripten::internal::BindingsDefinition anon_symbol(fn);
diff --git a/system/include/emscripten/val.h b/system/include/emscripten/val.h
new file mode 100644
index 00000000..96db9326
--- /dev/null
+++ b/system/include/emscripten/val.h
@@ -0,0 +1,177 @@
+#pragma once
+
+#include <stdint.h> // uintptr_t
+#include <emscripten/wire.h>
+
+namespace emscripten {
+ namespace internal {
+ // Implemented in JavaScript. Don't call these directly.
+ extern "C" {
+ typedef struct _EM_VAL* EM_VAL;
+
+ void _emval_incref(EM_VAL value);
+ void _emval_decref(EM_VAL value);
+ 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_call(
+ EM_VAL value,
+ unsigned argCount,
+ internal::TypeID argTypes[]
+ /*, ... */);
+ EM_VAL _emval_call_method(
+ EM_VAL value,
+ const char* methodName,
+ unsigned argCount,
+ internal::TypeID argTypes[]
+ /*, ... */);
+ }
+ }
+
+ class val {
+ public:
+ static val object() {
+ return val(internal::_emval_new_object());
+ };
+
+ static val take_ownership(internal::EM_VAL e) {
+ return val(e);
+ }
+
+ explicit val(long l)
+ : handle(internal::_emval_new_long(l))
+ {}
+
+ explicit val(const char* str)
+ : handle(internal::_emval_new_cstring(str))
+ {}
+
+ val() = delete;
+
+ val(const val& v)
+ : handle(v.handle)
+ {
+ internal::_emval_incref(handle);
+ }
+
+ ~val() {
+ internal::_emval_decref(handle);
+ }
+
+ val& operator=(const val& v) {
+ internal::_emval_incref(v.handle);
+ internal::_emval_decref(handle);
+ handle = v.handle;
+ 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));
+ }
+
+ 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));
+ }
+
+ val get(unsigned long key) const {
+ return val(internal::_emval_get_property_by_unsigned_long(handle, key));
+ }
+
+ void set(const char* key, val v) {
+ internal::_emval_set_property(handle, key, v.handle);
+ }
+
+ void set(long key, val v) {
+ internal::_emval_set_property_by_int(handle, key, v.handle);
+ }
+
+ template<typename ...Args>
+ val operator()(Args... args) {
+ internal::ArgTypeList<Args...> argList;
+ typedef internal::EM_VAL (*TypedCall)(
+ internal::EM_VAL,
+ unsigned,
+ internal::TypeID argTypes[],
+ typename internal::BindingType<Args>::WireType...);
+ TypedCall typedCall = reinterpret_cast<TypedCall>(&internal::_emval_call);
+ return val(
+ typedCall(
+ handle,
+ argList.count,
+ argList.types,
+ internal::toWireType(args)...));
+ }
+
+ template<typename ...Args>
+ val call(const char* name, Args... args) {
+ internal::ArgTypeList<Args...> argList;
+ typedef internal::EM_VAL (*TypedCall)(
+ internal::EM_VAL,
+ const char* name,
+ unsigned,
+ internal::TypeID argTypes[],
+ typename internal::BindingType<Args>::WireType...);
+ TypedCall typedCall = reinterpret_cast<TypedCall>(&internal::_emval_call_method);
+ return val(
+ typedCall(
+ handle,
+ name,
+ argList.count,
+ argList.types,
+ internal::toWireType(args)...));
+ }
+
+ template<typename T>
+ T as() const {
+ typedef internal::BindingType<T> BT;
+
+ typedef typename BT::WireType (*TypedAs)(
+ internal::EM_VAL value,
+ emscripten::internal::TypeID returnType);
+ TypedAs typedAs = reinterpret_cast<TypedAs>(&internal::_emval_as);
+
+ typename BT::WireType wt = typedAs(handle, internal::getTypeID<T>());
+ internal::WireDeleter<T> deleter(wt);
+ return BT::fromWireType(wt);
+ }
+
+ private:
+ // takes ownership, assumes handle already incref'd
+ explicit val(internal::EM_VAL handle)
+ : handle(handle)
+ {}
+
+ internal::EM_VAL handle;
+
+ friend struct internal::BindingType<val>;
+ };
+
+ namespace internal {
+ template<>
+ struct BindingType<val> {
+ typedef internal::EM_VAL WireType;
+ static WireType toWireType(val v) {
+ _emval_incref(v.handle);
+ return v.handle;
+ }
+ static val fromWireType(WireType v) {
+ return val(v);
+ }
+ };
+ }
+}
diff --git a/system/include/emscripten/wire.h b/system/include/emscripten/wire.h
new file mode 100644
index 00000000..722bf4b8
--- /dev/null
+++ b/system/include/emscripten/wire.h
@@ -0,0 +1,223 @@
+#pragma once
+
+// A value moving between JavaScript and C++ has three representations:
+// - The original JS value: a String
+// - The native on-the-wire value: a stack-allocated char*, say
+// - The C++ value: std::string
+//
+// We'll call the on-the-wire type WireType.
+
+namespace emscripten {
+ namespace internal {
+ 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.
+ template<typename T>
+ inline TypeID getTypeID() {
+ return reinterpret_cast<TypeID>(&typeid(T));
+ }
+
+ // count<>
+
+ template<typename... Args>
+ struct count;
+
+ template<>
+ struct count<> {
+ enum { value = 0 };
+ };
+
+ template<typename T, typename... Args>
+ struct count<T, Args...> {
+ enum { value = 1 + count<Args...>::value };
+ };
+
+ // ArgTypeList<>
+
+ template<typename... Args>
+ struct ArgTypes;
+
+ template<>
+ struct ArgTypes<> {
+ 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<typename... Args>
+ struct ArgTypeList {
+ enum { args_count = count<Args...>::value };
+
+ ArgTypeList() {
+ count = args_count;
+ ArgTypes<Args...>::fill(types);
+ }
+
+ unsigned count;
+ TypeID types[args_count];
+ };
+
+ // BindingType<T>
+
+ 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) { \
+ } \
+ }
+
+ EMSCRIPTEN_DEFINE_NATIVE_BINDING_TYPE(char);
+ EMSCRIPTEN_DEFINE_NATIVE_BINDING_TYPE(signed char);
+ EMSCRIPTEN_DEFINE_NATIVE_BINDING_TYPE(unsigned char);
+ EMSCRIPTEN_DEFINE_NATIVE_BINDING_TYPE(signed short);
+ EMSCRIPTEN_DEFINE_NATIVE_BINDING_TYPE(unsigned short);
+ EMSCRIPTEN_DEFINE_NATIVE_BINDING_TYPE(signed int);
+ EMSCRIPTEN_DEFINE_NATIVE_BINDING_TYPE(unsigned int);
+ EMSCRIPTEN_DEFINE_NATIVE_BINDING_TYPE(signed long);
+ EMSCRIPTEN_DEFINE_NATIVE_BINDING_TYPE(unsigned long);
+ EMSCRIPTEN_DEFINE_NATIVE_BINDING_TYPE(float);
+ EMSCRIPTEN_DEFINE_NATIVE_BINDING_TYPE(double);
+
+ template<>
+ struct BindingType<void> {
+ };
+
+ template<>
+ struct BindingType<bool> {
+ typedef bool WireType;
+ static WireType toWireType(bool b) {
+ return b;
+ }
+ static bool fromWireType(WireType wt) {
+ return wt;
+ }
+ static void destroy(WireType) {
+ }
+ };
+
+ template<>
+ struct BindingType<std::string> {
+ typedef char* WireType;
+ static WireType toWireType(std::string v) {
+ return strdup(v.c_str());
+ }
+ static std::string fromWireType(char* v) {
+ return std::string(v);
+ }
+ };
+
+ template<>
+ struct BindingType<const std::string&> {
+ typedef char* WireType;
+ static WireType toWireType(std::string v) {
+ return strdup(v.c_str());
+ }
+ static std::string fromWireType(char* v) {
+ return std::string(v);
+ }
+ };
+
+ template<typename Enum>
+ struct EnumBindingType {
+ typedef Enum WireType;
+
+ static WireType toWireType(Enum v) {
+ return v;
+ }
+ static Enum fromWireType(WireType v) {
+ return v;
+ }
+ };
+
+ template<typename T>
+ struct GenericBindingType {
+ 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 void destroy(WireType p) {
+ delete p;
+ }
+ };
+
+ template<typename T>
+ struct WireDeleter {
+ typedef typename BindingType<T>::WireType WireType;
+
+ WireDeleter(WireType wt)
+ : wt(wt)
+ {}
+
+ ~WireDeleter() {
+ BindingType<T>::destroy(wt);
+ }
+
+ WireType wt;
+ };
+
+ // catch-all generic binding
+ template<typename T>
+ struct BindingType : std::conditional<
+ std::is_enum<T>::value,
+ EnumBindingType<T>,
+ GenericBindingType<T>>::type
+ {};
+
+ template<typename T>
+ auto toWireType(const T& v) -> typename BindingType<T>::WireType {
+ return BindingType<T>::toWireType(v);
+ }
+
+ }
+}
diff --git a/system/lib/embind/bind.cpp b/system/lib/embind/bind.cpp
new file mode 100755
index 00000000..b63a86aa
--- /dev/null
+++ b/system/lib/embind/bind.cpp
@@ -0,0 +1,34 @@
+#include <emscripten/bind.h>
+
+using namespace emscripten;
+
+namespace emscripten {
+ namespace internal {
+ void registerStandardTypes() {
+ static bool first = true;
+ if (first) {
+ first = false;
+
+ _embind_register_void(getTypeID<void>(), "void");
+
+ _embind_register_bool(getTypeID<bool>(), "bool", true, false);
+
+ _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");
+
+ _embind_register_float(getTypeID<float>(), "float");
+ _embind_register_float(getTypeID<double>(), "double");
+
+ _embind_register_cstring(getTypeID<std::string>(), "std::string");
+ _embind_register_emval(getTypeID<val>(), "emscripten::val");
+ }
+ }
+ }
+}
diff --git a/tests/embind/embind_test.cpp b/tests/embind/embind_test.cpp
new file mode 100644
index 00000000..e7b4d985
--- /dev/null
+++ b/tests/embind/embind_test.cpp
@@ -0,0 +1,335 @@
+#include <string>
+#include <malloc.h>
+#include <emscripten/bind.h>
+
+using namespace emscripten;
+
+val emval_test_mallinfo() {
+ const auto& i = mallinfo();
+ val rv(val::object());
+ rv.set("arena", val(i.arena));
+ rv.set("ordblks", val(i.ordblks));
+ rv.set("smblks", val(i.smblks));
+ rv.set("hblks", val(i.hblks));
+ rv.set("usmblks", val(i.usmblks));
+ rv.set("fsmblks", val(i.fsmblks));
+ rv.set("uordblks", val(i.uordblks));
+ rv.set("fordblks", val(i.fordblks));
+ rv.set("keepcost", val(i.keepcost));
+ return rv;
+}
+
+val emval_test_new_integer() {
+ return val(15);
+}
+
+val emval_test_new_string() {
+ return val("Hello everyone");
+}
+
+val emval_test_new_object() {
+ val rv(val::object());
+ rv.set("foo", val("bar"));
+ rv.set("baz", val(1));
+ return rv;
+}
+
+unsigned emval_test_passthrough_unsigned(unsigned v) {
+ return v;
+}
+
+val emval_test_passthrough(val v) {
+ return v;
+}
+
+void emval_test_return_void() {
+}
+
+bool emval_test_not(bool b) {
+ return !b;
+}
+
+unsigned emval_test_as_unsigned(val v) {
+ return v.as<unsigned>();
+}
+
+unsigned emval_test_get_length(val v) {
+ return v.get("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;
+}
+
+unsigned emval_test_sum(val v) {
+ unsigned length = v.get("length").as<unsigned>();
+ double rv = 0;
+ for (unsigned i = 0; i < length; ++i) {
+ rv += v.get(i).as<double>();
+ }
+ return rv;
+}
+
+std::string emval_test_take_and_return_const_char_star(const char* str) {
+ return str;
+}
+
+std::string emval_test_take_and_return_std_string(std::string str) {
+ return str;
+}
+
+std::string emval_test_take_and_return_std_string_const_ref(const std::string& str) {
+ return str;
+}
+
+class ValHolder {
+public:
+ ValHolder(val v)
+ : v(v)
+ {}
+
+ val getVal() const {
+ return v;
+ }
+
+ void setVal(val v) {
+ this->v = v;
+ }
+
+ static int some_class_method(int i) {
+ return i;
+ }
+
+private:
+ val v;
+};
+
+ValHolder emval_test_return_ValHolder() {
+ return val::object();
+}
+
+void emval_test_set_ValHolder_to_empty_object(ValHolder& vh) {
+ vh.setVal(val::object());
+}
+
+class StringHolder {
+public:
+ StringHolder(const std::string& s)
+ : str(s)
+ {}
+
+ void set(const std::string& s) {
+ str = s;
+ }
+ std::string get() const {
+ return str;
+ }
+
+private:
+ std::string str;
+};
+
+struct TupleVector {
+ float x, y, z;
+};
+
+float readTupleVectorZ(const TupleVector& v) {
+ return v.z;
+}
+
+void writeTupleVectorZ(TupleVector& v, float z) {
+ v.z = z;
+}
+
+struct TupleVectorTuple {
+ TupleVector v;
+};
+
+TupleVector emval_test_return_TupleVector() {
+ TupleVector cv;
+ cv.x = 1;
+ cv.y = 2;
+ cv.z = 3;
+ return cv;
+}
+
+TupleVector emval_test_take_and_return_TupleVector(TupleVector v) {
+ return v;
+}
+
+TupleVectorTuple emval_test_return_TupleVectorTuple() {
+ TupleVectorTuple cvt;
+ cvt.v = emval_test_return_TupleVector();
+ 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;
+}
+
+StructVector emval_test_take_and_return_StructVector(StructVector v) {
+ return v;
+}
+
+struct CustomStruct {
+ CustomStruct()
+ : field(10)
+ {}
+ int field;
+};
+
+struct TupleInStruct {
+ TupleVector field;
+};
+
+TupleInStruct emval_test_take_and_return_TupleInStruct(TupleInStruct cs) {
+ return cs;
+}
+
+enum Enum { ONE, TWO };
+
+Enum emval_test_take_and_return_Enum(Enum e) {
+ return e;
+}
+
+enum class EnumClass { ONE, TWO };
+
+EnumClass emval_test_take_and_return_EnumClass(EnumClass e) {
+ return e;
+}
+
+class Interface {
+public:
+ virtual int method() = 0;
+ virtual TupleInStruct method2(const TupleInStruct& arg1, float arg2) = 0;
+ virtual void method3() = 0;
+};
+
+int emval_test_call_method(Interface& i) {
+ return i.method();
+}
+
+TupleInStruct emval_test_call_method2(Interface& i, const TupleInStruct& arg1, float arg2) {
+ return i.method2(arg1, arg2);
+}
+
+void emval_test_call_method3(Interface& i) {
+ i.method3();
+}
+
+void emval_test_call_function(val v, int i, float f, TupleVector tv, StructVector sv) {
+ v(i, f, tv, sv);
+}
+
+EMSCRIPTEN_BINDINGS(([]() {
+ 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_new_object", &emval_test_new_object);
+ function("emval_test_passthrough_unsigned", &emval_test_passthrough_unsigned);
+ function("emval_test_passthrough", &emval_test_passthrough);
+ function("emval_test_return_void", &emval_test_return_void);
+ function("emval_test_not", &emval_test_not);
+
+ 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("emval_test_sum", &emval_test_sum);
+
+ //function("emval_test_take_and_return_const_char_star", &emval_test_take_and_return_const_char_star);
+ function("emval_test_take_and_return_std_string", &emval_test_take_and_return_std_string);
+ function("emval_test_take_and_return_std_string_const_ref", &emval_test_take_and_return_std_string_const_ref);
+
+ //function("emval_test_take_and_return_CustomStruct", &emval_test_take_and_return_CustomStruct);
+
+ value_tuple<TupleVector>("TupleVector")
+ .element(&TupleVector::x)
+ .element(&TupleVector::y)
+ //.element(&TupleVector::z)
+ .element(&readTupleVectorZ, &writeTupleVectorZ)
+ ;
+
+ function("emval_test_return_TupleVector", &emval_test_return_TupleVector);
+ function("emval_test_take_and_return_TupleVector", &emval_test_take_and_return_TupleVector);
+
+ value_tuple<TupleVectorTuple>("TupleVectorTuple")
+ .element(&TupleVectorTuple::v)
+ ;
+
+ function("emval_test_return_TupleVectorTuple", &emval_test_return_TupleVectorTuple);
+
+ value_struct<StructVector>("StructVector")
+ .field("x", &StructVector::x)
+ .field("y", &StructVector::y)
+ .field("z", &StructVector::z)
+ ;
+
+ function("emval_test_return_StructVector", &emval_test_return_StructVector);
+ function("emval_test_take_and_return_StructVector", &emval_test_take_and_return_StructVector);
+
+ value_struct<TupleInStruct>("TupleInStruct")
+ .field("field", &TupleInStruct::field)
+ ;
+
+ function("emval_test_take_and_return_TupleInStruct", &emval_test_take_and_return_TupleInStruct);
+
+ class_<ValHolder>("ValHolder")
+ .constructor<val>()
+ .method("getVal", &ValHolder::getVal)
+ .method("setVal", &ValHolder::setVal)
+ .classmethod("some_class_method", &ValHolder::some_class_method)
+ ;
+ 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_<StringHolder>("StringHolder")
+ .constructor<std::string>()
+ .method("set", &StringHolder::set)
+ .method("get", &StringHolder::get)
+ ;
+
+ class_<CustomStruct>("CustomStruct")
+ .constructor<>()
+ .field("field", &CustomStruct::field)
+ ;
+
+ enum_<Enum>("Enum")
+ .value("ONE", ONE)
+ .value("TWO", TWO)
+ ;
+ function("emval_test_take_and_return_Enum", &emval_test_take_and_return_Enum);
+
+ enum_<EnumClass>("EnumClass")
+ .value("ONE", EnumClass::ONE)
+ .value("TWO", EnumClass::TWO)
+ ;
+ 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_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);
+}));
diff --git a/tests/embind/embind_test.js b/tests/embind/embind_test.js
new file mode 100644
index 00000000..326bf740
--- /dev/null
+++ b/tests/embind/embind_test.js
@@ -0,0 +1,341 @@
+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());
+
+ 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() {
+ 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() {
+ 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);
+ },
+ });
+});