summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/embind/embind.js301
-rw-r--r--src/embind/emval.js16
-rw-r--r--system/include/emscripten/bind.h158
-rw-r--r--system/include/emscripten/val.h14
-rw-r--r--tests/embind/embind.test.js414
-rw-r--r--tests/embind/embind_test.cpp277
6 files changed, 1051 insertions, 129 deletions
diff --git a/src/embind/embind.js b/src/embind/embind.js
index 4821c77b..124ea569 100644
--- a/src/embind/embind.js
+++ b/src/embind/embind.js
@@ -4,10 +4,12 @@
/*global readLatin1String*/
/*global __emval_register, _emval_handle_array, __emval_decref*/
/*global ___getTypeName*/
+/*global requireHandle*/
/*jslint sub:true*/ /* The symbols 'fromWireType' and 'toWireType' must be accessed via array notation to be closure-safe since craftInvokerFunction crafts functions as strings that can't be closured. */
var InternalError = Module['InternalError'] = extendError(Error, 'InternalError');
var BindingError = Module['BindingError'] = extendError(Error, 'BindingError');
var UnboundTypeError = Module['UnboundTypeError'] = extendError(BindingError, 'UnboundTypeError');
+var PureVirtualError = Module['PureVirtualError'] = extendError(BindingError, 'PureVirtualError');
function throwInternalError(message) {
throw new InternalError(message);
@@ -151,6 +153,59 @@ function _embind_repr(v) {
}
}
+// raw pointer -> instance
+var registeredInstances = {};
+
+function getBasestPointer(class_, ptr) {
+ if (ptr === undefined) {
+ throwBindingError('ptr should not be undefined');
+ }
+ while (class_.baseClass) {
+ ptr = class_.upcast(ptr);
+ class_ = class_.baseClass;
+ }
+ return ptr;
+}
+
+function registerInheritedInstance(class_, ptr, instance) {
+ ptr = getBasestPointer(class_, ptr);
+ if (registeredInstances.hasOwnProperty(ptr)) {
+ throwBindingError('Tried to register registered instance: ' + ptr);
+ } else {
+ registeredInstances[ptr] = instance;
+ }
+}
+
+function unregisterInheritedInstance(class_, ptr) {
+ ptr = getBasestPointer(class_, ptr);
+ if (registeredInstances.hasOwnProperty(ptr)) {
+ delete registeredInstances[ptr];
+ } else {
+ throwBindingError('Tried to unregister unregistered instance: ' + ptr);
+ }
+}
+
+function getInheritedInstance(class_, ptr) {
+ ptr = getBasestPointer(class_, ptr);
+ return registeredInstances[ptr];
+}
+
+function getInheritedInstanceCount() {
+ return Object.keys(registeredInstances).length;
+}
+Module['getInheritedInstanceCount'] = getInheritedInstanceCount;
+
+function getLiveInheritedInstances() {
+ var rv = [];
+ for (var k in registeredInstances) {
+ if (registeredInstances.hasOwnProperty(k)) {
+ rv.push(registeredInstances[k]);
+ }
+ }
+ return rv;
+}
+Module['getLiveInheritedInstances'] = getLiveInheritedInstances;
+
// typeID -> { toWireType: ..., fromWireType: ... }
var registeredTypes = {};
@@ -535,6 +590,9 @@ function __embind_register_emval(rawType, name) {
'argPackAdvance': 8,
'readValueFromPointer': simpleReadValueFromPointer,
destructorFunction: null, // This type does not need a destructor
+
+ // TODO: do we need a deleteObject here? write a test where
+ // emval is passed into JS via an interface
});
}
@@ -630,7 +688,7 @@ function craftInvokerFunction(humanName, argTypes, classType, cppInvokerFunc, cp
var argsList = "";
var argsListWired = "";
- for(var i = 0; i < argCount-2; ++i) {
+ for(var i = 0; i < argCount - 2; ++i) {
argsList += (i!==0?", ":"")+"arg"+i;
argsListWired += (i!==0?", ":"")+"arg"+i+"Wired";
}
@@ -665,7 +723,7 @@ function craftInvokerFunction(humanName, argTypes, classType, cppInvokerFunc, cp
invokerFnBody += "var thisWired = classParam.toWireType("+dtorStack+", this);\n";
}
- for(var i = 0; i < argCount-2; ++i) {
+ for(var i = 0; i < argCount - 2; ++i) {
invokerFnBody += "var arg"+i+"Wired = argType"+i+".toWireType("+dtorStack+", arg"+i+"); // "+argTypes[i+2].name+"\n";
args1.push("argType"+i);
args2.push(argTypes[i+2]);
@@ -684,7 +742,7 @@ function craftInvokerFunction(humanName, argTypes, classType, cppInvokerFunc, cp
invokerFnBody += "runDestructors(destructors);\n";
} else {
for(var i = isClassMethodFunc?1:2; i < argTypes.length; ++i) { // Skip return value at index 0 - it's not deleted here. Also skip class type if not a method.
- var paramName = (i === 1 ? "thisWired" : ("arg"+(i-2)+"Wired"));
+ var paramName = (i === 1 ? "thisWired" : ("arg"+(i - 2)+"Wired"));
if (argTypes[i].destructorFunction !== null) {
invokerFnBody += paramName+"_dtor("+paramName+"); // "+argTypes[i].name+"\n";
args1.push(paramName+"_dtor");
@@ -1124,14 +1182,14 @@ function RegisteredPointer(
}
}
-RegisteredPointer.prototype.getPointee = function(ptr) {
+RegisteredPointer.prototype.getPointee = function getPointee(ptr) {
if (this.rawGetPointee) {
ptr = this.rawGetPointee(ptr);
}
return ptr;
};
-RegisteredPointer.prototype.destructor = function(ptr) {
+RegisteredPointer.prototype.destructor = function destructor(ptr) {
if (this.rawDestructor) {
this.rawDestructor(ptr);
}
@@ -1140,7 +1198,13 @@ RegisteredPointer.prototype.destructor = function(ptr) {
RegisteredPointer.prototype['argPackAdvance'] = 8;
RegisteredPointer.prototype['readValueFromPointer'] = simpleReadValueFromPointer;
-RegisteredPointer.prototype['fromWireType'] = function(ptr) {
+RegisteredPointer.prototype['deleteObject'] = function deleteObject(handle) {
+ if (handle !== null) {
+ handle['delete']();
+ }
+};
+
+RegisteredPointer.prototype['fromWireType'] = function fromWireType(ptr) {
// ptr is a raw pointer (or a raw smartpointer)
// rawPointer is a maybe-null raw pointer
@@ -1150,6 +1214,13 @@ RegisteredPointer.prototype['fromWireType'] = function(ptr) {
return null;
}
+ var registeredInstance = getInheritedInstance(this.registeredClass, rawPointer);
+ if (undefined !== registeredInstance) {
+ var rv = registeredInstance['clone']();
+ this.destructor(ptr);
+ return rv;
+ }
+
function makeDefaultHandle() {
if (this.isSmartPointer) {
return makeClassHandle(this.registeredClass.instancePrototype, {
@@ -1225,7 +1296,7 @@ function getInstanceTypeName(handle) {
return handle.$$.ptrType.registeredClass.name;
}
-ClassHandle.prototype['isAliasOf'] = function(other) {
+ClassHandle.prototype['isAliasOf'] = function isAliasOf(other) {
if (!(this instanceof ClassHandle)) {
return false;
}
@@ -1255,19 +1326,24 @@ function throwInstanceAlreadyDeleted(obj) {
throwBindingError(getInstanceTypeName(obj) + ' instance already deleted');
}
-ClassHandle.prototype['clone'] = function() {
+ClassHandle.prototype['clone'] = function clone() {
if (!this.$$.ptr) {
throwInstanceAlreadyDeleted(this);
}
- var clone = Object.create(Object.getPrototypeOf(this), {
- $$: {
- value: shallowCopy(this.$$),
- }
- });
+ if (this.$$.preservePointerOnDelete) {
+ this.$$.count.value += 1;
+ return this;
+ } else {
+ var clone = Object.create(Object.getPrototypeOf(this), {
+ $$: {
+ value: shallowCopy(this.$$),
+ }
+ });
- clone.$$.count.value += 1;
- return clone;
+ clone.$$.count.value += 1;
+ return clone;
+ }
};
function runDestructor(handle) {
@@ -1283,16 +1359,20 @@ ClassHandle.prototype['delete'] = function ClassHandle_delete() {
if (!this.$$.ptr) {
throwInstanceAlreadyDeleted(this);
}
- if (this.$$.deleteScheduled) {
+
+ if (this.$$.deleteScheduled && !this.$$.preservePointerOnDelete) {
throwBindingError('Object already scheduled for deletion');
}
this.$$.count.value -= 1;
- if (0 === this.$$.count.value) {
+ var toDelete = 0 === this.$$.count.value;
+ if (toDelete) {
runDestructor(this);
}
- this.$$.smartPtr = undefined;
- this.$$.ptr = undefined;
+ if (!this.$$.preservePointerOnDelete) {
+ this.$$.smartPtr = undefined;
+ this.$$.ptr = undefined;
+ }
};
var deletionQueue = [];
@@ -1305,7 +1385,7 @@ ClassHandle.prototype['deleteLater'] = function deleteLater() {
if (!this.$$.ptr) {
throwInstanceAlreadyDeleted(this);
}
- if (this.$$.deleteScheduled) {
+ if (this.$$.deleteScheduled && !this.$$.preservePointerOnDelete) {
throwBindingError('Object already scheduled for deletion');
}
deletionQueue.push(this);
@@ -1351,12 +1431,15 @@ function RegisteredClass(
this.getActualType = getActualType;
this.upcast = upcast;
this.downcast = downcast;
+ this.pureVirtualFunctions = [];
}
function shallowCopy(o) {
var rv = {};
for (var k in o) {
- rv[k] = o[k];
+ if (Object.prototype.hasOwnProperty.call(o, k)) {
+ rv[k] = o[k];
+ }
}
return rv;
}
@@ -1491,12 +1574,12 @@ function __embind_register_class_constructor(
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() {
+ classType.registeredClass.constructor_body[argCount - 1] = function unboundTypeHandler() {
throwUnboundTypeError('Cannot construct ' + classType.name + ' due to unbound types', rawArgTypes);
};
whenDependentTypesAreResolved([], rawArgTypes, function(argTypes) {
- classType.registeredClass.constructor_body[argCount - 1] = function() {
+ classType.registeredClass.constructor_body[argCount - 1] = function constructor_body() {
if (arguments.length !== argCount - 1) {
throwBindingError(humanName + ' called with ' + arguments.length + ' arguments, expected ' + (argCount-1));
}
@@ -1569,7 +1652,8 @@ function __embind_register_class_function(
rawArgTypesAddr, // [ReturnType, ThisType, Args...]
invokerSignature,
rawInvoker,
- context
+ context,
+ isPureVirtual
) {
var rawArgTypes = heap32VectorToArray(argCount, rawArgTypesAddr);
methodName = readLatin1String(methodName);
@@ -1579,21 +1663,25 @@ function __embind_register_class_function(
classType = classType[0];
var humanName = classType.name + '.' + methodName;
- var unboundTypesHandler = function() {
+ if (isPureVirtual) {
+ classType.registeredClass.pureVirtualFunctions.push(methodName);
+ }
+
+ function unboundTypesHandler() {
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)) {
+ 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.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;
+ proto[methodName].overloadTable[argCount - 2] = unboundTypesHandler;
}
whenDependentTypesAreResolved([], rawArgTypes, function(argTypes) {
@@ -1605,7 +1693,7 @@ function __embind_register_class_function(
if (undefined === proto[methodName].overloadTable) {
proto[methodName] = memberFunction;
} else {
- proto[methodName].overloadTable[argCount-2] = memberFunction;
+ proto[methodName].overloadTable[argCount - 2] = memberFunction;
}
return [];
@@ -1614,53 +1702,6 @@ function __embind_register_class_function(
});
}
-function __embind_register_class_class_function(
- rawClassType,
- methodName,
- argCount,
- rawArgTypesAddr,
- invokerSignature,
- rawInvoker,
- fn
-) {
- var rawArgTypes = heap32VectorToArray(argCount, rawArgTypesAddr);
- methodName = readLatin1String(methodName);
- rawInvoker = requireFunction(invokerSignature, rawInvoker);
- whenDependentTypesAreResolved([], [rawClassType], function(classType) {
- classType = classType[0];
- var humanName = classType.name + '.' + methodName;
-
- var unboundTypesHandler = function() {
- throwUnboundTypeError('Cannot call ' + humanName + ' due to unbound types', rawArgTypes);
- };
-
- var 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;
- }
-
- whenDependentTypesAreResolved([], rawArgTypes, function(argTypes) {
- // Replace the initial unbound-types-handler stub with the proper function. If multiple overloads are registered,
- // the function handlers go into an overload table.
- var invokerArgsArray = [argTypes[0] /* return value */, null /* no class 'this'*/].concat(argTypes.slice(1) /* actual params */);
- var func = craftInvokerFunction(humanName, invokerArgsArray, null /* no class 'this'*/, rawInvoker, fn);
- if (undefined === proto[methodName].overloadTable) {
- proto[methodName] = func;
- } else {
- proto[methodName].overloadTable[argCount-1] = func;
- }
- return [];
- });
- return [];
- });
-}
-
function __embind_register_class_property(
classType,
fieldName,
@@ -1730,6 +1771,112 @@ function __embind_register_class_property(
});
}
+function __embind_register_class_class_function(
+ rawClassType,
+ methodName,
+ argCount,
+ rawArgTypesAddr,
+ invokerSignature,
+ rawInvoker,
+ fn
+) {
+ var rawArgTypes = heap32VectorToArray(argCount, rawArgTypesAddr);
+ methodName = readLatin1String(methodName);
+ rawInvoker = requireFunction(invokerSignature, rawInvoker);
+ whenDependentTypesAreResolved([], [rawClassType], function(classType) {
+ classType = classType[0];
+ var humanName = classType.name + '.' + methodName;
+
+ function unboundTypesHandler() {
+ 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;
+ }
+
+ whenDependentTypesAreResolved([], rawArgTypes, function(argTypes) {
+ // Replace the initial unbound-types-handler stub with the proper function. If multiple overloads are registered,
+ // the function handlers go into an overload table.
+ var invokerArgsArray = [argTypes[0] /* return value */, null /* no class 'this'*/].concat(argTypes.slice(1) /* actual params */);
+ var func = craftInvokerFunction(humanName, invokerArgsArray, null /* no class 'this'*/, rawInvoker, fn);
+ if (undefined === proto[methodName].overloadTable) {
+ proto[methodName] = func;
+ } else {
+ proto[methodName].overloadTable[argCount-1] = func;
+ }
+ return [];
+ });
+ return [];
+ });
+}
+
+function __embind_create_inheriting_constructor(constructorName, wrapperType, properties) {
+ constructorName = readLatin1String(constructorName);
+ wrapperType = requireRegisteredType(wrapperType, 'wrapper');
+ properties = requireHandle(properties);
+
+ var arraySlice = [].slice;
+
+ var registeredClass = wrapperType.registeredClass;
+ var wrapperPrototype = registeredClass.instancePrototype;
+ var baseClass = registeredClass.baseClass;
+ var baseClassPrototype = baseClass.instancePrototype;
+ var baseConstructor = registeredClass.baseClass.constructor;
+ var ctor = createNamedFunction(constructorName, function() {
+ registeredClass.baseClass.pureVirtualFunctions.forEach(function(name) {
+ if (this[name] === baseClassPrototype[name]) {
+ throw new PureVirtualError('Pure virtual function ' + name + ' must be implemented in JavaScript');
+ }
+ }.bind(this));
+
+ Object.defineProperty(this, '__parent', {
+ value: wrapperPrototype
+ });
+ this.__construct.apply(this, arraySlice.call(arguments));
+ });
+
+ // It's a little nasty that we're modifying the wrapper prototype here.
+
+ wrapperPrototype.__construct = function __construct() {
+ if (this === wrapperPrototype) {
+ throwBindingError("Pass correct 'this' to __construct");
+ }
+
+ var inner = baseConstructor.implement.apply(
+ undefined,
+ [this].concat(arraySlice.call(arguments)));
+ var $$ = inner.$$;
+ inner.notifyOnDestruction();
+ $$.preservePointerOnDelete = true;
+ Object.defineProperty(this, '$$', {
+ value: $$
+ });
+ registerInheritedInstance(registeredClass, $$.ptr, this);
+ };
+
+ wrapperPrototype.__destruct = function __destruct() {
+ if (this === wrapperPrototype) {
+ throwBindingError("Pass correct 'this' to __destruct");
+ }
+
+ unregisterInheritedInstance(registeredClass, this.$$.ptr);
+ };
+
+ ctor.prototype = Object.create(wrapperPrototype);
+ for (var p in properties) {
+ ctor.prototype[p] = properties[p];
+ }
+ return __emval_register(ctor);
+}
+
var char_0 = '0'.charCodeAt(0);
var char_9 = '9'.charCodeAt(0);
function makeLegalFunctionName(name) {
diff --git a/src/embind/emval.js b/src/embind/emval.js
index ebec3881..1661bc02 100644
--- a/src/embind/emval.js
+++ b/src/embind/emval.js
@@ -265,7 +265,14 @@ function __emval_get_method_caller(argCount, argTypes) {
" args += argType" + i + ".argPackAdvance;\n";
}
functionBody +=
- " var rv = handle[name](" + argsList + ");\n" +
+ " var rv = handle[name](" + argsList + ");\n";
+ for (var i = 0; i < argCount - 1; ++i) {
+ if (types[i + 1]['deleteObject']) {
+ functionBody +=
+ " argType" + i + ".deleteObject(arg" + i + ");\n";
+ }
+ }
+ functionBody +=
" return retType.toWireType(destructors, rv);\n" +
"};\n";
@@ -281,10 +288,13 @@ function __emval_call_method(caller, handle, methodName, destructorsRef, args) {
return caller(handle, methodName, allocateDestructors(destructorsRef), args);
}
-function __emval_has_function(handle, name) {
+function __emval_has_function(handle, name, classType) {
handle = requireHandle(handle);
name = getStringOrSymbol(name);
- return handle[name] instanceof Function;
+ classType = requireRegisteredType(classType, 'class wrapper filter');
+
+ var filter = classType.registeredClass.instancePrototype[name];
+ return (handle[name] instanceof Function) && (filter === undefined || handle[name] !== filter);
}
function __emval_typeof(handle) {
diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h
index eede0755..7bc28ff6 100644
--- a/system/include/emscripten/bind.h
+++ b/system/include/emscripten/bind.h
@@ -149,7 +149,8 @@ namespace emscripten {
TYPEID argTypes[],
const char* invokerSignature,
GenericFunction invoker,
- void* context);
+ void* context,
+ unsigned isPureVirtual);
void _embind_register_class_property(
TYPEID classType,
@@ -172,6 +173,11 @@ namespace emscripten {
GenericFunction invoker,
GenericFunction method);
+ EM_VAL _embind_create_inheriting_constructor(
+ const char* constructorName,
+ TYPEID wrapperType,
+ EM_VAL properties);
+
void _embind_register_enum(
TYPEID enumType,
const char* name,
@@ -276,6 +282,33 @@ namespace emscripten {
return method;
}
+ namespace internal {
+ // this should be in <type_traits>, but alas, it's not
+ template<typename T> struct remove_class;
+ template<typename C, typename R, typename... A>
+ struct remove_class<R(C::*)(A...)> { using type = R(A...); };
+ template<typename C, typename R, typename... A>
+ struct remove_class<R(C::*)(A...) const> { using type = R(A...); };
+ template<typename C, typename R, typename... A>
+ struct remove_class<R(C::*)(A...) volatile> { using type = R(A...); };
+ template<typename C, typename R, typename... A>
+ struct remove_class<R(C::*)(A...) const volatile> { using type = R(A...); };
+
+ template<typename LambdaType>
+ struct CalculateLambdaSignature {
+ using type = typename std::add_pointer<
+ typename remove_class<
+ decltype(&LambdaType::operator())
+ >::type
+ >::type;
+ };
+ }
+
+ template<typename LambdaType>
+ typename internal::CalculateLambdaSignature<LambdaType>::type optional_override(const LambdaType& fp) {
+ return fp;
+ }
+
////////////////////////////////////////////////////////////////////////////////
// Invoker
////////////////////////////////////////////////////////////////////////////////
@@ -873,40 +906,55 @@ namespace emscripten {
};
};
+
////////////////////////////////////////////////////////////////////////////////
// CLASSES
////////////////////////////////////////////////////////////////////////////////
+ namespace internal {
+ class WrapperBase {
+ public:
+ void setNotifyJSOnDestruction(bool notify) {
+ notifyJSOnDestruction = notify;
+ }
+
+ protected:
+ bool notifyJSOnDestruction = false;
+ };
+ }
+
// abstract classes
template<typename T>
- class wrapper : public T {
+ class wrapper : public T, public internal::WrapperBase {
public:
typedef T class_type;
- explicit wrapper(val&& wrapped)
- : wrapped(std::forward<val>(wrapped))
+ template<typename... Args>
+ explicit wrapper(val&& wrapped, Args&&... args)
+ : T(std::forward<Args>(args)...)
+ , wrapped(std::forward<val>(wrapped))
{}
+ ~wrapper() {
+ if (notifyJSOnDestruction) {
+ call<void>("__destruct");
+ }
+ }
+
template<typename ReturnType, typename... Args>
ReturnType call(const char* name, Args&&... args) const {
return wrapped.call<ReturnType>(name, std::forward<Args>(args)...);
}
- template<typename ReturnType, typename... Args, typename Default>
- ReturnType optional_call(const char* name, Default def, Args&&... args) const {
- if (wrapped.has_function(name)) {
- return call<ReturnType>(name, std::forward<Args>(args)...);
- } else {
- return def();
- }
- }
-
private:
val wrapped;
};
-#define EMSCRIPTEN_WRAPPER(T) \
- T(::emscripten::val&& v): wrapper(std::forward<::emscripten::val>(v)) {}
+#define EMSCRIPTEN_WRAPPER(T) \
+ template<typename... Args> \
+ T(::emscripten::val&& v, Args&&... args) \
+ : wrapper(std::forward<::emscripten::val>(v), std::forward<Args>(args)...) \
+ {}
namespace internal {
struct NoBaseClass {
@@ -987,6 +1035,45 @@ namespace emscripten {
SmartPtrIfNeeded(U&, const char*) {
}
};
+
+ template<typename WrapperType>
+ val wrapped_extend(const std::string& name, const val& properties) {
+ return val::take_ownership(_embind_create_inheriting_constructor(
+ name.c_str(),
+ TypeID<WrapperType>::get(),
+ properties.__get_handle()));
+ }
+ };
+
+ struct pure_virtual {
+ template<typename InputType, int Index>
+ struct Transform {
+ typedef InputType type;
+ };
+ };
+
+ namespace internal {
+ template<typename... Policies>
+ struct isPureVirtual;
+
+ template<typename... Rest>
+ struct isPureVirtual<pure_virtual, Rest...> {
+ static constexpr bool value = true;
+ };
+
+ template<typename T, typename... Rest>
+ struct isPureVirtual<T, Rest...> {
+ static constexpr bool value = isPureVirtual<Rest...>::value;
+ };
+
+ template<>
+ struct isPureVirtual<> {
+ static constexpr bool value = false;
+ };
+ }
+
+ template<typename... ConstructorArgs>
+ struct constructor {
};
template<typename ClassType, typename BaseSpecifier = internal::NoBaseClass>
@@ -1095,18 +1182,38 @@ namespace emscripten {
return *this;
}
- template<typename WrapperType, typename PointerType = WrapperType*>
- const class_& allow_subclass(const char* wrapperClassName, const char* pointerName = "<UnknownPointerName>") const {
+ template<typename WrapperType, typename PointerType = WrapperType*, typename... ConstructorArgs>
+ const class_& allow_subclass(
+ const char* wrapperClassName,
+ const char* pointerName = "<UnknownPointerName>",
+ ::emscripten::constructor<ConstructorArgs...> = ::emscripten::constructor<ConstructorArgs...>()
+ ) const {
using namespace internal;
auto cls = class_<WrapperType, base<ClassType>>(wrapperClassName)
+ .function("notifyOnDestruction", select_overload<void(WrapperType&)>([](WrapperType& wrapper) {
+ wrapper.setNotifyJSOnDestruction(true);
+ }))
;
SmartPtrIfNeeded<PointerType> _(cls, pointerName);
- return class_function(
- "implement",
- &wrapped_new<PointerType, WrapperType, val>,
- allow_raw_pointer<ret_val>());
+ return
+ class_function(
+ "implement",
+ &wrapped_new<PointerType, WrapperType, val, ConstructorArgs...>,
+ allow_raw_pointer<ret_val>())
+ .class_function(
+ "extend",
+ &wrapped_extend<WrapperType>)
+ ;
+ }
+
+ template<typename WrapperType, typename... ConstructorArgs>
+ const class_& allow_subclass(
+ const char* wrapperClassName,
+ ::emscripten::constructor<ConstructorArgs...> constructor
+ ) const {
+ return allow_subclass<WrapperType, WrapperType*>(wrapperClassName, "<UnknownPointerName>", constructor);
}
template<typename ReturnType, typename... Args, typename... Policies>
@@ -1123,7 +1230,8 @@ namespace emscripten {
args.types,
getSignature(invoker),
reinterpret_cast<GenericFunction>(invoker),
- getContext(memberFunction));
+ getContext(memberFunction),
+ isPureVirtual<Policies...>::value);
return *this;
}
@@ -1141,7 +1249,8 @@ namespace emscripten {
args.types,
getSignature(invoker),
reinterpret_cast<GenericFunction>(invoker),
- getContext(memberFunction));
+ getContext(memberFunction),
+ isPureVirtual<Policies...>::value);
return *this;
}
@@ -1158,7 +1267,8 @@ namespace emscripten {
args.types,
getSignature(invoke),
reinterpret_cast<GenericFunction>(invoke),
- getContext(function));
+ getContext(function),
+ false);
return *this;
}
diff --git a/system/include/emscripten/val.h b/system/include/emscripten/val.h
index bfd8610a..31f5923e 100644
--- a/system/include/emscripten/val.h
+++ b/system/include/emscripten/val.h
@@ -61,7 +61,8 @@ namespace emscripten {
EM_VAR_ARGS argv);
bool _emval_has_function(
EM_VAL value,
- const char* methodName);
+ const char* methodName,
+ internal::TYPEID filter);
EM_VAL _emval_typeof(EM_VAL value);
}
@@ -392,8 +393,10 @@ namespace emscripten {
return MethodCaller<ReturnValue, Args...>::call(handle, name, std::forward<Args>(args)...);
}
- bool has_function(const char* name) const {
- return _emval_has_function(handle, name);
+ template<typename ClassType>
+ bool has_implementation_defined_function(const char* name) const {
+ using namespace internal;
+ return _emval_has_function(handle, name, TypeID<ClassType>::get());
}
template<typename T>
@@ -411,6 +414,11 @@ namespace emscripten {
return fromGenericWireType<T>(result);
}
+ // private: TODO: use a friend?
+ internal::EM_VAL __get_handle() const {
+ return handle;
+ }
+
val typeof() const {
return val(_emval_typeof(handle));
}
diff --git a/tests/embind/embind.test.js b/tests/embind/embind.test.js
index 53d3988a..a6b2e98c 100644
--- a/tests/embind/embind.test.js
+++ b/tests/embind/embind.test.js
@@ -1548,7 +1548,7 @@ module({
});
});
- BaseFixture.extend("abstract methods", function() {
+ BaseFixture.extend("implementing abstract methods with JS objects", function() {
test("can call abstract methods", function() {
var obj = cm.getAbstractClass();
assert.equal("from concrete", obj.abstractMethod());
@@ -1580,7 +1580,8 @@ module({
};
var impl = cm.AbstractClass.implement(new MyImplementation);
- assert.equal(expected, impl.optionalMethod(expected));
+ // TODO: remove .implement() as a public API. It interacts poorly with Class.extend.
+ //assert.equal(expected, impl.optionalMethod(expected));
assert.equal(expected, cm.callOptionalMethod(impl, expected));
impl.delete();
});
@@ -1588,7 +1589,8 @@ module({
test("if not implemented then optional method runs default", function() {
var impl = cm.AbstractClass.implement({});
assert.equal("optionalfoo", impl.optionalMethod("foo"));
- assert.equal("optionalfoo", cm.callOptionalMethod(impl, "foo"));
+ // TODO: remove .implement() as a public API. It interacts poorly with Class.extend.
+ //assert.equal("optionalfoo", cm.callOptionalMethod(impl, "foo"));
impl.delete();
});
@@ -1638,6 +1640,366 @@ module({
impl.delete();
});
+
+ test("returning a cached new shared pointer from interfaces implemented in JS code does not leak", function() {
+ var derived = cm.embind_test_return_smart_derived_ptr();
+ var impl = cm.AbstractClass.implement({
+ returnsSharedPtr: function() {
+ return derived;
+ }
+ });
+ cm.callReturnsSharedPtrMethod(impl);
+ impl.delete();
+ derived.delete();
+ // Let the memory leak test superfixture check that no leaks occurred.
+ });
+ });
+
+ BaseFixture.extend("constructor prototype class inheritance", function() {
+ var Empty = cm.AbstractClass.extend("Empty", {
+ abstractMethod: function() {
+ }
+ });
+
+ test("can extend, construct, and delete", function() {
+ var instance = new Empty;
+ instance.delete();
+ });
+
+ test("properties set in constructor are externally visible", function() {
+ var HasProperty = cm.AbstractClass.extend("HasProperty", {
+ __construct: function(x) {
+ this.__parent.__construct.call(this);
+ this.property = x;
+ },
+ abstractMethod: function() {
+ }
+ });
+ var instance = new HasProperty(10);
+ assert.equal(10, instance.property);
+ instance.delete();
+ });
+
+ test("pass derived object to c++", function() {
+ var Implementation = cm.AbstractClass.extend("Implementation", {
+ abstractMethod: function() {
+ return "abc";
+ },
+ });
+ var instance = new Implementation;
+ var result = cm.callAbstractMethod(instance);
+ instance.delete();
+ assert.equal("abc", result);
+ });
+
+ test("properties set in constructor are visible in overridden methods", function() {
+ var HasProperty = cm.AbstractClass.extend("HasProperty", {
+ __construct: function(x) {
+ this.__parent.__construct.call(this);
+ this.x = x;
+ },
+ abstractMethod: function() {
+ return this.x;
+ },
+ });
+ var instance = new HasProperty("xyz");
+ var result = cm.callAbstractMethod(instance);
+ instance.delete();
+ assert.equal("xyz", result);
+ });
+
+ test("interface methods are externally visible", function() {
+ var instance = new Empty;
+ var result = instance.concreteMethod();
+ instance.delete();
+ assert.equal("concrete", result);
+ });
+
+ test("optional methods are externally visible", function() {
+ var instance = new Empty;
+ var result = instance.optionalMethod("_123");
+ instance.delete();
+ assert.equal("optional_123", result);
+ });
+
+ test("optional methods: not defined", function() {
+ var instance = new Empty;
+ var result = cm.callOptionalMethod(instance, "_123");
+ instance.delete();
+ assert.equal("optional_123", result);
+ });
+
+ // Calling C++ implementations of optional functions can be
+ // made to work, but requires an interface change on the C++
+ // side, using a technique similar to the one described at
+ // https://wiki.python.org/moin/boost.python/OverridableVirtualFunctions
+ //
+ // The issue is that, in a standard binding, calling
+ // parent.prototype.optionalMethod invokes the wrapper
+ // function, which checks that the JS object implements
+ // 'optionalMethod', which it does. Thus, C++ calls back into
+ // JS, resulting in an infinite loop.
+ //
+ // The solution, for optional methods, is to bind a special
+ // concrete implementation that specifically calls the base
+ // class's implementation. See the binding of
+ // AbstractClass::optionalMethod in embind_test.cpp.
+
+ test("can call parent implementation from within derived implementation", function() {
+ var parent = cm.AbstractClass;
+ var ExtendsOptionalMethod = parent.extend("ExtendsOptionalMethod", {
+ abstractMethod: function() {
+ },
+ optionalMethod: function(s) {
+ return "optionaljs_" + parent.prototype.optionalMethod.call(this, s);
+ },
+ });
+ var instance = new ExtendsOptionalMethod;
+ var result = cm.callOptionalMethod(instance, "_123");
+ instance.delete();
+ assert.equal("optionaljs_optional_123", result);
+ });
+
+ // TODO: deriving from classes with constructors?
+
+ test("instanceof", function() {
+ var instance = new Empty;
+ assert.instanceof(instance, Empty);
+ assert.instanceof(instance, cm.AbstractClass);
+ instance.delete();
+ });
+
+ test("returning null shared pointer from interfaces implemented in JS code does not leak", function() {
+ var C = cm.AbstractClass.extend("C", {
+ abstractMethod: function() {
+ },
+ returnsSharedPtr: function() {
+ return null;
+ }
+ });
+ var impl = new C;
+ cm.callReturnsSharedPtrMethod(impl);
+ impl.delete();
+ // Let the memory leak test superfixture check that no leaks occurred.
+ });
+
+ test("returning a new shared pointer from interfaces implemented in JS code does not leak", function() {
+ var C = cm.AbstractClass.extend("C", {
+ abstractMethod: function() {
+ },
+ returnsSharedPtr: function() {
+ return cm.embind_test_return_smart_derived_ptr().deleteLater();
+ }
+ });
+ var impl = new C;
+ cm.callReturnsSharedPtrMethod(impl);
+ impl.delete();
+ // Let the memory leak test superfixture check that no leaks occurred.
+ });
+
+ test("void methods work", function() {
+ var saved = {};
+ var C = cm.AbstractClass.extend("C", {
+ abstractMethod: function() {
+ },
+ differentArguments: function(i, d, f, q, s) {
+ saved.i = i;
+ saved.d = d;
+ saved.f = f;
+ saved.q = q;
+ saved.s = s;
+ }
+ });
+ var impl = new C;
+
+ cm.callDifferentArguments(impl, 1, 2, 3, 4, "foo");
+
+ assert.deepEqual(saved, {
+ i: 1,
+ d: 2,
+ f: 3,
+ q: 4,
+ s: "foo",
+ });
+
+ impl.delete();
+ });
+
+ test("returning a cached new shared pointer from interfaces implemented in JS code does not leak", function() {
+ var derived = cm.embind_test_return_smart_derived_ptr();
+ var C = cm.AbstractClass.extend("C", {
+ abstractMethod: function() {
+ },
+ returnsSharedPtr: function() {
+ return derived;
+ }
+ });
+ var impl = new C;
+ cm.callReturnsSharedPtrMethod(impl);
+ impl.delete();
+ derived.delete();
+ // Let the memory leak test superfixture check that no leaks occurred.
+ });
+
+ test("calling pure virtual function gives good error message", function() {
+ var C = cm.AbstractClass.extend("C", {});
+ var error = assert.throws(cm.PureVirtualError, function() {
+ new C;
+ });
+ assert.equal('Pure virtual function abstractMethod must be implemented in JavaScript', error.message);
+ });
+
+ test("can extend from C++ class with constructor arguments", function() {
+ var parent = cm.AbstractClassWithConstructor;
+ var C = parent.extend("C", {
+ __construct: function(x) {
+ this.__parent.__construct.call(this, x);
+ },
+ abstractMethod: function() {
+ return this.concreteMethod();
+ }
+ });
+
+ var impl = new C("hi");
+ var rv = cm.callAbstractMethod2(impl);
+ impl.delete();
+
+ assert.equal("hi", rv);
+ });
+
+ test("__destruct is called when object is destroyed", function() {
+ var parent = cm.HeldAbstractClass;
+ var calls = [];
+ var C = parent.extend("C", {
+ method: function() {
+ },
+ __destruct: function() {
+ calls.push("__destruct");
+ this.__parent.__destruct.call(this);
+ }
+ });
+ var impl = new C;
+ var copy = impl.clone();
+ impl.delete();
+ assert.deepEqual([], calls);
+ copy.delete();
+ assert.deepEqual(["__destruct"], calls);
+ });
+
+ test("if JavaScript implementation of interface is returned, don't wrap in new handle", function() {
+ var parent = cm.HeldAbstractClass;
+ var C = parent.extend("C", {
+ method: function() {
+ }
+ });
+ var impl = new C;
+ var rv = cm.passHeldAbstractClass(impl);
+ impl.delete();
+ assert.equal(impl, rv);
+ rv.delete();
+ });
+
+ test("can instantiate two wrappers with constructors", function() {
+ var parent = cm.HeldAbstractClass;
+ var C = parent.extend("C", {
+ __construct: function() {
+ this.__parent.__construct.call(this);
+ },
+ method: function() {
+ }
+ });
+ var a = new C;
+ var b = new C;
+ a.delete();
+ b.delete();
+ });
+
+ test("incorrectly calling parent is an error", function() {
+ var parent = cm.HeldAbstractClass;
+ var C = parent.extend("C", {
+ __construct: function() {
+ this.__parent.__construct();
+ },
+ method: function() {
+ }
+ });
+ assert.throws(cm.BindingError, function() {
+ new C;
+ });
+ });
+
+ test("deleteLater() works for JavaScript implementations", function() {
+ var parent = cm.HeldAbstractClass;
+ var C = parent.extend("C", {
+ method: function() {
+ }
+ });
+ var impl = new C;
+ var rv = cm.passHeldAbstractClass(impl);
+ impl.deleteLater();
+ rv.deleteLater();
+ cm.flushPendingDeletes();
+ });
+
+ test("deleteLater() combined with delete() works for JavaScript implementations", function() {
+ var parent = cm.HeldAbstractClass;
+ var C = parent.extend("C", {
+ method: function() {
+ }
+ });
+ var impl = new C;
+ var rv = cm.passHeldAbstractClass(impl);
+ impl.deleteLater();
+ rv.delete();
+ cm.flushPendingDeletes();
+ });
+
+ test("method arguments with pointer ownership semantics are cleaned up after call", function() {
+ var parent = cm.AbstractClass;
+ var C = parent.extend("C", {
+ abstractMethod: function() {
+ },
+ });
+ var impl = new C;
+ cm.passShared(impl);
+ impl.delete();
+ });
+
+ test("method arguments with pointer ownership semantics can be cloned", function() {
+ var parent = cm.AbstractClass;
+ var owned;
+ var C = parent.extend("C", {
+ abstractMethod: function() {
+ },
+ passShared: function(p) {
+ owned = p.clone();
+ }
+ });
+ var impl = new C;
+ cm.passShared(impl);
+ impl.delete();
+
+ assert.equal("Derived", owned.getClassName());
+ owned.delete();
+ });
+
+ test("emscripten::val method arguments don't leak", function() {
+ var parent = cm.AbstractClass;
+ var got;
+ var C = parent.extend("C", {
+ abstractMethod: function() {
+ },
+ passVal: function(g) {
+ got = g;
+ }
+ });
+ var impl = new C;
+ var v = {};
+ cm.passVal(impl, v);
+ impl.delete();
+
+ assert.equal(v, got);
+ });
});
BaseFixture.extend("registration order", function() {
@@ -1945,19 +2307,6 @@ module({
});
});
- test("returning a cached new shared pointer from interfaces implemented in JS code does not leak", function() {
- var derived = cm.embind_test_return_smart_derived_ptr();
- var impl = cm.AbstractClass.implement({
- returnsSharedPtr: function() {
- return derived;
- }
- });
- cm.callReturnsSharedPtrMethod(impl);
- impl.delete();
- derived.delete();
- // Let the memory leak test superfixture check that no leaks occurred.
- });
-
BaseFixture.extend("val::as", function() {
test("built-ins", function() {
assert.equal(true, cm.val_as_bool(true));
@@ -2031,6 +2380,39 @@ module({
});
});
+ BaseFixture.extend("intrusive pointers", function() {
+ test("can pass intrusive pointers", function() {
+ var ic = new cm.IntrusiveClass;
+ var d = cm.passThroughIntrusiveClass(ic);
+ assert.true(ic.isAliasOf(d));
+ ic.delete();
+ d.delete();
+ });
+
+ test("can hold intrusive pointers", function() {
+ var ic = new cm.IntrusiveClass;
+ var holder = new cm.IntrusiveClassHolder;
+ holder.set(ic);
+ ic.delete();
+ var d = holder.get();
+ d.delete();
+ holder.delete();
+ });
+
+ test("can extend from intrusive pointer class and still preserve reference in JavaScript", function() {
+ var C = cm.IntrusiveClass.extend("C", {
+ });
+ var instance = new C;
+ var holder = new cm.IntrusiveClassHolder;
+ holder.set(instance);
+ instance.delete();
+
+ var back = holder.get();
+ assert.equal(back, instance);
+ holder.delete();
+ });
+ });
+
BaseFixture.extend("typeof", function() {
test("typeof", function() {
assert.equal("object", cm.getTypeOfVal(null));
diff --git a/tests/embind/embind_test.cpp b/tests/embind/embind_test.cpp
index 52103bbb..30267994 100644
--- a/tests/embind/embind_test.cpp
+++ b/tests/embind/embind_test.cpp
@@ -1090,6 +1090,16 @@ public:
virtual std::shared_ptr<Derived> returnsSharedPtr() = 0;
virtual void differentArguments(int i, double d, unsigned char f, double q, std::string) = 0;
+
+ std::string concreteMethod() const {
+ return "concrete";
+ }
+
+ virtual void passShared(const std::shared_ptr<Derived>&) {
+ }
+
+ virtual void passVal(const val& v) {
+ }
};
EMSCRIPTEN_SYMBOL(optionalMethod);
@@ -1103,9 +1113,10 @@ public:
}
std::string optionalMethod(std::string s) const {
- return optional_call<std::string>(optionalMethod_symbol, [&] {
- return AbstractClass::optionalMethod(s);
- }, s);
+ return call<std::string>("optionalMethod", s);
+ //return optional_call<std::string>(optionalMethod_symbol, [&] {
+ // return AbstractClass::optionalMethod(s);
+ //}, s);
}
std::shared_ptr<Derived> returnsSharedPtr() {
@@ -1115,6 +1126,14 @@ public:
void differentArguments(int i, double d, unsigned char f, double q, std::string s) {
return call<void>("differentArguments", i, d, f, q, s);
}
+
+ virtual void passShared(const std::shared_ptr<Derived>& p) override {
+ return call<void>("passShared", p);
+ }
+
+ virtual void passVal(const val& v) override {
+ return call<void>("passVal", v);
+ }
};
class ConcreteClass : public AbstractClass {
@@ -1122,7 +1141,6 @@ class ConcreteClass : public AbstractClass {
return "from concrete";
}
-
void differentArguments(int i, double d, unsigned char f, double q, std::string s) {
}
@@ -1152,12 +1170,74 @@ void callDifferentArguments(AbstractClass& ac, int i, double d, unsigned char f,
return ac.differentArguments(i, d, f, q, s);
}
+struct AbstractClassWithConstructor {
+ explicit AbstractClassWithConstructor(std::string s)
+ : s(s)
+ {}
+
+ virtual std::string abstractMethod() = 0;
+ std::string concreteMethod() {
+ return s;
+ }
+
+ std::string s;
+};
+
+struct AbstractClassWithConstructorWrapper : public wrapper<AbstractClassWithConstructor> {
+ EMSCRIPTEN_WRAPPER(AbstractClassWithConstructorWrapper);
+
+ virtual std::string abstractMethod() override {
+ return call<std::string>("abstractMethod");
+ }
+};
+
+std::string callAbstractMethod2(AbstractClassWithConstructor& ac) {
+ return ac.abstractMethod();
+}
+
+struct HeldAbstractClass : public PolyBase, public PolySecondBase {
+ virtual void method() = 0;
+};
+struct HeldAbstractClassWrapper : wrapper<HeldAbstractClass> {
+ EMSCRIPTEN_WRAPPER(HeldAbstractClassWrapper);
+
+ virtual void method() override {
+ return call<void>("method");
+ }
+};
+
+std::shared_ptr<PolySecondBase> passHeldAbstractClass(std::shared_ptr<HeldAbstractClass> p) {
+ return p;
+}
+
+void passShared(AbstractClass& ac) {
+ auto p = std::make_shared<Derived>();
+ ac.passShared(p);
+}
+
+void passVal(AbstractClass& ac, val v) {
+ return ac.passVal(v);
+}
+
EMSCRIPTEN_BINDINGS(interface_tests) {
class_<AbstractClass>("AbstractClass")
.smart_ptr<std::shared_ptr<AbstractClass>>("shared_ptr<AbstractClass>")
.allow_subclass<AbstractClassWrapper>("AbstractClassWrapper")
- .function("abstractMethod", &AbstractClass::abstractMethod)
- .function("optionalMethod", &AbstractClass::optionalMethod)
+ .function("abstractMethod", &AbstractClass::abstractMethod, pure_virtual())
+ // The select_overload is necessary because, otherwise, the C++ compiler
+ // cannot deduce the signature of the lambda function.
+ .function("optionalMethod", optional_override(
+ [](AbstractClass& this_, std::string s) {
+ return this_.AbstractClass::optionalMethod(s);
+ }
+ ))
+ .function("concreteMethod", &AbstractClass::concreteMethod)
+ .function("passShared", select_overload<void(AbstractClass&, const std::shared_ptr<Derived>&)>([](AbstractClass& self, const std::shared_ptr<Derived>& derived) {
+ self.AbstractClass::passShared(derived);
+ }))
+ .function("passVal", select_overload<void(AbstractClass&, const val&)>([](AbstractClass& self, const val& v) {
+ self.AbstractClass::passVal(v);
+ }))
;
function("getAbstractClass", &getAbstractClass);
@@ -1165,6 +1245,22 @@ EMSCRIPTEN_BINDINGS(interface_tests) {
function("callOptionalMethod", &callOptionalMethod);
function("callReturnsSharedPtrMethod", &callReturnsSharedPtrMethod);
function("callDifferentArguments", &callDifferentArguments);
+ function("passShared", &passShared);
+ function("passVal", &passVal);
+
+ class_<AbstractClassWithConstructor>("AbstractClassWithConstructor")
+ .allow_subclass<AbstractClassWithConstructorWrapper>("AbstractClassWithConstructorWrapper", constructor<std::string>())
+ .function("abstractMethod", &AbstractClassWithConstructor::abstractMethod, pure_virtual())
+ .function("concreteMethod", &AbstractClassWithConstructor::concreteMethod)
+ ;
+ function("callAbstractMethod2", &callAbstractMethod2);
+
+ class_<HeldAbstractClass, base<PolySecondBase>>("HeldAbstractClass")
+ .smart_ptr<std::shared_ptr<HeldAbstractClass>>("shared_ptr<HeldAbstractClass>")
+ .allow_subclass<HeldAbstractClassWrapper, std::shared_ptr<HeldAbstractClassWrapper>>("HeldAbstractClassWrapper")
+ .function("method", &HeldAbstractClass::method, pure_virtual())
+ ;
+ function("passHeldAbstractClass", &passHeldAbstractClass);
}
template<typename T, size_t sizeOfArray>
@@ -2369,6 +2465,175 @@ EMSCRIPTEN_BINDINGS(val_new_) {
function("construct_with_ints_and_float", &construct_with_ints_and_float);
}
+template <typename T>
+class intrusive_ptr {
+public:
+ typedef T element_type;
+
+ intrusive_ptr(std::nullptr_t = nullptr)
+ : px(nullptr)
+ {}
+
+ template <typename U>
+ explicit intrusive_ptr(U* px)
+ : px(px)
+ {
+ addRef(px);
+ }
+
+ intrusive_ptr(const intrusive_ptr& that)
+ : px(that.px)
+ {
+ addRef(px);
+ }
+
+ template<typename U>
+ intrusive_ptr(const intrusive_ptr<U>& that)
+ : px(that.get())
+ {
+ addRef(px);
+ }
+
+ intrusive_ptr& operator=(const intrusive_ptr& that) {
+ reset(that.get());
+ return *this;
+ }
+
+ intrusive_ptr& operator=(intrusive_ptr&& that) {
+ release(px);
+ px = that.px;
+ that.px = 0;
+ return *this;
+ }
+
+ template<typename U>
+ intrusive_ptr& operator=(const intrusive_ptr<U>& that) {
+ reset(that.get());
+ return *this;
+ }
+
+ template<typename U>
+ intrusive_ptr& operator=(intrusive_ptr<U>&& that) {
+ release(px);
+ px = that.px;
+ that.px = 0;
+ return *this;
+ }
+
+ ~intrusive_ptr() {
+ release(px);
+ }
+
+ void reset(T* nx = nullptr) {
+ addRef(nx);
+ release(px);
+ px = nx;
+ }
+
+ T* get() const {
+ return px;
+ }
+
+ T& operator*() const {
+ return *px;
+ }
+
+ T* operator->() const {
+ return px;
+ }
+
+ explicit operator bool() const {
+ return px != nullptr;
+ }
+
+ void swap(intrusive_ptr& rhs) {
+ std::swap(px, rhs.px);
+ }
+
+private:
+ void addRef(T* px) {
+ if (px) {
+ ++px->referenceCount;
+ }
+ }
+
+ void release(T* px) {
+ if (--px->referenceCount == 0) {
+ delete px;
+ }
+ }
+
+ T* px;
+
+ template<typename U>
+ friend class intrusive_ptr;
+};
+
+namespace emscripten {
+ template<typename T>
+ struct smart_ptr_trait<intrusive_ptr<T>> {
+ typedef intrusive_ptr<T> pointer_type;
+ typedef T element_type;
+
+ static sharing_policy get_sharing_policy() {
+ return sharing_policy::INTRUSIVE;
+ }
+
+ static T* get(const intrusive_ptr<T>& p) {
+ return p.get();
+ }
+
+ static intrusive_ptr<T> share(const intrusive_ptr<T>& r, T* ptr) {
+ return intrusive_ptr<T>(ptr);
+ }
+
+ static pointer_type* construct_null() {
+ return new pointer_type;
+ }
+ };
+}
+
+template<typename T>
+intrusive_ptr<T> make_intrusive_ptr() {
+ return intrusive_ptr<T>(new T);
+}
+
+struct IntrusiveClass {
+ virtual ~IntrusiveClass() {}
+ long referenceCount = 0;
+};
+
+struct IntrusiveClassWrapper : public wrapper<IntrusiveClass> {
+ EMSCRIPTEN_WRAPPER(IntrusiveClassWrapper);
+};
+
+template<typename T>
+struct Holder {
+ void set(const T& v) {
+ value = v;
+ }
+ const T& get() const {
+ return value;
+ }
+ T value;
+};
+
+EMSCRIPTEN_BINDINGS(intrusive_pointers) {
+ class_<IntrusiveClass>("IntrusiveClass")
+ .smart_ptr_constructor("intrusive_ptr<IntrusiveClass>", &make_intrusive_ptr<IntrusiveClass>)
+ .allow_subclass<IntrusiveClassWrapper, intrusive_ptr<IntrusiveClassWrapper>>("IntrusiveClassWrapper")
+ ;
+
+ typedef Holder<intrusive_ptr<IntrusiveClass>> IntrusiveClassHolder;
+ class_<IntrusiveClassHolder>("IntrusiveClassHolder")
+ .constructor<>()
+ .function("set", &IntrusiveClassHolder::set)
+ .function("get", &IntrusiveClassHolder::get)
+ ;
+
+ function("passThroughIntrusiveClass", &passThrough<intrusive_ptr<IntrusiveClass>>);
+}
+
std::string getTypeOfVal(const val& v) {
return v.typeof().as<std::string>();
}