diff options
author | Bill Welden <bwelden@imvu.com> | 2012-12-22 10:19:52 -0800 |
---|---|---|
committer | Jukka Jylänki <jujjyl@gmail.com> | 2013-04-12 14:22:43 +0300 |
commit | 2308f2a5b19e92d9f974b4a7ee733d5c6f3da4bb (patch) | |
tree | 87cc47a16987dd783f2186e0300e4a6ca4fcf162 | |
parent | ea98beba7402b83a05434342d5b2af85aae3dd42 (diff) |
More work on auto downcasting.
-rwxr-xr-x | src/embind/embind.js | 131 | ||||
-rwxr-xr-x | system/include/emscripten/bind.h | 26 | ||||
-rwxr-xr-x | system/lib/embind/bind.cpp | 24 |
3 files changed, 141 insertions, 40 deletions
diff --git a/src/embind/embind.js b/src/embind/embind.js index 643ce728..285e632a 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -182,7 +182,12 @@ function makeInvoker(name, argCount, argTypes, invoker, fn) { for (var i = 1; i < argCount; ++i) { args[i] = argTypes[i].toWireType(destructors, arguments[i-1]); } - var rv = argTypes[0].fromWireType(invoker.apply(null, args)); + var rv = invoker.apply(null, args); + if (argTypes[0].hasOwnProperty('fromWireTypeAutoDowncast')) { + rv = argTypes[0].fromWireTypeAutoDowncast(rv); + } else { + rv = argTypes[0].fromWireType(rv); + } runDestructors(destructors); return rv; }; @@ -367,12 +372,14 @@ function __embind_register_struct_field( function __embind_register_smart_ptr( pointerType, pointeeType, + isPolymorphic, name, destructor, getPointee ) { name = Pointer_stringify(name); var pointeeTypeImpl = requireRegisteredType(pointeeType, 'class'); + pointeeTypeImpl.smartPointerType = pointerType; destructor = FUNCTION_TABLE[destructor]; getPointee = FUNCTION_TABLE[getPointee]; @@ -416,6 +423,44 @@ function __embind_register_smart_ptr( registerType(pointerType, name, { name: name, Handle: Handle, + fromWireTypeAutoDowncast: function(ptr) { + if (!getPointee(ptr)) { + destructor(ptr); + return null; + } + if (isPolymorphic) { + // todo: clean up this code + var pointee = getPointee(ptr); + var toType = ___getDynamicPointerType(pointee); + var toTypeImpl = null; + if (toType === null || toType === pointeeType) { + return new Handle(ptr); + } + // todo: getDerivationPath is expensive -- cache the result + var derivation = Module.__getDerivationPath(toType, pointeeType); + var candidate = null; + for (var i = 0; i < derivation.size(); i++) { + candidate = derivation.at(i); + toTypeImpl = typeRegistry[candidate]; + if (toTypeImpl) { + break; + } + } + derivation.delete(); + if (toTypeImpl === null) { + return new Handle(ptr); + } + var toTypePointerImpl = requireRegisteredType(toTypeImpl.smartPointerType); + // todo: need to clone the ptr here (really??) + var castPtr = toTypePointerImpl.fromWireType(ptr); + // todo: do we really need ___dynamicPointerCast here? We know what type we're starting with. + castPtr.ptr = ___dynamicPointerCast(pointee, candidate); + // todo: we need to release the pre-cast pointer, don't we? how did this get past the tests? + return castPtr; + } else { + return new Handle(ptr); + } + }, fromWireType: function(ptr) { if (!getPointee(ptr)) { destructor(ptr); @@ -493,6 +538,7 @@ function __embind_register_vector( }); } +// TODO: null pointers are always zero (not a Handle) in Javascript function __embind_register_class( classType, pointerType, @@ -513,7 +559,7 @@ function __embind_register_class( } }; - h.count = {value: 1}; + h.count = {value: 1, ptr: ptr }; h.ptr = ptr; for(var prop in Handle.prototype) { @@ -532,7 +578,7 @@ function __embind_register_class( var clone = Object.create(Handle.prototype); clone.count = this.count; clone.ptr = this.ptr; - + clone.count.value += 1; return clone; }; @@ -543,6 +589,8 @@ function __embind_register_class( return rv; }; + // todo: test delete with upcast and downcast multiply derived pointers + // todo: then replace this.count.ptr below with this.ptr and make sure it fails Handle.prototype['delete'] = function() { if (!this.ptr) { throw new BindingError(classType.name + ' instance already deleted'); @@ -550,7 +598,7 @@ function __embind_register_class( this.count.value -= 1; if (0 === this.count.value) { - destructor(this.ptr); + destructor(this.count.ptr); } this.ptr = undefined; }; @@ -581,7 +629,7 @@ function __embind_register_class( var pointerName = name + '*'; registerType(pointerType, pointerName, { name: pointerName, - fromWireType: function(ptr) { + fromWireTypeAutoDowncast: function(ptr) { if (isPolymorphic) { var toType = ___getDynamicPointerType(ptr); var toTypeImpl = null; @@ -602,13 +650,16 @@ function __embind_register_class( return new Handle(ptr); } var toTypePointerImpl = requireRegisteredType(toTypeImpl.pointerType); - var castPtr = ___dynamicPointerCast(ptr, classType, candidate); - return toTypePointerImpl.fromWireTypeStatic(castPtr); + var handle = toTypePointerImpl.fromWireType(ptr); + handle.ptr = ___staticPointerCast(handle.ptr, classType, candidate); + // todo: can come back -1 or -2!! Throw appropriate exception + return handle; } else { - return new Handle(ptr); - } + handle = new Handle(ptr); + } + return handle; }, - fromWireTypeStatic: function(ptr) { + fromWireType: function(ptr) { return new Handle(ptr); }, toWireType: function(destructors, o) { @@ -688,8 +739,12 @@ function __embind_register_class_method( for (var i = 1; i < argCount; ++i) { args[i + 1] = argTypes[i].toWireType(destructors, arguments[i-1]); } - - var rv = argTypes[0].fromWireType(invoker.apply(null, args)); + var rv = invoker.apply(null, args); + if (argTypes[0].hasOwnProperty('fromWireTypeAutoDowncast')) { + rv = argTypes[0].fromWireTypeAutoDowncast(rv); + } else { + rv = argTypes[0].fromWireType(rv); + } runDestructors(destructors); return rv; }; @@ -698,6 +753,7 @@ function __embind_register_class_method( /*global ___staticPointerCast: false*/ function __embind_register_cast_method( classType, + isPolymorphic, methodName, returnType, invoker @@ -716,42 +772,75 @@ function __embind_register_cast_method( if (arguments.length !== 0) { throw new BindingError('emscripten binding method ' + humanName + ' called with arguments, none expected'); } - + if (isPolymorphic) { + // todo: this is all only to validate the cast -- cache the result + var runtimeType = ___getDynamicPointerType(this.ptr); + var derivation = Module.__getDerivationPath(returnType, runtimeType); // downcast is valid + var size = derivation.size(); + derivation.delete(); + if (size == 0) { + derivation = Module.__getDerivationPath(runtimeType, returnType); // upcast is valid + size = derivation.size(); + derivation.delete(); + if (size == 0) { + // todo: return zero + return returnTypeImpl.fromWireType(0); + } + } + } var args = new Array(1); args[0] = this.ptr; var rv = returnTypeImpl.fromWireType(invoker.apply(null, args)); - rv.count = this.count; // the cast value shares the reference count of the original pointer, but does not increment it + rv.count = this.count; this.count.value ++; return rv; }; } function __embind_register_pointer_cast_method( - classType, - methodName, + pointerType, returnType, + returnPointeeType, + isPolymorphic, + methodName, invoker ) { - classType = requireRegisteredType(classType, 'class'); + var pointerTypeImpl = requireRegisteredType(pointerType, 'smart pointer class'); methodName = Pointer_stringify(methodName); - var humanName = classType.name + '.' + methodName; + var humanName = pointerTypeImpl.name + '.' + methodName; - returnType = requireRegisteredType(returnType, 'method ' + humanName + ' return value'); + var returnTypeImpl = requireRegisteredType(returnType, 'method ' + humanName + ' return value'); invoker = FUNCTION_TABLE[invoker]; - classType.Handle.prototype[methodName] = function() { + pointerTypeImpl.Handle.prototype[methodName] = function() { if (!this.ptr) { throw new BindingError('cannot call emscripten binding method ' + humanName + ' on deleted object'); } if (arguments.length !== 0) { throw new BindingError('emscripten binding method ' + humanName + ' called with arguments, none expected'); } + if (isPolymorphic) { + // todo: just validating the cast -- cache the result + // todo: throw exception instead of returning zero + var runtimeType = ___getDynamicPointerType(this.ptr); + var derivation = Module.__getDerivationPath(returnPointeeType, runtimeType); // downcast is valid + var size = derivation.size(); + derivation.delete(); + if (size == 0) { + derivation = Module.__getDerivationPath(runtimeType, returnPointeeType); // upcast is valid + size = derivation.size(); + derivation.delete(); + if (size == 0) { + return 0; + } + } + } var args = new Array(2); var newPtr = _malloc(8); args[0] = newPtr; args[1] = this.smartPointer; invoker.apply(null,args); - var rv = returnType.fromWireType(newPtr); // in case ptr needs to be adjusted for multiple inheritance + var rv = returnTypeImpl.fromWireType(newPtr); return rv; }; } diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index 90dca77c..c37aed24 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -95,6 +95,7 @@ namespace emscripten { void _embind_register_smart_ptr( TYPEID pointerType, TYPEID pointeeType, + bool isPolymorphic, const char* pointerName, GenericFunction destructor, GenericFunction getPointee); @@ -124,14 +125,17 @@ namespace emscripten { void _embind_register_cast_method( TYPEID classType, + bool isPolymorphic, const char* methodName, TYPEID returnType, GenericFunction invoker); void _embind_register_pointer_cast_method( - TYPEID classType, - const char* methodName, + TYPEID pointerType, TYPEID returnType, + TYPEID returnPointeeType, + bool isPolymorphic, + const char* methodName, GenericFunction invoker); void _embind_register_class_field( @@ -269,16 +273,16 @@ namespace emscripten { extern "C" { int __getDynamicPointerType(int p); - int __dynamicPointerCast(int p, int from, int to); + int __dynamicPointerCast(int p, int to); } template<typename FromType, typename ToType> - ToType& performCast(FromType& from) { - return *dynamic_cast<ToType*>(&from); + ToType& performRawStaticCast(FromType& from) { + return *static_cast<ToType*>(&from); }; template<typename FromRawType, typename ToRawType> - std::shared_ptr<ToRawType> performPointerCast(std::shared_ptr<FromRawType> from) { + std::shared_ptr<ToRawType> performSharedStaticCast(std::shared_ptr<FromRawType> from) { return std::shared_ptr<ToRawType>(from, static_cast<ToRawType*>(from.get())); }; @@ -598,6 +602,7 @@ namespace emscripten { _embind_register_smart_ptr( TypeID<PointerType>::get(), TypeID<PointeeType>::get(), + std::is_polymorphic<PointeeType>::value, name, reinterpret_cast<GenericFunction>(&raw_destructor<PointerType>), reinterpret_cast<GenericFunction>(&get_pointee<PointerType>)); @@ -611,9 +616,11 @@ namespace emscripten { typedef typename PointerType::element_type PointeeType; _embind_register_pointer_cast_method( TypeID<PointerType>::get(), - methodName, TypeID<ReturnType>::get(), - reinterpret_cast<GenericFunction>(&performPointerCast<PointeeType,ReturnPointeeType>)); + TypeID<ReturnPointeeType>::get(), + std::is_polymorphic<PointeeType>::value, + methodName, + reinterpret_cast<GenericFunction>(&performSharedStaticCast<PointeeType,ReturnPointeeType>)); return *this; } }; @@ -756,9 +763,10 @@ namespace emscripten { _embind_register_cast_method( TypeID<ClassType>::get(), + std::is_polymorphic<ClassType>::value, methodName, TypeID<ReturnType>::get(), - reinterpret_cast<GenericFunction>(&performCast<ClassType,ReturnType>)); + reinterpret_cast<GenericFunction>(&performRawStaticCast<ClassType,ReturnType>)); return *this; } }; diff --git a/system/lib/embind/bind.cpp b/system/lib/embind/bind.cpp index de03052a..bc1e09ee 100755 --- a/system/lib/embind/bind.cpp +++ b/system/lib/embind/bind.cpp @@ -138,19 +138,19 @@ namespace emscripten { return derivationPath;
}
- void* EMSCRIPTEN_KEEPALIVE __staticPointerCast(void* p, int dv, int bs) {
+ void* EMSCRIPTEN_KEEPALIVE __staticPointerCast(void* p, int from, int to) {
std::vector<std::vector<const __cxxabiv1::__class_type_info*>> paths;
int direction = 1;
- const std::type_info* dv1 = (std::type_info*)dv;
- const std::type_info* bs1 = (std::type_info*)bs;
- const __cxxabiv1::__class_type_info* dv2 = dynamic_cast<const __cxxabiv1::__class_type_info*>(dv1);
- const __cxxabiv1::__class_type_info* bs2 = dynamic_cast<const __cxxabiv1::__class_type_info*>(bs1);
+ const std::type_info* from1 = (std::type_info*)from;
+ const std::type_info* to1 = (std::type_info*)to;
+ const __cxxabiv1::__class_type_info* from2 = dynamic_cast<const __cxxabiv1::__class_type_info*>(from1);
+ const __cxxabiv1::__class_type_info* to2 = dynamic_cast<const __cxxabiv1::__class_type_info*>(to1);
- if (dv2 && bs2) {
- __cxxabiv1::__getDerivationPaths(dv2, bs2, std::vector<const __cxxabiv1::__class_type_info*>(), paths);
+ if (from2 && to2) {
+ __cxxabiv1::__getDerivationPaths(from2, to2, std::vector<const __cxxabiv1::__class_type_info*>(), paths);
if (paths.size() == 0) {
- __cxxabiv1::__getDerivationPaths(bs2, dv2, std::vector<const __cxxabiv1::__class_type_info*>(), paths);
+ __cxxabiv1::__getDerivationPaths(to2, from2, std::vector<const __cxxabiv1::__class_type_info*>(), paths);
direction = -1;
}
}
@@ -189,10 +189,14 @@ namespace emscripten { // __dynamicPointerCast performs a C++ dynamic_cast<>() operation, but allowing run-time specification of
// the from and to pointer types.
- int EMSCRIPTEN_KEEPALIVE __dynamicPointerCast(int p, int from, int to) {
+ int EMSCRIPTEN_KEEPALIVE __dynamicPointerCast(int p, int to) {
// The final parameter is a place-holder for a hint, a feature which is not currently implemented
// in the emscripten runtime. The compiler passes a dummy value of -1, and so do we.
- return (int)__dynamic_cast((void *)p, (const std::type_info*)from, (const std::type_info *)to, -1);
+ int ret = (int)__staticPointerCast((void *)p, __getDynamicPointerType(p), to);
+ if (ret < 0) {
+ return 0;
+ }
+ return ret;
}
const char* EMSCRIPTEN_KEEPALIVE __typeName(int p) {
|