From a20c654c1fcbb877ff8bd0d305f0908695dc8c7e Mon Sep 17 00:00:00 2001 From: mey Date: Thu, 4 Oct 2012 15:57:49 -0700 Subject: [PATCH 001/258] Supporting returning std::unique_ptr from C++ to javascript. --- system/include/emscripten/bind.h | 116 +++++++++++++++---------------- system/include/emscripten/val.h | 14 ++-- system/include/emscripten/wire.h | 34 ++++++--- system/lib/embind/bind.cpp | 30 ++++---- 4 files changed, 106 insertions(+), 88 deletions(-) diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index 55fda986c..400a94728 100644 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -19,56 +19,56 @@ namespace emscripten { const char* payload) __attribute__((noreturn)); void _embind_register_void( - TypeID voidType, + TYPEID voidType, const char* name); void _embind_register_bool( - TypeID boolType, + TYPEID boolType, const char* name, bool trueValue, bool falseValue); void _embind_register_integer( - TypeID integerType, + TYPEID integerType, const char* name); void _embind_register_float( - TypeID floatType, + TYPEID floatType, const char* name); void _embind_register_cstring( - TypeID stringType, + TYPEID stringType, const char* name); void _embind_register_emval( - TypeID emvalType, + TYPEID emvalType, const char* name); void _embind_register_function( const char* name, - TypeID returnType, + TYPEID returnType, unsigned argCount, - TypeID argTypes[], + TYPEID argTypes[], GenericFunction invoker, GenericFunction function); void _embind_register_tuple( - TypeID tupleType, + TYPEID tupleType, const char* name, GenericFunction constructor, GenericFunction destructor); void _embind_register_tuple_element( - TypeID tupleType, - TypeID elementType, + TYPEID tupleType, + TYPEID elementType, GenericFunction getter, GenericFunction setter, size_t memberPointerSize, void* memberPointer); void _embind_register_tuple_element_accessor( - TypeID tupleType, - TypeID elementType, + TYPEID tupleType, + TYPEID elementType, GenericFunction staticGetter, size_t getterSize, void* getter, @@ -77,69 +77,69 @@ namespace emscripten { void* setter); void _embind_register_struct( - TypeID structType, + TYPEID structType, const char* name, GenericFunction constructor, GenericFunction destructor); void _embind_register_struct_field( - TypeID structType, + TYPEID structType, const char* name, - TypeID fieldType, + TYPEID fieldType, GenericFunction getter, GenericFunction setter, size_t memberPointerSize, void* memberPointer); void _embind_register_class( - TypeID classType, + TYPEID classType, const char* className, GenericFunction destructor); void _embind_register_class_constructor( - TypeID classType, + TYPEID classType, unsigned argCount, - TypeID argTypes[], + TYPEID argTypes[], GenericFunction constructor); void _embind_register_class_method( - TypeID classType, + TYPEID classType, const char* methodName, - TypeID returnType, + TYPEID returnType, unsigned argCount, - TypeID argTypes[], + TYPEID argTypes[], GenericFunction invoker, size_t memberFunctionSize, void* memberFunction); void _embind_register_class_field( - TypeID classType, + TYPEID classType, const char* fieldName, - TypeID fieldType, + TYPEID fieldType, GenericFunction getter, GenericFunction setter, size_t memberPointerSize, void* memberPointer); void _embind_register_class_classmethod( - TypeID classType, + TYPEID classType, const char* methodName, - TypeID returnType, + TYPEID returnType, unsigned argCount, - TypeID argTypes[], + TYPEID argTypes[], GenericFunction method); void _embind_register_enum( - TypeID enumType, + TYPEID enumType, const char* name); void _embind_register_enum_value( - TypeID enumType, + TYPEID enumType, const char* valueName, GenericEnumValue value); void _embind_register_interface( - TypeID interfaceType, + TYPEID interfaceType, const char* name, GenericFunction constructor, GenericFunction destructor); @@ -193,7 +193,7 @@ namespace emscripten { internal::ArgTypeList args; internal::_embind_register_function( name, - internal::getTypeID(), + internal::TypeID::get(), args.count, args.types, reinterpret_cast(&internal::Invoker::invoke), @@ -321,7 +321,7 @@ namespace emscripten { value_tuple(const char* name) { internal::registerStandardTypes(); internal::_embind_register_tuple( - internal::getTypeID(), + internal::TypeID::get(), name, reinterpret_cast(&internal::raw_constructor), reinterpret_cast(&internal::raw_destructor)); @@ -330,8 +330,8 @@ namespace emscripten { template value_tuple& element(ElementType ClassType::*field) { internal::_embind_register_tuple_element( - internal::getTypeID(), - internal::getTypeID(), + internal::TypeID::get(), + internal::TypeID::get(), reinterpret_cast(&internal::FieldAccess::get), reinterpret_cast(&internal::FieldAccess::set), sizeof(field), @@ -343,8 +343,8 @@ namespace emscripten { template value_tuple& element(ElementType (*getter)(const ClassType&), void (*setter)(ClassType&, ElementType)) { internal::_embind_register_tuple_element_accessor( - internal::getTypeID(), - internal::getTypeID(), + internal::TypeID::get(), + internal::TypeID::get(), reinterpret_cast(&internal::FieldAccess::template propertyGet), sizeof(getter), &getter, @@ -357,8 +357,8 @@ namespace emscripten { template value_tuple& element(ElementType (*getter)(const ClassType&), void (*setter)(ClassType&, const ElementType&)) { internal::_embind_register_tuple_element_accessor( - internal::getTypeID(), - internal::getTypeID(), + internal::TypeID::get(), + internal::TypeID::get(), reinterpret_cast(&internal::FieldAccess::template propertyGet), sizeof(getter), &getter, @@ -371,8 +371,8 @@ namespace emscripten { template value_tuple& element(ElementType (*getter)(const ClassType&), void (*setter)(ClassType&, const ElementType&&)) { internal::_embind_register_tuple_element_accessor( - internal::getTypeID(), - internal::getTypeID(), + internal::TypeID::get(), + internal::TypeID::get(), reinterpret_cast(&internal::FieldAccess::template propertyGet), sizeof(getter), &getter, @@ -385,8 +385,8 @@ namespace emscripten { template value_tuple& element(ElementType (*getter)(const ClassType&), void (*setter)(ClassType&, ElementType&)) { internal::_embind_register_tuple_element_accessor( - internal::getTypeID(), - internal::getTypeID(), + internal::TypeID::get(), + internal::TypeID::get(), reinterpret_cast(&internal::FieldAccess::template propertyGet), sizeof(getter), &getter, @@ -403,7 +403,7 @@ namespace emscripten { value_struct(const char* name) { internal::registerStandardTypes(); internal::_embind_register_struct( - internal::getTypeID(), + internal::TypeID::get(), name, reinterpret_cast(&internal::raw_constructor), reinterpret_cast(&internal::raw_destructor)); @@ -412,9 +412,9 @@ namespace emscripten { template value_struct& field(const char* fieldName, FieldType ClassType::*field) { internal::_embind_register_struct_field( - internal::getTypeID(), + internal::TypeID::get(), fieldName, - internal::getTypeID(), + internal::TypeID::get(), reinterpret_cast(&internal::FieldAccess::get), reinterpret_cast(&internal::FieldAccess::set), sizeof(field), @@ -432,7 +432,7 @@ namespace emscripten { class_(const char* name) { internal::registerStandardTypes(); internal::_embind_register_class( - internal::getTypeID(), + internal::TypeID::get(), name, reinterpret_cast(&internal::raw_destructor)); } @@ -441,7 +441,7 @@ namespace emscripten { class_& constructor() { internal::ArgTypeList args; internal::_embind_register_class_constructor( - internal::getTypeID(), + internal::TypeID::get(), args.count, args.types, reinterpret_cast(&internal::raw_constructor)); @@ -452,9 +452,9 @@ namespace emscripten { class_& method(const char* methodName, ReturnType (ClassType::*memberFunction)(Args...)) { internal::ArgTypeList args; internal::_embind_register_class_method( - internal::getTypeID(), + internal::TypeID::get(), methodName, - internal::getTypeID(), + internal::TypeID::get(), args.count, args.types, reinterpret_cast(&internal::MethodInvoker::invoke), @@ -467,9 +467,9 @@ namespace emscripten { class_& method(const char* methodName, ReturnType (ClassType::*memberFunction)(Args...) const) { internal::ArgTypeList args; internal::_embind_register_class_method( - internal::getTypeID(), + internal::TypeID::get(), methodName, - internal::getTypeID(), + internal::TypeID::get(), args.count, args.types, reinterpret_cast(&internal::ConstMethodInvoker::invoke), @@ -481,9 +481,9 @@ namespace emscripten { template class_& field(const char* fieldName, FieldType ClassType::*field) { internal::_embind_register_class_field( - internal::getTypeID(), + internal::TypeID::get(), fieldName, - internal::getTypeID(), + internal::TypeID::get(), reinterpret_cast(&internal::FieldAccess::get), reinterpret_cast(&internal::FieldAccess::set), sizeof(field), @@ -495,9 +495,9 @@ namespace emscripten { class_& classmethod(const char* methodName, ReturnType (*classMethod)(Args...)) { internal::ArgTypeList args; internal::_embind_register_class_classmethod( - internal::getTypeID(), + internal::TypeID::get(), methodName, - internal::getTypeID(), + internal::TypeID::get(), args.count, args.types, reinterpret_cast(classMethod)); @@ -510,7 +510,7 @@ namespace emscripten { public: enum_(const char* name) { _embind_register_enum( - internal::getTypeID(), + internal::TypeID::get(), name); } @@ -520,7 +520,7 @@ namespace emscripten { static_assert(sizeof(value) <= sizeof(internal::GenericEnumValue), "enum type must fit in a GenericEnumValue"); _embind_register_enum_value( - internal::getTypeID(), + internal::TypeID::get(), name, static_cast(value)); return *this; @@ -637,7 +637,7 @@ namespace emscripten { interface(const char* name) { _embind_register_interface( - internal::getTypeID(), + internal::TypeID::get(), name, reinterpret_cast(&internal::create_interface_wrapper), reinterpret_cast(&internal::raw_destructor)); diff --git a/system/include/emscripten/val.h b/system/include/emscripten/val.h index 96db93264..6dd4021c1 100644 --- a/system/include/emscripten/val.h +++ b/system/include/emscripten/val.h @@ -19,17 +19,17 @@ namespace emscripten { 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); + void _emval_as(EM_VAL value, emscripten::internal::TYPEID returnType); EM_VAL _emval_call( EM_VAL value, unsigned argCount, - internal::TypeID argTypes[] + internal::TYPEID argTypes[] /*, ... */); EM_VAL _emval_call_method( EM_VAL value, const char* methodName, unsigned argCount, - internal::TypeID argTypes[] + internal::TYPEID argTypes[] /*, ... */); } } @@ -106,7 +106,7 @@ namespace emscripten { typedef internal::EM_VAL (*TypedCall)( internal::EM_VAL, unsigned, - internal::TypeID argTypes[], + internal::TYPEID argTypes[], typename internal::BindingType::WireType...); TypedCall typedCall = reinterpret_cast(&internal::_emval_call); return val( @@ -124,7 +124,7 @@ namespace emscripten { internal::EM_VAL, const char* name, unsigned, - internal::TypeID argTypes[], + internal::TYPEID argTypes[], typename internal::BindingType::WireType...); TypedCall typedCall = reinterpret_cast(&internal::_emval_call_method); return val( @@ -142,10 +142,10 @@ namespace emscripten { typedef typename BT::WireType (*TypedAs)( internal::EM_VAL value, - emscripten::internal::TypeID returnType); + emscripten::internal::TYPEID returnType); TypedAs typedAs = reinterpret_cast(&internal::_emval_as); - typename BT::WireType wt = typedAs(handle, internal::getTypeID()); + typename BT::WireType wt = typedAs(handle, internal::TypeID::get()); internal::WireDeleter deleter(wt); return BT::fromWireType(wt); } diff --git a/system/include/emscripten/wire.h b/system/include/emscripten/wire.h index fbf0897d3..dcfd09d07 100644 --- a/system/include/emscripten/wire.h +++ b/system/include/emscripten/wire.h @@ -9,7 +9,7 @@ namespace emscripten { namespace internal { - typedef const struct _TypeID* TypeID; + typedef const struct _TYPEID* TYPEID; // This implementation is technically not legal, as it's not // required that two calls to typeid produce the same exact @@ -18,9 +18,18 @@ namespace emscripten { // an int, and store all TypeInfo we see in a map, allocating // new TypeIDs as we add new items to the map. template - inline TypeID getTypeID() { - return reinterpret_cast(&typeid(T)); - } + struct TypeID { + static TYPEID get() { + return reinterpret_cast(&typeid(T)); + } + }; + + template + struct TypeID> { + static TYPEID get() { + return TypeID::get(); + } + }; // count<> @@ -44,14 +53,14 @@ namespace emscripten { template<> struct ArgTypes<> { - static void fill(TypeID* argTypes) { + static void fill(TYPEID* argTypes) { } }; template struct ArgTypes { - static void fill(TypeID* argTypes) { - *argTypes = getTypeID(); + static void fill(TYPEID* argTypes) { + *argTypes = TypeID::get(); return ArgTypes::fill(argTypes + 1); } }; @@ -66,7 +75,7 @@ namespace emscripten { } unsigned count; - TypeID types[args_count]; + TYPEID types[args_count]; }; // BindingType @@ -191,6 +200,15 @@ namespace emscripten { } }; + template + struct GenericBindingType> { + typedef typename BindingType::WireType WireType; + + static WireType toWireType(std::unique_ptr p) { + return BindingType::toWireType(*p); + } + }; + template struct WireDeleter { typedef typename BindingType::WireType WireType; diff --git a/system/lib/embind/bind.cpp b/system/lib/embind/bind.cpp index b63a86aae..befce08b1 100755 --- a/system/lib/embind/bind.cpp +++ b/system/lib/embind/bind.cpp @@ -9,25 +9,25 @@ namespace emscripten { if (first) { first = false; - _embind_register_void(getTypeID(), "void"); + _embind_register_void(TypeID::get(), "void"); - _embind_register_bool(getTypeID(), "bool", true, false); + _embind_register_bool(TypeID::get(), "bool", true, false); - _embind_register_integer(getTypeID(), "char"); - _embind_register_integer(getTypeID(), "signed char"); - _embind_register_integer(getTypeID(), "unsigned char"); - _embind_register_integer(getTypeID(), "short"); - _embind_register_integer(getTypeID(), "unsigned short"); - _embind_register_integer(getTypeID(), "int"); - _embind_register_integer(getTypeID(), "unsigned int"); - _embind_register_integer(getTypeID(), "long"); - _embind_register_integer(getTypeID(), "unsigned long"); + _embind_register_integer(TypeID::get(), "char"); + _embind_register_integer(TypeID::get(), "signed char"); + _embind_register_integer(TypeID::get(), "unsigned char"); + _embind_register_integer(TypeID::get(), "short"); + _embind_register_integer(TypeID::get(), "unsigned short"); + _embind_register_integer(TypeID::get(), "int"); + _embind_register_integer(TypeID::get(), "unsigned int"); + _embind_register_integer(TypeID::get(), "long"); + _embind_register_integer(TypeID::get(), "unsigned long"); - _embind_register_float(getTypeID(), "float"); - _embind_register_float(getTypeID(), "double"); + _embind_register_float(TypeID::get(), "float"); + _embind_register_float(TypeID::get(), "double"); - _embind_register_cstring(getTypeID(), "std::string"); - _embind_register_emval(getTypeID(), "emscripten::val"); + _embind_register_cstring(TypeID::get(), "std::string"); + _embind_register_emval(TypeID::get(), "emscripten::val"); } } } From db4baa7a70f37b6e4754dacf645d8674e52dad43 Mon Sep 17 00:00:00 2001 From: mey Date: Tue, 9 Oct 2012 17:54:38 -0700 Subject: [PATCH 002/258] Exposing std::shared_ptr to javascript. --- src/embind/embind.js | 82 +++++++++++++++++++++++++++++++- system/include/emscripten/bind.h | 26 ++++++++++ 2 files changed, 107 insertions(+), 1 deletion(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index d40d6ca2a..d9358afd9 100644 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -354,6 +354,86 @@ function __embind_register_struct_field( }; } +function __embind_register_shared_ptr( + ptrType, + classType, + name, + destructor, + internalPtrGetter +) { + name = Pointer_stringify(name); + classType = requireRegisteredType(classType, 'class'); + destructor = FUNCTION_TABLE[destructor]; + internalPtrGetter = FUNCTION_TABLE[internalPtrGetter]; + + var Handle = createNamedFunction(name, function(ptr) { + this.count = {value: 1}; + this.ptr = ptr; + + var args = new Array(1); + args[0] = ptr; + this.internalReference = classType.fromWireType(internalPtrGetter.apply(null, args)); + }); + + for(var prop in classType.Handle.prototype){ + if(prop === 'clone' || prop === 'move' === prop === 'delete'){ + continue; + } + + function createDuplicatedFunc(prop) { + return function() { + console.log(arguments); + return classType.Handle.prototype[prop].apply(this.internalReference, arguments); + } + } + + Handle.prototype[prop] = createDuplicatedFunc(prop); + } + + 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) { + console.log(destructor); + destructor(this.ptr); + } + this.ptr = undefined; + } + + typeRegistry[ptrType] = { + name: name, + Handle: Handle, + fromWireType: function(ptr) { + return new Handle(ptr); + }, + toWireType: function(destructors, o) { + return o.ptr; + } + }; +} + function __embind_register_class( classType, name, @@ -479,7 +559,7 @@ function __embind_register_class_method( 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; diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index 400a94728..2b84878e3 100644 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -91,6 +91,13 @@ namespace emscripten { size_t memberPointerSize, void* memberPointer); + void _embind_register_shared_ptr( + TYPEID ptrType, + TYPEID classType, + const char* ptrName, + GenericFunction destructor, + GenericFunction invoker); + void _embind_register_class( TYPEID classType, const char* className, @@ -215,6 +222,11 @@ namespace emscripten { delete ptr; } + template + ClassType* getSharedInternalPtr(std::shared_ptr* ptr) { + return ptr->get(); + } + template struct MethodInvoker { typedef ReturnType (ClassType::*MemberPointer)(Args...); @@ -424,6 +436,20 @@ namespace emscripten { } }; + template + class shared_ptr_ { + public: + shared_ptr_(const char* name) { + internal::registerStandardTypes(); + internal::_embind_register_shared_ptr( + internal::TypeID>::get(), + internal::TypeID::get(), + name, + reinterpret_cast(&internal::raw_destructor>), + reinterpret_cast(&internal::getSharedInternalPtr)); + } + }; + // TODO: support class definitions without constructors. // TODO: support external class constructors template From 60bae3faed65429634f1104531b08f938d20b7a3 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Wed, 10 Oct 2012 01:46:12 -0700 Subject: [PATCH 003/258] Some simplifications and optimizations to smart pointer support --- src/embind/embind.js | 92 ++++++++++++++------------------ system/include/emscripten/bind.h | 46 ++++++++-------- 2 files changed, 62 insertions(+), 76 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index d9358afd9..790c09700 100644 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -354,49 +354,36 @@ function __embind_register_struct_field( }; } -function __embind_register_shared_ptr( - ptrType, - classType, - name, - destructor, - internalPtrGetter -) { - name = Pointer_stringify(name); - classType = requireRegisteredType(classType, 'class'); - destructor = FUNCTION_TABLE[destructor]; - internalPtrGetter = FUNCTION_TABLE[internalPtrGetter]; - - var Handle = createNamedFunction(name, function(ptr) { - this.count = {value: 1}; - this.ptr = ptr; - - var args = new Array(1); - args[0] = ptr; - this.internalReference = classType.fromWireType(internalPtrGetter.apply(null, args)); - }); +function __embind_register_smart_ptr( + pointerType, + pointeeType, + name, + destructor, + getPointee +) { + name = Pointer_stringify(name); + pointeeType = requireRegisteredType(pointeeType, 'class'); + destructor = FUNCTION_TABLE[destructor]; + getPointee = FUNCTION_TABLE[getPointee]; - for(var prop in classType.Handle.prototype){ - if(prop === 'clone' || prop === 'move' === prop === 'delete'){ - continue; - } - - function createDuplicatedFunc(prop) { - return function() { - console.log(arguments); - return classType.Handle.prototype[prop].apply(this.internalReference, arguments); - } - } - - Handle.prototype[prop] = createDuplicatedFunc(prop); - } - - Handle.prototype.clone = function() { + var Handle = createNamedFunction(name, function(ptr) { + this.count = {value: 1}; + this.smartPointer = ptr; + this.ptr = getPointee(ptr); + }); + + // TODO: test for SmartPtr.prototype.constructor property? + // We likely want it distinct from pointeeType.prototype.constructor + Handle.prototype = Object.create(pointeeType.Handle.prototype); + + Handle.prototype.clone = function() { if (!this.ptr) { - throw new BindingError(classType.name + ' instance already deleted'); + throw new BindingError(pointeeType.name + ' instance already deleted'); } var clone = Object.create(Handle.prototype); clone.count = this.count; + clone.smartPointer = this.smartPointer; clone.ptr = this.ptr; clone.count.value += 1; @@ -408,30 +395,29 @@ function __embind_register_shared_ptr( this.delete(); return rv; }; - - Handle.prototype['delete'] = function() { - if (!this.ptr) { - throw new BindingError(classType.name + ' instance already deleted'); + + Handle.prototype['delete'] = function() { + if (!this.ptr) { + throw new BindingError(pointeeType.name + ' instance already deleted'); } - + this.count.value -= 1; if (0 === this.count.value) { - console.log(destructor); - destructor(this.ptr); + destructor(this.smartPointer); } + this.smartPointer = undefined; this.ptr = undefined; - } - - typeRegistry[ptrType] = { - name: name, - Handle: Handle, - fromWireType: function(ptr) { - return new Handle(ptr); + } + + typeRegistry[pointerType] = { + name: name, + fromWireType: function(ptr) { + return new Handle(ptr); }, toWireType: function(destructors, o) { - return o.ptr; + return o.ptr; } - }; + }; } function __embind_register_class( diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index 2b84878e3..6902eb86a 100644 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -91,12 +91,12 @@ namespace emscripten { size_t memberPointerSize, void* memberPointer); - void _embind_register_shared_ptr( - TYPEID ptrType, - TYPEID classType, - const char* ptrName, - GenericFunction destructor, - GenericFunction invoker); + void _embind_register_smart_ptr( + TYPEID pointerType, + TYPEID pointeeType, + const char* pointerName, + GenericFunction destructor, + GenericFunction getPointee); void _embind_register_class( TYPEID classType, @@ -157,7 +157,7 @@ namespace emscripten { class BindingsDefinition { public: template - BindingsDefinition(Function fn) { + BindingsDefinition(Function fn) { fn(); } }; @@ -222,9 +222,10 @@ namespace emscripten { delete ptr; } - template - ClassType* getSharedInternalPtr(std::shared_ptr* ptr) { - return ptr->get(); + template + typename PointerType::element_type* get_pointee(PointerType* ptr) { + // TODO: replace with general pointer traits implementation + return ptr->get(); } template @@ -436,19 +437,18 @@ namespace emscripten { } }; - template - class shared_ptr_ { - public: - shared_ptr_(const char* name) { - internal::registerStandardTypes(); - internal::_embind_register_shared_ptr( - internal::TypeID>::get(), - internal::TypeID::get(), - name, - reinterpret_cast(&internal::raw_destructor>), - reinterpret_cast(&internal::getSharedInternalPtr)); - } - }; + template + inline void register_smart_ptr(const char* name) { + typedef typename PointerType::element_type PointeeType; + + internal::registerStandardTypes(); + internal::_embind_register_smart_ptr( + internal::TypeID::get(), + internal::TypeID::get(), + name, + reinterpret_cast(&internal::raw_destructor), + reinterpret_cast(&internal::get_pointee)); + } // TODO: support class definitions without constructors. // TODO: support external class constructors From 681ea9fc51ee9908008f0913356f673018c11c2e Mon Sep 17 00:00:00 2001 From: mey Date: Wed, 10 Oct 2012 15:21:58 -0700 Subject: [PATCH 004/258] Fixing some tabs and removing some duplication in smart ptr embind. --- src/embind/embind.js | 20 ++++++++++++++------ system/include/emscripten/wire.h | 18 +++++++++--------- 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 790c09700..56f3520a5 100644 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -389,12 +389,6 @@ function __embind_register_smart_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) { @@ -433,6 +427,20 @@ function __embind_register_class( this.ptr = ptr; }); + Handle.prototype.clone = function() { + if (!this.ptr) { + throw new BindingError(pointeeType.name + ' instance already deleted'); + } + + var clone = Object.create(Handle.prototype); + clone.count = this.count; + clone.smartPointer = this.smartPointer; + clone.ptr = this.ptr; + + clone.count.value += 1; + return clone; + }; + Handle.prototype.clone = function() { if (!this.ptr) { throw new BindingError(classType.name + ' instance already deleted'); diff --git a/system/include/emscripten/wire.h b/system/include/emscripten/wire.h index dcfd09d07..be8e4f6d7 100644 --- a/system/include/emscripten/wire.h +++ b/system/include/emscripten/wire.h @@ -19,17 +19,17 @@ namespace emscripten { // new TypeIDs as we add new items to the map. template struct TypeID { - static TYPEID get() { - return reinterpret_cast(&typeid(T)); - } + static TYPEID get() { + return reinterpret_cast(&typeid(T)); + } }; template - struct TypeID> { - static TYPEID get() { - return TypeID::get(); - } - }; + struct TypeID> { + static TYPEID get() { + return TypeID::get(); + } + }; // count<> @@ -205,7 +205,7 @@ namespace emscripten { typedef typename BindingType::WireType WireType; static WireType toWireType(std::unique_ptr p) { - return BindingType::toWireType(*p); + return BindingType::toWireType(*p); } }; From d0bf1b0aea625931255c31641ba53ad6d4cee7c3 Mon Sep 17 00:00:00 2001 From: Todd Lee Date: Tue, 23 Oct 2012 12:23:03 -0700 Subject: [PATCH 005/258] Handle shared_ptr correctly.(keep underlying pointer point to the same address) There was some global variable dependency. These bleed thru tests and affected test result. Adding a way to reset this state. --- src/embind/embind.js | 2 +- src/embind/emval.js | 5 +++++ system/include/emscripten/bind.h | 4 ++-- system/include/emscripten/wire.h | 26 +++++++++++++++++++++++++- 4 files changed, 33 insertions(+), 4 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 56f3520a5..f63a8eff3 100644 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -409,7 +409,7 @@ function __embind_register_smart_ptr( return new Handle(ptr); }, toWireType: function(destructors, o) { - return o.ptr; + return o.smartPointer; } }; } diff --git a/src/embind/emval.js b/src/embind/emval.js index 9574ab379..26ceaf464 100644 --- a/src/embind/emval.js +++ b/src/embind/emval.js @@ -13,12 +13,17 @@ Module.count_emval_handles = function() { return _emval_handle_array.length; }; +Module.reset_emval_handles = function() { + _emval_handle_array = []; + _emval_free_list = []; +} // 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; } diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index 6902eb86a..b8858284d 100644 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -223,9 +223,9 @@ namespace emscripten { } template - typename PointerType::element_type* get_pointee(PointerType* ptr) { + typename PointerType::element_type* get_pointee(PointerType ptr) { // TODO: replace with general pointer traits implementation - return ptr->get(); + return ptr.get(); } template diff --git a/system/include/emscripten/wire.h b/system/include/emscripten/wire.h index be8e4f6d7..ab4caa976 100644 --- a/system/include/emscripten/wire.h +++ b/system/include/emscripten/wire.h @@ -30,6 +30,13 @@ namespace emscripten { return TypeID::get(); } }; + + template + struct TypeID> { + static TYPEID get() { + return TypeID::get(); + } + }; // count<> @@ -209,6 +216,24 @@ namespace emscripten { } }; + template + struct GenericBindingType> { + typedef typename std::shared_ptr ActualT; + typedef ActualT* WireType; + + static WireType toWireType(std::shared_ptr p) { + return new std::shared_ptr(p); + } + + static std::shared_ptr fromWireType(WireType p) { + return *p; + } + + static void destroy(WireType p) { + delete p; + } + }; + template struct WireDeleter { typedef typename BindingType::WireType WireType; @@ -236,6 +261,5 @@ namespace emscripten { auto toWireType(const T& v) -> typename BindingType::WireType { return BindingType::toWireType(v); } - } } From f91f1ddf374b4f881cd5cf2ba35263cfbe965224 Mon Sep 17 00:00:00 2001 From: Todd Lee Date: Fri, 26 Oct 2012 15:18:35 -0700 Subject: [PATCH 006/258] removed duplicate code / unnecessary code --- src/embind/embind.js | 19 +++---------------- system/include/emscripten/wire.h | 7 ------- 2 files changed, 3 insertions(+), 23 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index f63a8eff3..1f5da572e 100644 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -402,7 +402,7 @@ function __embind_register_smart_ptr( this.smartPointer = undefined; this.ptr = undefined; } - + typeRegistry[pointerType] = { name: name, fromWireType: function(ptr) { @@ -427,20 +427,6 @@ function __embind_register_class( this.ptr = ptr; }); - Handle.prototype.clone = function() { - if (!this.ptr) { - throw new BindingError(pointeeType.name + ' instance already deleted'); - } - - var clone = Object.create(Handle.prototype); - clone.count = this.count; - clone.smartPointer = this.smartPointer; - clone.ptr = this.ptr; - - clone.count.value += 1; - return clone; - }; - Handle.prototype.clone = function() { if (!this.ptr) { throw new BindingError(classType.name + ' instance already deleted'); @@ -533,6 +519,7 @@ function __embind_register_class_method( 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]; @@ -553,7 +540,7 @@ function __embind_register_class_method( 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; diff --git a/system/include/emscripten/wire.h b/system/include/emscripten/wire.h index ab4caa976..b618be83c 100644 --- a/system/include/emscripten/wire.h +++ b/system/include/emscripten/wire.h @@ -31,13 +31,6 @@ namespace emscripten { } }; - template - struct TypeID> { - static TYPEID get() { - return TypeID::get(); - } - }; - // count<> template From 1c0c7be7aaf6f231bd29d8b9db68506be6a0a5b1 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Tue, 30 Oct 2012 14:01:52 -0700 Subject: [PATCH 007/258] Some minor generated code size reductions. --- src/embind/emval.js | 46 +++++++++++++++++++++----------- system/include/emscripten/bind.h | 2 +- system/include/emscripten/val.h | 24 +++++++++++++++++ 3 files changed, 56 insertions(+), 16 deletions(-) diff --git a/src/embind/emval.js b/src/embind/emval.js index 26ceaf464..b2adb89a3 100644 --- a/src/embind/emval.js +++ b/src/embind/emval.js @@ -16,7 +16,8 @@ Module.count_emval_handles = function() { Module.reset_emval_handles = function() { _emval_handle_array = []; _emval_free_list = []; -} +}; + // Private C++ API function __emval_register(value) { @@ -86,31 +87,46 @@ function __emval_as(handle, returnType) { return returnType.toWireType(destructors, _emval_handle_array[handle].value); } -function __emval_call(handle, argCount, argTypes) { - var args = Array.prototype.slice.call(arguments, 3); - var fn = _emval_handle_array[handle].value; +function parseParameters(argCount, argTypes, argWireTypes) { var a = new Array(argCount); for (var i = 0; i < argCount; ++i) { var argType = requireRegisteredType( HEAP32[(argTypes >> 2) + i], "parameter " + i); - a[i] = argType.fromWireType(args[i]); + a[i] = argType.fromWireType(argWireTypes[i]); } - var rv = fn.apply(undefined, a); + return a; +} + +function __emval_call(handle, argCount, argTypes) { + var fn = _emval_handle_array[handle].value; + var args = parseParameters( + argCount, + argTypes, + Array.prototype.slice.call(arguments, 3)); + var rv = fn.apply(undefined, args); return __emval_register(rv); } function __emval_call_method(handle, name, argCount, argTypes) { name = Pointer_stringify(name); - var args = Array.prototype.slice.call(arguments, 4); + + var args = parseParameters( + argCount, + argTypes, + Array.prototype.slice.call(arguments, 4)); var obj = _emval_handle_array[handle].value; - var a = new Array(argCount); - for (var i = 0; i < argCount; ++i) { - var argType = requireRegisteredType( - HEAP32[(argTypes >> 2) + i], - "parameter " + i); - a[i] = argType.fromWireType(args[i]); - } - var rv = obj[name].apply(obj, a); + var rv = obj[name].apply(obj, args); return __emval_register(rv); } + +function __emval_call_void_method(handle, name, argCount, argTypes) { + name = Pointer_stringify(name); + + var args = parseParameters( + argCount, + argTypes, + Array.prototype.slice.call(arguments, 4)); + var obj = _emval_handle_array[handle].value; + obj[name].apply(obj, args); +} diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index b8858284d..2de14fab7 100644 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -632,7 +632,7 @@ namespace emscripten { template struct Caller { static void call(val& v, const char* name, Args... args) { - v.call(name, args...); + v.call_void(name, args...); } }; diff --git a/system/include/emscripten/val.h b/system/include/emscripten/val.h index 6dd4021c1..ada896388 100644 --- a/system/include/emscripten/val.h +++ b/system/include/emscripten/val.h @@ -31,6 +31,12 @@ namespace emscripten { unsigned argCount, internal::TYPEID argTypes[] /*, ... */); + void _emval_call_void_method( + EM_VAL value, + const char* methodName, + unsigned argCount, + internal::TYPEID argTypes[] + /*, ...*/); } } @@ -136,6 +142,24 @@ namespace emscripten { internal::toWireType(args)...)); } + template + void call_void(const char* name, Args... args) { + internal::ArgTypeList argList; + typedef void (*TypedCall)( + internal::EM_VAL, + const char* name, + unsigned, + internal::TYPEID argTypes[], + typename internal::BindingType::WireType...); + TypedCall typedCall = reinterpret_cast(&internal::_emval_call_void_method); + return typedCall( + handle, + name, + argList.count, + argList.types, + internal::toWireType(args)...); + } + template T as() const { typedef internal::BindingType BT; From df6ba03eed0f08abe51fb1a5afd3e9d2c94a878f Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Tue, 30 Oct 2012 15:18:22 -0700 Subject: [PATCH 008/258] Fix several ownership/lifetime bugs in argument wire types --- src/embind/embind.js | 4 ++-- src/embind/emval.js | 5 ----- system/include/emscripten/bind.h | 2 +- system/include/emscripten/val.h | 2 +- system/include/emscripten/wire.h | 34 ++++++++------------------------ 5 files changed, 12 insertions(+), 35 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 1f5da572e..3e916ed4b 100644 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -368,8 +368,8 @@ function __embind_register_smart_ptr( var Handle = createNamedFunction(name, function(ptr) { this.count = {value: 1}; - this.smartPointer = ptr; - this.ptr = getPointee(ptr); + this.smartPointer = ptr; // std::shared_ptr* + this.ptr = getPointee(ptr); // T* }); // TODO: test for SmartPtr.prototype.constructor property? diff --git a/src/embind/emval.js b/src/embind/emval.js index b2adb89a3..6552b0a73 100644 --- a/src/embind/emval.js +++ b/src/embind/emval.js @@ -13,11 +13,6 @@ Module.count_emval_handles = function() { return _emval_handle_array.length; }; -Module.reset_emval_handles = function() { - _emval_handle_array = []; - _emval_free_list = []; -}; - // Private C++ API function __emval_register(value) { diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index 2de14fab7..69d1f08c3 100644 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -223,7 +223,7 @@ namespace emscripten { } template - typename PointerType::element_type* get_pointee(PointerType ptr) { + typename PointerType::element_type* get_pointee(const PointerType& ptr) { // TODO: replace with general pointer traits implementation return ptr.get(); } diff --git a/system/include/emscripten/val.h b/system/include/emscripten/val.h index ada896388..8369caf76 100644 --- a/system/include/emscripten/val.h +++ b/system/include/emscripten/val.h @@ -194,7 +194,7 @@ namespace emscripten { return v.handle; } static val fromWireType(WireType v) { - return val(v); + return val::take_ownership(v); } }; } diff --git a/system/include/emscripten/wire.h b/system/include/emscripten/wire.h index b618be83c..ceabd2805 100644 --- a/system/include/emscripten/wire.h +++ b/system/include/emscripten/wire.h @@ -130,7 +130,7 @@ namespace emscripten { template<> struct BindingType { typedef char* WireType; - static WireType toWireType(std::string v) { + static WireType toWireType(const std::string& v) { return strdup(v.c_str()); } static std::string fromWireType(char* v) { @@ -138,14 +138,14 @@ namespace emscripten { } }; - template<> - struct BindingType { - typedef char* WireType; - static WireType toWireType(std::string v) { - return strdup(v.c_str()); + template + struct BindingType { + typedef typename BindingType::WireType WireType; + static WireType toWireType(const T& v) { + return BindingType::toWireType(v); } - static std::string fromWireType(char* v) { - return std::string(v); + static T fromWireType(WireType wt) { + return BindingType::fromWireType(wt); } }; @@ -209,24 +209,6 @@ namespace emscripten { } }; - template - struct GenericBindingType> { - typedef typename std::shared_ptr ActualT; - typedef ActualT* WireType; - - static WireType toWireType(std::shared_ptr p) { - return new std::shared_ptr(p); - } - - static std::shared_ptr fromWireType(WireType p) { - return *p; - } - - static void destroy(WireType p) { - delete p; - } - }; - template struct WireDeleter { typedef typename BindingType::WireType WireType; From 6e2ce5357fb9938974201d8ab6e6d724d66987f5 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Tue, 30 Oct 2012 16:05:26 -0700 Subject: [PATCH 009/258] Add the ability to extend the prototype of native classes from JavaScript. --- src/embind/embind.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 3e916ed4b..c4e7d843c 100644 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -462,7 +462,7 @@ function __embind_register_class( var body = constructor.body; body.apply(this, arguments); }); - constructor.prototype = Object.create(Handle.prototype); + constructor.prototype = Handle.prototype; typeRegistry[classType] = { name: name, From e71c512ef802afce1f78cb589e4decd0dd695711 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Tue, 30 Oct 2012 16:41:25 -0700 Subject: [PATCH 010/258] Add validation that prevents registering the same type multiple times (either by name or type ID) --- src/embind/embind.js | 78 +++++++++++++++++++++++--------------------- 1 file changed, 40 insertions(+), 38 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index c4e7d843c..5c3238314 100644 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -4,6 +4,13 @@ /*global Pointer_stringify, writeStringToMemory*/ /*global __emval_register, _emval_handle_array, __emval_decref*/ +function exposePublicSymbol(name, value) { + if (Module.hasOwnProperty(name)) { + throw new BindingError("Cannot register public name '" + name + "' twice"); + } + Module[name] = value; +} + function createNamedFunction(name, body) { /*jshint evil:true*/ return new Function( @@ -25,30 +32,29 @@ function _embind_repr(v) { var typeRegistry = {}; -function validateType(type, name) { +function registerType(type, name, info) { 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'); + if (typeRegistry.hasOwnProperty(type)) { + throw new BindingError("Cannot register type '" + name + "' twice"); } + typeRegistry[type] = info; } function __embind_register_void(voidType, name) { name = Pointer_stringify(name); - validateType(voidType, name); - typeRegistry[voidType] = { + registerType(voidType, name, { name: name, fromWireType: function() { return undefined; } - }; + }); } function __embind_register_bool(boolType, name, trueValue, falseValue) { name = Pointer_stringify(name); - validateType(boolType, name); - typeRegistry[boolType] = { + registerType(boolType, name, { name: name, toWireType: function(destructors, o) { return o ? trueValue : falseValue; @@ -56,13 +62,12 @@ function __embind_register_bool(boolType, name, trueValue, falseValue) { fromWireType: function(wt) { return wt === trueValue; }, - }; + }); } function __embind_register_integer(primitiveType, name) { name = Pointer_stringify(name); - validateType(primitiveType, name); - typeRegistry[primitiveType] = { + registerType(primitiveType, name, { name: name, toWireType: function(destructors, value) { if (typeof value !== "number") { @@ -73,13 +78,12 @@ function __embind_register_integer(primitiveType, name) { fromWireType: function(value) { return value; } - }; + }); } function __embind_register_float(primitiveType, name) { name = Pointer_stringify(name); - validateType(primitiveType, name); - typeRegistry[primitiveType] = { + registerType(primitiveType, name, { name: name, toWireType: function(destructors, value) { if (typeof value !== "number") { @@ -90,13 +94,12 @@ function __embind_register_float(primitiveType, name) { fromWireType: function(value) { return value; } - }; + }); } function __embind_register_cstring(stringType, name) { name = Pointer_stringify(name); - validateType(stringType, name); - typeRegistry[stringType] = { + registerType(stringType, name, { name: name, toWireType: function(destructors, value) { var ptr = _malloc(value.length + 1); @@ -110,13 +113,12 @@ function __embind_register_cstring(stringType, name) { _free(value); return rv; } - }; + }); } function __embind_register_emval(emvalType, name) { name = Pointer_stringify(name); - validateType(emvalType, name); - typeRegistry[emvalType] = { + registerType(emvalType, name, { name: name, toWireType: function(destructors, value) { return __emval_register(value); @@ -126,7 +128,7 @@ function __embind_register_emval(emvalType, name) { __emval_decref(handle); return rv; } - }; + }); } var BindingError = Error; @@ -169,7 +171,7 @@ function __embind_register_function(name, returnType, argCount, argTypes, invoke invoker = FUNCTION_TABLE[invoker]; argTypes = requireArgumentTypes(argCount, argTypes, name); - Module[name] = function() { + exposePublicSymbol(name, function() { if (arguments.length !== argCount) { throw new BindingError('emscripten binding function ' + name + ' called with ' + arguments.length + ' arguments, expected ' + argCount); } @@ -182,7 +184,7 @@ function __embind_register_function(name, returnType, argCount, argTypes, invoke var rv = returnType.fromWireType(invoker.apply(null, args)); runDestructors(destructors); return rv; - }; + }); } function __embind_register_tuple(tupleType, name, constructor, destructor) { @@ -192,7 +194,7 @@ function __embind_register_tuple(tupleType, name, constructor, destructor) { var elements = []; - typeRegistry[tupleType] = { + registerType(tupleType, name, { name: name, elements: elements, fromWireType: function(ptr) { @@ -217,7 +219,7 @@ function __embind_register_tuple(tupleType, name, constructor, destructor) { destructors.push(ptr); return ptr; } - }; + }); } function copyMemberPointer(memberPointer, memberPointerSize) { @@ -297,7 +299,7 @@ function __embind_register_struct( constructor = FUNCTION_TABLE[constructor]; destructor = FUNCTION_TABLE[destructor]; - typeRegistry[structType] = { + registerType(structType, name, { fields: {}, fromWireType: function(ptr) { var fields = this.fields; @@ -323,7 +325,7 @@ function __embind_register_struct( destructors.push(ptr); return ptr; } - }; + }); } function __embind_register_struct_field( @@ -401,9 +403,9 @@ function __embind_register_smart_ptr( } this.smartPointer = undefined; this.ptr = undefined; - } + }; - typeRegistry[pointerType] = { + registerType(pointerType, name, { name: name, fromWireType: function(ptr) { return new Handle(ptr); @@ -411,7 +413,7 @@ function __embind_register_smart_ptr( toWireType: function(destructors, o) { return o.smartPointer; } - }; + }); } function __embind_register_class( @@ -464,7 +466,7 @@ function __embind_register_class( }); constructor.prototype = Handle.prototype; - typeRegistry[classType] = { + registerType(classType, name, { name: name, constructor: constructor, Handle: Handle, @@ -474,9 +476,9 @@ function __embind_register_class( toWireType: function(destructors, o) { return o.ptr; } - }; + }); - Module[name] = constructor; + exposePublicSymbol(name, constructor); } function __embind_register_class_constructor( @@ -625,7 +627,7 @@ function __embind_register_enum( } Enum.values = {}; - typeRegistry[enumType] = { + registerType(enumType, name, { name: name, constructor: Enum, toWireType: function(destructors, c) { @@ -634,9 +636,9 @@ function __embind_register_enum( fromWireType: function(c) { return Enum.values[c]; }, - }; + }); - Module[name] = Enum; + exposePublicSymbol(name, Enum); } function __embind_register_enum_value( @@ -667,7 +669,7 @@ function __embind_register_interface( constructor = FUNCTION_TABLE[constructor]; destructor = FUNCTION_TABLE[destructor]; - typeRegistry[interfaceType] = { + registerType(interfaceType, name, { name: name, toWireType: function(destructors, o) { var handle = __emval_register(o); @@ -676,6 +678,6 @@ function __embind_register_interface( destructors.push(ptr); return ptr; }, - }; + }); } From db7397e72edefdea48076e0087a08085658366f1 Mon Sep 17 00:00:00 2001 From: Todd Lee Date: Thu, 1 Nov 2012 14:17:13 -0700 Subject: [PATCH 011/258] There can be an empty shared ptr. It is totally valid. --- src/embind/embind.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 5c3238314..e9c92f5e4 100644 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -393,7 +393,7 @@ function __embind_register_smart_ptr( }; Handle.prototype['delete'] = function() { - if (!this.ptr) { + if (!this.ptr && !this.smartPointer) { throw new BindingError(pointeeType.name + ' instance already deleted'); } From 3703d2b5cdf66f7ef0ce6f56a54856102e4a1cfd Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Thu, 1 Nov 2012 18:13:48 -0700 Subject: [PATCH 012/258] Checkpoint work towards exposing call policies for raw pointers. --- src/embind/embind.js | 11 +++ system/include/emscripten/bind.h | 129 +++++++++++++++++++++++-------- system/include/emscripten/wire.h | 117 ++++++++++++++++++++++------ 3 files changed, 199 insertions(+), 58 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index e9c92f5e4..e08688380 100644 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -416,6 +416,17 @@ function __embind_register_smart_ptr( }); } +function __embind_register_raw_pointer( + pointeeType, + pointerType +) { + pointeeType = requireRegisteredType(pointeeType, 'class'); + var name = pointeeType.name + '*'; + registerType(pointerType, name, { + name: name, + }); +} + function __embind_register_class( classType, name, diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index 69d1f08c3..f2e70b31d 100644 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -165,6 +165,27 @@ namespace emscripten { } namespace emscripten { + //////////////////////////////////////////////////////////////////////////////// + // POLICIES + //////////////////////////////////////////////////////////////////////////////// + + template + struct arg { + static constexpr int index = Index; + }; + + template + struct allow_raw_pointer { + template + struct Transform { + typedef typename std::conditional< + Index == Slot::index, + internal::AllowedRawPointer::type>, + InputType + >::type type; + }; + }; + namespace internal { template struct Invoker { @@ -193,6 +214,10 @@ namespace emscripten { }; } + //////////////////////////////////////////////////////////////////////////////// + // FUNCTIONS + //////////////////////////////////////////////////////////////////////////////// + template void function(const char* name, ReturnType (fn)(Args...)) { internal::registerStandardTypes(); @@ -328,6 +353,10 @@ namespace emscripten { }; } + //////////////////////////////////////////////////////////////////////////////// + // VALUE TUPLES + //////////////////////////////////////////////////////////////////////////////// + template class value_tuple { public: @@ -410,6 +439,10 @@ namespace emscripten { } }; + //////////////////////////////////////////////////////////////////////////////// + // VALUE STRUCTS + //////////////////////////////////////////////////////////////////////////////// + template class value_struct { public: @@ -437,6 +470,10 @@ namespace emscripten { } }; + //////////////////////////////////////////////////////////////////////////////// + // SMART POINTERS + //////////////////////////////////////////////////////////////////////////////// + template inline void register_smart_ptr(const char* name) { typedef typename PointerType::element_type PointeeType; @@ -450,55 +487,67 @@ namespace emscripten { reinterpret_cast(&internal::get_pointee)); } + //////////////////////////////////////////////////////////////////////////////// + // CLASSES + //////////////////////////////////////////////////////////////////////////////// + // TODO: support class definitions without constructors. // TODO: support external class constructors template class class_ { public: class_(const char* name) { - internal::registerStandardTypes(); - internal::_embind_register_class( - internal::TypeID::get(), + using namespace internal; + + registerStandardTypes(); + _embind_register_class( + TypeID::get(), name, - reinterpret_cast(&internal::raw_destructor)); + reinterpret_cast(&raw_destructor)); } template class_& constructor() { - internal::ArgTypeList args; - internal::_embind_register_class_constructor( - internal::TypeID::get(), + using namespace internal; + + ArgTypeList args; + _embind_register_class_constructor( + TypeID::get(), args.count, args.types, - reinterpret_cast(&internal::raw_constructor)); + reinterpret_cast(&raw_constructor)); return *this; } - template - class_& method(const char* methodName, ReturnType (ClassType::*memberFunction)(Args...)) { - internal::ArgTypeList args; - internal::_embind_register_class_method( - internal::TypeID::get(), + template + class_& method(const char* methodName, ReturnType (ClassType::*memberFunction)(Args...), Policies...) { + using namespace internal; + + typename WithPolicies::template ArgTypeList args; + _embind_register_class_method( + TypeID::get(), methodName, - internal::TypeID::get(), + TypeID::get(), args.count, args.types, - reinterpret_cast(&internal::MethodInvoker::invoke), + reinterpret_cast(&MethodInvoker::invoke), sizeof(memberFunction), &memberFunction); return *this; } - template - class_& method(const char* methodName, ReturnType (ClassType::*memberFunction)(Args...) const) { - internal::ArgTypeList args; - internal::_embind_register_class_method( - internal::TypeID::get(), + template + class_& method(const char* methodName, ReturnType (ClassType::*memberFunction)(Args...) const, Policies...) { + using namespace internal; + + typename WithPolicies::template ArgTypeList args; + _embind_register_class_method( + TypeID::get(), methodName, - internal::TypeID::get(), + TypeID::get(), args.count, args.types, - reinterpret_cast(&internal::ConstMethodInvoker::invoke), + reinterpret_cast(&ConstMethodInvoker::invoke), sizeof(memberFunction), &memberFunction); return *this; @@ -506,31 +555,39 @@ namespace emscripten { template class_& field(const char* fieldName, FieldType ClassType::*field) { - internal::_embind_register_class_field( - internal::TypeID::get(), + using namespace internal; + + _embind_register_class_field( + TypeID::get(), fieldName, - internal::TypeID::get(), - reinterpret_cast(&internal::FieldAccess::get), - reinterpret_cast(&internal::FieldAccess::set), + TypeID::get(), + reinterpret_cast(&FieldAccess::get), + reinterpret_cast(&FieldAccess::set), sizeof(field), &field); return *this; } - template - class_& classmethod(const char* methodName, ReturnType (*classMethod)(Args...)) { - internal::ArgTypeList args; - internal::_embind_register_class_classmethod( - internal::TypeID::get(), + template + class_& classmethod(const char* methodName, ReturnType (*classMethod)(Args...), Policies...) { + using namespace internal; + + typename WithPolicies::template ArgTypeList args; + _embind_register_class_classmethod( + TypeID::get(), methodName, - internal::TypeID::get(), + TypeID::get(), args.count, args.types, - reinterpret_cast(classMethod)); + reinterpret_cast(classMethod)); return *this; } }; + //////////////////////////////////////////////////////////////////////////////// + // ENUMS + //////////////////////////////////////////////////////////////////////////////// + template class enum_ { public: @@ -597,6 +654,10 @@ namespace emscripten { }; } + //////////////////////////////////////////////////////////////////////////////// + // INTERFACES + //////////////////////////////////////////////////////////////////////////////// + template class wrapper : public InterfaceType { public: diff --git a/system/include/emscripten/wire.h b/system/include/emscripten/wire.h index ceabd2805..ba78906b9 100644 --- a/system/include/emscripten/wire.h +++ b/system/include/emscripten/wire.h @@ -11,12 +11,19 @@ namespace emscripten { namespace internal { typedef const struct _TYPEID* TYPEID; + extern "C" { + void _embind_register_raw_pointer( + TYPEID pointee, + TYPEID pointer); + } + // This implementation is technically not legal, as it's not // required that two calls to typeid produce the same exact - // std::type_info instance. That said, it's likely to work. - // Should it not work in the future: replace TypeID with - // an int, and store all TypeInfo we see in a map, allocating - // new TypeIDs as we add new items to the map. + // std::type_info instance. That said, it's likely to work + // given Emscripten compiles everything into one binary. + // Should it not work in the future: replace TypeID with an + // int, and store all TypeInfo we see in a map, allocating new + // TypeIDs as we add new items to the map. template struct TypeID { static TYPEID get() { @@ -30,6 +37,24 @@ namespace emscripten { return TypeID::get(); } }; + + template + struct TypeID { + static_assert(!std::is_pointer::value, "Implicitly binding raw pointers is illegal. Specify ???"); + }; + + template + struct AllowedRawPointer { + }; + + template + struct TypeID> { + static TYPEID get() { + TYPEID ptype = reinterpret_cast(&typeid(T*)); + _embind_register_raw_pointer(TypeID::get(), ptype); + return ptype; + } + }; // count<> @@ -38,44 +63,81 @@ namespace emscripten { template<> struct count<> { - enum { value = 0 }; + constexpr static unsigned value = 0; }; template struct count { - enum { value = 1 + count::value }; + constexpr static unsigned value = 1 + count::value; }; - // ArgTypeList<> + // ExecutePolicies<> - template - struct ArgTypes; + template + struct ExecutePolicies; template<> - struct ArgTypes<> { + struct ExecutePolicies<> { + template + struct With { + typedef T type; + }; + }; + + template + struct ExecutePolicies { + template + struct With { + typedef typename Policy::template Transform< + typename ExecutePolicies::template With::type, + Index + >::type type; + }; + }; + + // ArgTypes<> + + template + struct ArgTypes; + + template + struct ArgTypes { + template static void fill(TYPEID* argTypes) { } }; - template - struct ArgTypes { + template + struct ArgTypes { + template static void fill(TYPEID* argTypes) { - *argTypes = TypeID::get(); - return ArgTypes::fill(argTypes + 1); + typedef typename ExecutePolicies::template With::type TransformT; + + *argTypes = TypeID::get(); + return ArgTypes::fill(argTypes + 1); } }; + // WithPolicies<...>::ArgTypeList<...> + template + struct WithPolicies { + template + struct ArgTypeList { + enum { args_count = count::value }; + + ArgTypeList() { + count = args_count; + ArgTypes<0, Args...>::template fill(types); + } + + unsigned count; + TYPEID types[args_count]; + }; + }; + + // TODO: kill this and make every signature support policies template - struct ArgTypeList { - enum { args_count = count::value }; - - ArgTypeList() { - count = args_count; - ArgTypes::fill(types); - } - - unsigned count; - TYPEID types[args_count]; + struct ArgTypeList : WithPolicies<>::ArgTypeList { }; // BindingType @@ -149,6 +211,13 @@ namespace emscripten { } }; + template + struct BindingType; + + template + struct BindingType> { + }; + template struct EnumBindingType { typedef Enum WireType; From eb5226e16cca6df5c4c889213150970dc9c6d5c3 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Thu, 1 Nov 2012 18:22:36 -0700 Subject: [PATCH 013/258] Class static methods were downright broken. --- src/embind/embind.js | 14 ++++++++------ system/include/emscripten/bind.h | 2 ++ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index e08688380..ef1851e13 100644 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -566,27 +566,29 @@ function __embind_register_class_classmethod( returnType, argCount, argTypes, - method + invoker, + fn ) { 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]; + invoker = FUNCTION_TABLE[invoker]; classType.constructor[methodName] = function() { if (arguments.length !== argCount) { - throw new BindingError('emscripten binding method ' + humanName + ' called with ' + arguments.length + ' arguments, expected ' + argCount); + throw new BindingError('emscripten binding class method ' + humanName + ' called with ' + arguments.length + ' arguments, expected ' + argCount); } var destructors = []; - var args = new Array(argCount); + var args = new Array(argCount + 1); + args[0] = fn; for (var i = 0; i < argCount; ++i) { - args[i] = argTypes[i].toWireType(destructors, arguments[i]); + args[i + 1] = argTypes[i].toWireType(destructors, arguments[i]); } - var rv = returnType.fromWireType(method.apply(null, args)); + var rv = returnType.fromWireType(invoker.apply(null, args)); runDestructors(destructors); return rv; }; diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index f2e70b31d..e48be414b 100644 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -134,6 +134,7 @@ namespace emscripten { TYPEID returnType, unsigned argCount, TYPEID argTypes[], + GenericFunction invoker, GenericFunction method); void _embind_register_enum( @@ -579,6 +580,7 @@ namespace emscripten { TypeID::get(), args.count, args.types, + reinterpret_cast(&internal::Invoker::invoke), reinterpret_cast(classMethod)); return *this; } From 8a4997cf16605a7811f32a30116356b9bc0e912e Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Thu, 1 Nov 2012 18:27:21 -0700 Subject: [PATCH 014/258] Kill some duplication --- src/embind/embind.js | 40 ++++++++++++++-------------------------- 1 file changed, 14 insertions(+), 26 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index ef1851e13..43238f07a 100644 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -165,15 +165,10 @@ function runDestructors(destructors) { } } -function __embind_register_function(name, returnType, argCount, argTypes, invoker, fn) { - name = Pointer_stringify(name); - returnType = requireRegisteredType(returnType, "Function " + name + " return value"); - invoker = FUNCTION_TABLE[invoker]; - argTypes = requireArgumentTypes(argCount, argTypes, name); - - exposePublicSymbol(name, function() { +function makeInvoker(name, returnType, argCount, argTypes, invoker, fn) { + return function() { if (arguments.length !== argCount) { - throw new BindingError('emscripten binding function ' + name + ' called with ' + arguments.length + ' arguments, expected ' + argCount); + throw new BindingError('function ' + name + ' called with ' + arguments.length + ' arguments, expected ' + argCount); } var destructors = []; var args = new Array(argCount + 1); @@ -184,7 +179,16 @@ function __embind_register_function(name, returnType, argCount, argTypes, invoke var rv = returnType.fromWireType(invoker.apply(null, args)); runDestructors(destructors); return rv; - }); + } +} + +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); + + exposePublicSymbol(name, makeInvoker(name, returnType, argCount, argTypes, invoker, fn)); } function __embind_register_tuple(tupleType, name, constructor, destructor) { @@ -576,23 +580,7 @@ function __embind_register_class_classmethod( argTypes = requireArgumentTypes(argCount, argTypes, 'classmethod ' + humanName); invoker = FUNCTION_TABLE[invoker]; - classType.constructor[methodName] = function() { - if (arguments.length !== argCount) { - throw new BindingError('emscripten binding class method ' + humanName + ' 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; - }; -} + classType.constructor[methodName] = makeInvoker(humanName, returnType, argCount, argTypes, invoker, fn);} function __embind_register_class_field( classType, From 3f20ded152916e31d7c85763074686461db7e3b8 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Thu, 1 Nov 2012 18:27:54 -0700 Subject: [PATCH 015/258] jshint --- src/embind/embind.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 43238f07a..dd8e2b3b2 100644 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -179,7 +179,7 @@ function makeInvoker(name, returnType, argCount, argTypes, invoker, fn) { var rv = returnType.fromWireType(invoker.apply(null, args)); runDestructors(destructors); return rv; - } + }; } function __embind_register_function(name, returnType, argCount, argTypes, invoker, fn) { From 6014feed714b61f57f26254de6a2d2c7d89363e5 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Thu, 1 Nov 2012 18:37:40 -0700 Subject: [PATCH 016/258] Add support for raw pointers to embind --- src/embind/embind.js | 3 +++ system/include/emscripten/wire.h | 8 +++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index dd8e2b3b2..8cfb87c8a 100644 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -428,6 +428,9 @@ function __embind_register_raw_pointer( var name = pointeeType.name + '*'; registerType(pointerType, name, { name: name, + toWireType: function(destructors, o) { + return o.ptr; + } }); } diff --git a/system/include/emscripten/wire.h b/system/include/emscripten/wire.h index ba78906b9..276042aa1 100644 --- a/system/include/emscripten/wire.h +++ b/system/include/emscripten/wire.h @@ -212,10 +212,12 @@ namespace emscripten { }; template - struct BindingType; + struct BindingType { + typedef T* WireType; - template - struct BindingType> { + static T* fromWireType(WireType wt) { + return wt; + } }; template From 8691fb1726c63263f99e86b8dd29d1a90d639579 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Thu, 1 Nov 2012 19:33:42 -0700 Subject: [PATCH 017/258] Allow multiple pointer arguments and allow multiple functions taking the same raw pointer type --- src/embind/embind.js | 32 +++++++++-------- system/include/emscripten/bind.h | 38 ++++++++++++++------ system/include/emscripten/val.h | 62 ++++++++++++++++++-------------- system/include/emscripten/wire.h | 19 ++-------- 4 files changed, 84 insertions(+), 67 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 8cfb87c8a..bbbeb96c8 100644 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -420,22 +420,10 @@ function __embind_register_smart_ptr( }); } -function __embind_register_raw_pointer( - pointeeType, - pointerType -) { - pointeeType = requireRegisteredType(pointeeType, 'class'); - var name = pointeeType.name + '*'; - registerType(pointerType, name, { - name: name, - toWireType: function(destructors, o) { - return o.ptr; - } - }); -} - function __embind_register_class( classType, + pointerType, + constPointerType, name, destructor ) { @@ -496,6 +484,22 @@ function __embind_register_class( } }); + var pointerName = name + '*'; + registerType(pointerType, pointerName, { + name: pointerName, + toWireType: function(destructors, o) { + return o.ptr; + } + }); + + var constPointerName = name + ' const*'; + registerType(constPointerType, constPointerName, { + name: constPointerName, + toWireType: function(destructors, o) { + return o.ptr; + } + }); + exposePublicSymbol(name, constructor); } diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index e48be414b..9bfef10be 100644 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -100,6 +100,8 @@ namespace emscripten { void _embind_register_class( TYPEID classType, + TYPEID pointerType, + TYPEID constPointerType, const char* className, GenericFunction destructor); @@ -187,6 +189,18 @@ namespace emscripten { }; }; + // whitelist all raw pointers + struct allow_raw_pointers { + template + struct Transform { + typedef typename std::conditional< + std::is_pointer::value, + internal::AllowedRawPointer::type>, + InputType + >::type type; + }; + }; + namespace internal { template struct Invoker { @@ -219,18 +233,20 @@ namespace emscripten { // FUNCTIONS //////////////////////////////////////////////////////////////////////////////// - template + template void function(const char* name, ReturnType (fn)(Args...)) { - internal::registerStandardTypes(); + using namespace internal; - internal::ArgTypeList args; - internal::_embind_register_function( + registerStandardTypes(); + + typename WithPolicies::template ArgTypeList args; + _embind_register_function( name, - internal::TypeID::get(), + TypeID::get(), args.count, args.types, - reinterpret_cast(&internal::Invoker::invoke), - reinterpret_cast(fn)); + reinterpret_cast(&Invoker::invoke), + reinterpret_cast(fn)); } namespace internal { @@ -503,15 +519,17 @@ namespace emscripten { registerStandardTypes(); _embind_register_class( TypeID::get(), + TypeID>::get(), + TypeID>::get(), name, reinterpret_cast(&raw_destructor)); } - template - class_& constructor() { + template + class_& constructor(Policies...) { using namespace internal; - ArgTypeList args; + typename WithPolicies::template ArgTypeList args; _embind_register_class_constructor( TypeID::get(), args.count, diff --git a/system/include/emscripten/val.h b/system/include/emscripten/val.h index 8369caf76..e3e579011 100644 --- a/system/include/emscripten/val.h +++ b/system/include/emscripten/val.h @@ -19,7 +19,7 @@ namespace emscripten { 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); + void _emval_as(EM_VAL value, TYPEID returnType); EM_VAL _emval_call( EM_VAL value, unsigned argCount, @@ -108,69 +108,77 @@ namespace emscripten { template val operator()(Args... args) { - internal::ArgTypeList argList; - typedef internal::EM_VAL (*TypedCall)( - internal::EM_VAL, + using namespace internal; + + WithPolicies<>::ArgTypeList argList; + typedef EM_VAL (*TypedCall)( + EM_VAL, unsigned, - internal::TYPEID argTypes[], - typename internal::BindingType::WireType...); - TypedCall typedCall = reinterpret_cast(&internal::_emval_call); + TYPEID argTypes[], + typename BindingType::WireType...); + TypedCall typedCall = reinterpret_cast(&_emval_call); return val( typedCall( handle, argList.count, argList.types, - internal::toWireType(args)...)); + toWireType(args)...)); } template val call(const char* name, Args... args) { - internal::ArgTypeList argList; - typedef internal::EM_VAL (*TypedCall)( - internal::EM_VAL, + using namespace internal; + + WithPolicies<>::ArgTypeList argList; + typedef EM_VAL (*TypedCall)( + EM_VAL, const char* name, unsigned, - internal::TYPEID argTypes[], - typename internal::BindingType::WireType...); - TypedCall typedCall = reinterpret_cast(&internal::_emval_call_method); + TYPEID argTypes[], + typename BindingType::WireType...); + TypedCall typedCall = reinterpret_cast(&_emval_call_method); return val( typedCall( handle, name, argList.count, argList.types, - internal::toWireType(args)...)); + toWireType(args)...)); } template void call_void(const char* name, Args... args) { - internal::ArgTypeList argList; + using namespace internal; + + WithPolicies<>::ArgTypeList argList; typedef void (*TypedCall)( - internal::EM_VAL, + EM_VAL, const char* name, unsigned, - internal::TYPEID argTypes[], - typename internal::BindingType::WireType...); - TypedCall typedCall = reinterpret_cast(&internal::_emval_call_void_method); + TYPEID argTypes[], + typename BindingType::WireType...); + TypedCall typedCall = reinterpret_cast(&_emval_call_void_method); return typedCall( handle, name, argList.count, argList.types, - internal::toWireType(args)...); + toWireType(args)...); } template T as() const { - typedef internal::BindingType BT; + using namespace internal; + + typedef BindingType BT; typedef typename BT::WireType (*TypedAs)( - internal::EM_VAL value, - emscripten::internal::TYPEID returnType); - TypedAs typedAs = reinterpret_cast(&internal::_emval_as); + EM_VAL value, + TYPEID returnType); + TypedAs typedAs = reinterpret_cast(&_emval_as); - typename BT::WireType wt = typedAs(handle, internal::TypeID::get()); - internal::WireDeleter deleter(wt); + typename BT::WireType wt = typedAs(handle, TypeID::get()); + WireDeleter deleter(wt); return BT::fromWireType(wt); } diff --git a/system/include/emscripten/wire.h b/system/include/emscripten/wire.h index 276042aa1..cee0f5393 100644 --- a/system/include/emscripten/wire.h +++ b/system/include/emscripten/wire.h @@ -11,12 +11,6 @@ namespace emscripten { namespace internal { typedef const struct _TYPEID* TYPEID; - extern "C" { - void _embind_register_raw_pointer( - TYPEID pointee, - TYPEID pointer); - } - // 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 @@ -40,7 +34,7 @@ namespace emscripten { template struct TypeID { - static_assert(!std::is_pointer::value, "Implicitly binding raw pointers is illegal. Specify ???"); + static_assert(!std::is_pointer::value, "Implicitly binding raw pointers is illegal. Specify allow_raw_pointer>"); }; template @@ -50,9 +44,7 @@ namespace emscripten { template struct TypeID> { static TYPEID get() { - TYPEID ptype = reinterpret_cast(&typeid(T*)); - _embind_register_raw_pointer(TypeID::get(), ptype); - return ptype; + return reinterpret_cast(&typeid(T*)); } }; @@ -114,7 +106,7 @@ namespace emscripten { typedef typename ExecutePolicies::template With::type TransformT; *argTypes = TypeID::get(); - return ArgTypes::fill(argTypes + 1); + return ArgTypes::template fill(argTypes + 1); } }; @@ -135,11 +127,6 @@ namespace emscripten { }; }; - // TODO: kill this and make every signature support policies - template - struct ArgTypeList : WithPolicies<>::ArgTypeList { - }; - // BindingType template From ebbe87925570d572204c105ff15a1c5581c76d54 Mon Sep 17 00:00:00 2001 From: jinsuck Date: Fri, 2 Nov 2012 15:36:52 -0700 Subject: [PATCH 018/258] to get string from emval --- system/include/emscripten/wire.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/system/include/emscripten/wire.h b/system/include/emscripten/wire.h index cee0f5393..89b18d772 100644 --- a/system/include/emscripten/wire.h +++ b/system/include/emscripten/wire.h @@ -185,6 +185,9 @@ namespace emscripten { static std::string fromWireType(char* v) { return std::string(v); } + static void destroy(WireType v) { + delete v; + } }; template From 848bdefa4e082936f3b1e26cbd9d0239efb9cf62 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Mon, 5 Nov 2012 12:26:16 -0800 Subject: [PATCH 019/258] std::string's WireType::destroy should be free(), not delete. --- system/include/emscripten/wire.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/include/emscripten/wire.h b/system/include/emscripten/wire.h index 89b18d772..89e68c684 100644 --- a/system/include/emscripten/wire.h +++ b/system/include/emscripten/wire.h @@ -186,7 +186,7 @@ namespace emscripten { return std::string(v); } static void destroy(WireType v) { - delete v; + free(v); } }; From 565823a14ad14a5c16f8c40f714963e7cb259e33 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Mon, 5 Nov 2012 13:20:13 -0800 Subject: [PATCH 020/258] Returning an empty shared_ptr returns null to JavaScript. Similarly, passing null into a shared_ptr creates an empty shared_ptr. --- src/embind/embind.js | 16 +++++++++++++--- system/include/emscripten/wire.h | 18 ++++++++++++++++++ 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index bbbeb96c8..98d4b13e4 100644 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -60,7 +60,9 @@ function __embind_register_bool(boolType, name, trueValue, falseValue) { return o ? trueValue : falseValue; }, fromWireType: function(wt) { - return wt === trueValue; + // ambiguous emscripten ABI: sometimes return values are + // true or false, and sometimes integers (0 or 1) + return !!wt; }, }); } @@ -397,7 +399,7 @@ function __embind_register_smart_ptr( }; Handle.prototype['delete'] = function() { - if (!this.ptr && !this.smartPointer) { + if (!this.ptr) { throw new BindingError(pointeeType.name + ' instance already deleted'); } @@ -412,10 +414,18 @@ function __embind_register_smart_ptr( registerType(pointerType, name, { name: name, fromWireType: function(ptr) { + if (!getPointee(ptr)) { + destructor(ptr); + return null; + } return new Handle(ptr); }, toWireType: function(destructors, o) { - return o.smartPointer; + if (null === o) { + return 0; + } else { + return o.smartPointer; + } } }); } diff --git a/system/include/emscripten/wire.h b/system/include/emscripten/wire.h index 89e68c684..452628a05 100644 --- a/system/include/emscripten/wire.h +++ b/system/include/emscripten/wire.h @@ -210,6 +210,24 @@ namespace emscripten { } }; + template + struct BindingType> { + typedef std::shared_ptr shared_ptr; + typedef std::shared_ptr* WireType; + + static WireType toWireType(shared_ptr p) { + return new shared_ptr(p); + } + + static shared_ptr fromWireType(WireType wt) { + if (wt) { + return shared_ptr(*wt); + } else { + return shared_ptr(); + } + } + }; + template struct EnumBindingType { typedef Enum WireType; From 3c5dead6741c0a2f3268b04d1ee9a0262f368785 Mon Sep 17 00:00:00 2001 From: Todd Lee Date: Mon, 5 Nov 2012 14:16:46 -0800 Subject: [PATCH 021/258] Add support for std::vector. --- src/embind/embind.js | 45 ++++++++++++++++++++++++ system/include/emscripten/bind.h | 59 ++++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+) diff --git a/src/embind/embind.js b/src/embind/embind.js index 98d4b13e4..0036013d6 100644 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -430,6 +430,51 @@ function __embind_register_smart_ptr( }); } +function __embind_register_vector( + vectorType, + elementType, + name, + constructor, + destructor, + length, + getter, + setter +) { + name = Pointer_stringify(name); + elementType = requireRegisteredType(elementType, 'vector ' + name); + + constructor = FUNCTION_TABLE[constructor]; + destructor = FUNCTION_TABLE[destructor]; + length = FUNCTION_TABLE[length]; + getter = FUNCTION_TABLE[getter]; + setter = FUNCTION_TABLE[setter]; + + registerType(vectorType, name, { + name: name, + fromWireType: function(ptr) { + var arr = []; + var n = length(ptr); + + for (var i = 0; i < n; i++) { + var v = elementType.fromWireType(getter(ptr, i)); + arr.push(v); + } + + destructor(ptr); + return arr; + }, + toWireType: function(destructors, o) { + var vec = constructor(); + for (var val in o) { + setter(vec, elementType.toWireType(destructors, o[val])); + } + destructors.push(destructor); + destructors.push(vec); + return vec; + } + }); +} + function __embind_register_class( classType, pointerType, diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index 9bfef10be..d92933782 100644 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -98,6 +99,16 @@ namespace emscripten { GenericFunction destructor, GenericFunction getPointee); + void _embind_register_vector( + TYPEID vectorType, + TYPEID elementType, + const char* name, + GenericFunction constructor, + GenericFunction destructor, + GenericFunction length, + GenericFunction getter, + GenericFunction setter); + void _embind_register_class( TYPEID classType, TYPEID pointerType, @@ -368,6 +379,33 @@ namespace emscripten { setter(ptr, FieldBinding::fromWireType(value)); } }; + + template + struct Vector { + typedef typename VectorType::value_type ElementType; + typedef internal::BindingType FieldBinding; + typedef typename FieldBinding::WireType WireType; + + static int length( + VectorType* ptr + ) { + return (*ptr).size(); + } + + static WireType getAt( + VectorType* ptr, + int pos + ) { + return FieldBinding::toWireType((*ptr).at(pos)); + } + + static void push_back( + VectorType* ptr, + WireType val + ) { + (*ptr).push_back(FieldBinding::fromWireType(val)); + } + }; } //////////////////////////////////////////////////////////////////////////////// @@ -504,6 +542,27 @@ namespace emscripten { reinterpret_cast(&internal::get_pointee)); } + //////////////////////////////////////////////////////////////////////////////// + // VECTORS + //////////////////////////////////////////////////////////////////////////////// + + template + inline void register_vector(const char* name) { + typedef typename VectorType::value_type ElementType; + + internal::registerStandardTypes(); + internal::_embind_register_vector( + internal::TypeID::get(), + internal::TypeID::get(), + name, + reinterpret_cast(&internal::raw_constructor), + reinterpret_cast(&internal::raw_destructor), + reinterpret_cast(&internal::Vector::length), + reinterpret_cast(&internal::Vector::getAt), + reinterpret_cast(&internal::Vector::push_back) + ); + } + //////////////////////////////////////////////////////////////////////////////// // CLASSES //////////////////////////////////////////////////////////////////////////////// From d90ef85c03fa1094936d00b635df554c40ff0361 Mon Sep 17 00:00:00 2001 From: Llorens Marti Garcia Date: Mon, 5 Nov 2012 17:22:01 -0800 Subject: [PATCH 022/258] Fix on EnumBindingType --- system/include/emscripten/wire.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/system/include/emscripten/wire.h b/system/include/emscripten/wire.h index 452628a05..3e96482a5 100644 --- a/system/include/emscripten/wire.h +++ b/system/include/emscripten/wire.h @@ -238,6 +238,8 @@ namespace emscripten { static Enum fromWireType(WireType v) { return v; } + static void destroy(WireType) { + } }; template From 6326240227f217b85ac505163deb83990ecf92e6 Mon Sep 17 00:00:00 2001 From: Todd Lee Date: Fri, 9 Nov 2012 15:54:58 -0800 Subject: [PATCH 023/258] This enables passing smart ptr as an element of val. --- system/include/emscripten/wire.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/system/include/emscripten/wire.h b/system/include/emscripten/wire.h index 3e96482a5..99c4f926d 100644 --- a/system/include/emscripten/wire.h +++ b/system/include/emscripten/wire.h @@ -226,6 +226,10 @@ namespace emscripten { return shared_ptr(); } } + + static void destroy(WireType p) { + delete p; + } }; template From d622aa9caba106ddc31f6aee01082eb029a35515 Mon Sep 17 00:00:00 2001 From: mey Date: Fri, 9 Nov 2012 17:06:23 -0800 Subject: [PATCH 024/258] Exposing std::function to javascript. --- src/embind/embind.js | 81 +++++++++++++++++++++++++++++++- system/include/emscripten/bind.h | 48 +++++++++++++++++++ 2 files changed, 128 insertions(+), 1 deletion(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 0036013d6..1953542a9 100644 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -421,6 +421,7 @@ function __embind_register_smart_ptr( return new Handle(ptr); }, toWireType: function(destructors, o) { + return o.smartPointer; if (null === o) { return 0; } else { @@ -430,6 +431,84 @@ function __embind_register_smart_ptr( }); } +function __embind_register_function_ptr( + name, + functorType, + returnType, + argCount, + argTypes, + destructor, + invoker +) { + name = Pointer_stringify(name); + var humanName = 'functor::' + name; + + returnType = requireRegisteredType(returnType); + argTypes = requireArgumentTypes(argCount, argTypes, humanName); + destructor = FUNCTION_TABLE[destructor]; + invoker = FUNCTION_TABLE[invoker]; + + var Handle = createNamedFunction(name, function(ptr) { + this.count = {value: 1}; + this.functorPtr = ptr; + }); + + Handle.prototype['delete'] = function() { + if (!this.functorPtr) { + throw new BindingError(functorType.name + ' instance already deleted'); + } + + this.count.value -= 1; + if(0 === this.count.value) { + destructor(this.functorPtr); + } + this.functorPtr = undefined; + }; + + function createFunctor(ptr) { + var h = new Handle(ptr); + + var invoke = function() { + if(!this.functorPtr) { + throw new BindingError('cannot call invoke functor ' + humanName + ' on deleted object'); + } + + if (arguments.length !== argCount) { + throw new BindingError('emscripten functor ' + humanName + ' called with ' + arguments.length + ' arguments, expected ' + argCount); + } + + var destructors = []; + var args = new Array(argCount + 1); + args[0] = this.functorPtr; + + 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; + }.bind(h); + + invoke.handle = h; + invoke['delete'] = function() { + this.handle.delete(); + }; + + return invoke; + } + + registerType(functorType, name, { + name: name, + fromWireType: function(ptr) { + return createFunctor(ptr); + }, + toWireType: function(destructors, o) { + return o.ptr; + } + }); +} + function __embind_register_vector( vectorType, elementType, @@ -489,7 +568,7 @@ function __embind_register_class( this.count = {value: 1}; this.ptr = ptr; }); - + Handle.prototype.clone = function() { if (!this.ptr) { throw new BindingError(classType.name + ' instance already deleted'); diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index d92933782..1ce7967d0 100644 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -99,6 +100,18 @@ namespace emscripten { GenericFunction destructor, GenericFunction getPointee); +<<<<<<< HEAD +======= + void _embind_register_function_ptr( + const char* name, + TYPEID functorType, + TYPEID returnType, + unsigned argCount, + TYPEID argTypes[], + GenericFunction destructor, + GenericFunction invoker); + +>>>>>>> Exposing std::function to javascript. void _embind_register_vector( TYPEID vectorType, TYPEID elementType, @@ -281,6 +294,21 @@ namespace emscripten { return ptr.get(); } + template + struct FunctorInvoker { + static typename internal::BindingType::WireType invoke( + const FunctorType& ptr, + typename internal::BindingType::WireType... args + ) { + return internal::BindingType::toWireType( + ptr( + internal::BindingType::fromWireType(args)... + ) + ); + } + }; + + template struct MethodInvoker { typedef ReturnType (ClassType::*MemberPointer)(Args...); @@ -542,6 +570,26 @@ namespace emscripten { reinterpret_cast(&internal::get_pointee)); } + //////////////////////////////////////////////////////////////////////////////// + // FUNCTION POINTERS + //////////////////////////////////////////////////////////////////////////////// + template + inline void register_function_ptr(const char* name) { + typedef std::function FunctorType; + + internal::registerStandardTypes(); + typename internal::WithPolicies::template ArgTypeList args; + internal::_embind_register_function_ptr( + name, + internal::TypeID::get(), + internal::TypeID::get(), + args.count, + args.types, + reinterpret_cast(&internal::raw_destructor), + reinterpret_cast(&internal::FunctorInvoker::invoke) + ); + } + //////////////////////////////////////////////////////////////////////////////// // VECTORS //////////////////////////////////////////////////////////////////////////////// From 01212260c27a76c7f02b62a4c42c3d4501351e60 Mon Sep 17 00:00:00 2001 From: mey Date: Fri, 9 Nov 2012 17:10:10 -0800 Subject: [PATCH 025/258] Merging. --- system/include/emscripten/bind.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index 1ce7967d0..97a9b5cc0 100644 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -100,8 +100,6 @@ namespace emscripten { GenericFunction destructor, GenericFunction getPointee); -<<<<<<< HEAD -======= void _embind_register_function_ptr( const char* name, TYPEID functorType, @@ -111,7 +109,6 @@ namespace emscripten { GenericFunction destructor, GenericFunction invoker); ->>>>>>> Exposing std::function to javascript. void _embind_register_vector( TYPEID vectorType, TYPEID elementType, From 8ff84649adf2120ad7800f9bba03fff194b657bb Mon Sep 17 00:00:00 2001 From: mey Date: Fri, 9 Nov 2012 17:14:07 -0800 Subject: [PATCH 026/258] Merging. --- src/embind/embind.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 1953542a9..4cf58028d 100644 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -421,7 +421,6 @@ function __embind_register_smart_ptr( return new Handle(ptr); }, toWireType: function(destructors, o) { - return o.smartPointer; if (null === o) { return 0; } else { From 69f2f335d3194ae6ef482169e23ad8edcd8dd1ca Mon Sep 17 00:00:00 2001 From: mey Date: Mon, 12 Nov 2012 16:47:03 -0800 Subject: [PATCH 027/258] Fleshing out functors and expanding them. --- src/embind/embind.js | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 4cf58028d..74afda02a 100644 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -492,7 +492,24 @@ function __embind_register_function_ptr( invoke.handle = h; invoke['delete'] = function() { this.handle.delete(); - }; + }.bind(invoke); + invoke.clone = function() { + if (!this.handle.functorPtr) { + throw new BindingError(functorType.name + ' instance already deleted'); + } + + var clone = createFunctor(this.handle.functorPtr); + clone.handle.count = this.handle.count; + clone.handle.ptr = this.handle.ptr; + + clone.handle.count.value += 1; + return clone; + }.bind(invoke); + invoke.move = function() { + var rv = this.clone(); + this.delete(); + return rv; + }.bind(invoke); return invoke; } @@ -503,7 +520,7 @@ function __embind_register_function_ptr( return createFunctor(ptr); }, toWireType: function(destructors, o) { - return o.ptr; + return o.handle.functorPtr; } }); } From 2c34b6dc6cf61087ff4f341bc33aefaa968803ba Mon Sep 17 00:00:00 2001 From: jinsuck Date: Tue, 13 Nov 2012 13:23:23 -0800 Subject: [PATCH 028/258] Allow constructing interface wrapper from existing interface object --- system/include/emscripten/bind.h | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index 97a9b5cc0..cb8f396de 100644 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -767,7 +767,15 @@ namespace emscripten { initialized = true; return *this; } - + + optional& operator=(optional& o) { + if (initialized) { + get()->~T(); + } + new(get()) T(*o); + initialized = true; + } + private: T* get() { return reinterpret_cast(&data); @@ -785,6 +793,14 @@ namespace emscripten { template class wrapper : public InterfaceType { public: + wrapper() {} // to avoid error "call to implicitly deleted construrtor..." + + wrapper(InterfaceType* interface) { + // why dynamic_cast causes javascript crash? + wrapper* iw = static_cast*>(interface); + jsobj = iw->jsobj; + } + // Not necessary in any example so far, but appeases a compiler warning. virtual ~wrapper() {} From 778b8eb983544434b2350fe3bb36363bbef0b347 Mon Sep 17 00:00:00 2001 From: jinsuck Date: Wed, 14 Nov 2012 15:00:56 -0800 Subject: [PATCH 029/258] add a syntactic sugar to for cloning to a shared pointer of interface wrapper --- system/include/emscripten/bind.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index cb8f396de..ffbf94015 100644 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -806,6 +806,13 @@ namespace emscripten { typedef InterfaceType interface; + template + static std::shared_ptr cloneToSharedPtr(InterfaceType& i) { + ConcreteWrapperType* cw = new ConcreteWrapperType(&i); + InterfaceType* ip = dynamic_cast(cw); + return std::shared_ptr(ip); + } + void initialize(internal::EM_VAL handle) { if (jsobj) { internal::_embind_fatal_error( From 0d745fa1167007c29554da9f10bd6a92bc0d171b Mon Sep 17 00:00:00 2001 From: jinsuck Date: Fri, 16 Nov 2012 11:35:59 -0800 Subject: [PATCH 030/258] 1) add a method to run global javascript function on val 2) fix a bug to return val from interface wrapper --- src/embind/emval.js | 7 +++++++ system/include/emscripten/val.h | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/src/embind/emval.js b/src/embind/emval.js index 6552b0a73..f74c0fd54 100644 --- a/src/embind/emval.js +++ b/src/embind/emval.js @@ -66,6 +66,13 @@ function __emval_get_property_by_unsigned_long(handle, k) { return __emval_register(_emval_handle_array[handle].value[k]); } +function __emval_eval_global_method(handle, objectName, methodName) { + var objectNameStr = Pointer_stringify(objectName); + var methodNameStr = Pointer_stringify(methodName); + var result = eval.call(null, objectNameStr)[methodNameStr](_emval_handle_array[handle].value); + return __emval_register(result); +} + function __emval_set_property(handle, k, value) { k = Pointer_stringify(k); _emval_handle_array[handle].value[k] = _emval_handle_array[value].value; diff --git a/system/include/emscripten/val.h b/system/include/emscripten/val.h index e3e579011..68f5b63e3 100644 --- a/system/include/emscripten/val.h +++ b/system/include/emscripten/val.h @@ -17,6 +17,7 @@ namespace emscripten { 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); + EM_VAL _emval_eval_global_method(EM_VAL object, const char* objectName, const char* methodName); 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, TYPEID returnType); @@ -98,6 +99,10 @@ namespace emscripten { return val(internal::_emval_get_property_by_unsigned_long(handle, key)); } + val eval_global_method(const char* objectName, const char* methodName) { + return val(internal::_emval_eval_global_method(handle, objectName, methodName)); + } + void set(const char* key, val v) { internal::_emval_set_property(handle, key, v.handle); } @@ -204,6 +209,8 @@ namespace emscripten { static val fromWireType(WireType v) { return val::take_ownership(v); } + static void destroy(WireType v) { + } }; } } From e237bfd79d7ae865f7eeaccefff5e8a9c7afe309 Mon Sep 17 00:00:00 2001 From: mey Date: Fri, 16 Nov 2012 13:56:27 -0800 Subject: [PATCH 031/258] Removing Functor as a specific type; folding operator call into the class definition. --- src/embind/embind.js | 159 +++++++++++-------------------- system/include/emscripten/bind.h | 62 ++++++------ 2 files changed, 90 insertions(+), 131 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 74afda02a..ff5adf645 100644 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -430,101 +430,6 @@ function __embind_register_smart_ptr( }); } -function __embind_register_function_ptr( - name, - functorType, - returnType, - argCount, - argTypes, - destructor, - invoker -) { - name = Pointer_stringify(name); - var humanName = 'functor::' + name; - - returnType = requireRegisteredType(returnType); - argTypes = requireArgumentTypes(argCount, argTypes, humanName); - destructor = FUNCTION_TABLE[destructor]; - invoker = FUNCTION_TABLE[invoker]; - - var Handle = createNamedFunction(name, function(ptr) { - this.count = {value: 1}; - this.functorPtr = ptr; - }); - - Handle.prototype['delete'] = function() { - if (!this.functorPtr) { - throw new BindingError(functorType.name + ' instance already deleted'); - } - - this.count.value -= 1; - if(0 === this.count.value) { - destructor(this.functorPtr); - } - this.functorPtr = undefined; - }; - - function createFunctor(ptr) { - var h = new Handle(ptr); - - var invoke = function() { - if(!this.functorPtr) { - throw new BindingError('cannot call invoke functor ' + humanName + ' on deleted object'); - } - - if (arguments.length !== argCount) { - throw new BindingError('emscripten functor ' + humanName + ' called with ' + arguments.length + ' arguments, expected ' + argCount); - } - - var destructors = []; - var args = new Array(argCount + 1); - args[0] = this.functorPtr; - - 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; - }.bind(h); - - invoke.handle = h; - invoke['delete'] = function() { - this.handle.delete(); - }.bind(invoke); - invoke.clone = function() { - if (!this.handle.functorPtr) { - throw new BindingError(functorType.name + ' instance already deleted'); - } - - var clone = createFunctor(this.handle.functorPtr); - clone.handle.count = this.handle.count; - clone.handle.ptr = this.handle.ptr; - - clone.handle.count.value += 1; - return clone; - }.bind(invoke); - invoke.move = function() { - var rv = this.clone(); - this.delete(); - return rv; - }.bind(invoke); - - return invoke; - } - - registerType(functorType, name, { - name: name, - fromWireType: function(ptr) { - return createFunctor(ptr); - }, - toWireType: function(destructors, o) { - return o.handle.functorPtr; - } - }); -} - function __embind_register_vector( vectorType, elementType, @@ -579,10 +484,25 @@ function __embind_register_class( ) { name = Pointer_stringify(name); destructor = FUNCTION_TABLE[destructor]; - + var Handle = createNamedFunction(name, function(ptr) { - this.count = {value: 1}; - this.ptr = ptr; + var h = function() { + if(h.operator_call !== undefined) { + return h.operator_call.apply(h, arguments); + } else { + throw new BindingError(name + ' does not define call operator'); + } + }; + + h.count = {value: 1}; + h.ptr = ptr; + + for(var prop in Handle.prototype) { + var dp = Object.getOwnPropertyDescriptor(Handle.prototype, prop); + Object.defineProperty(h, prop, dp); + } + + return h; }); Handle.prototype.clone = function() { @@ -618,7 +538,7 @@ function __embind_register_class( var constructor = createNamedFunction(name, function() { var body = constructor.body; - body.apply(this, arguments); + return body.apply(this, arguments); }); constructor.prototype = Handle.prototype; @@ -676,7 +596,8 @@ function __embind_register_class_constructor( var ptr = constructor.apply(null, args); runDestructors(destructors); - classType.Handle.call(this, ptr); + + return classType.Handle.call(this, ptr); }; } @@ -700,6 +621,7 @@ function __embind_register_class_method( memberFunction = copyMemberPointer(memberFunction, memberFunctionSize); classType.Handle.prototype[methodName] = function() { + if (!this.ptr) { throw new BindingError('cannot call emscripten binding method ' + humanName + ' on deleted object'); } @@ -737,7 +659,42 @@ function __embind_register_class_classmethod( argTypes = requireArgumentTypes(argCount, argTypes, 'classmethod ' + humanName); invoker = FUNCTION_TABLE[invoker]; - classType.constructor[methodName] = makeInvoker(humanName, returnType, argCount, argTypes, invoker, fn);} + classType.constructor[methodName] = makeInvoker(humanName, returnType, argCount, argTypes, invoker, fn); +} + +function __embind_register_class_operator_call( + classType, + returnType, + argCount, + argTypes, + invoker +) { + classType = requireRegisteredType(classType, 'class'); + returnType = requireRegisteredType(returnType, 'method ' + humanName + ' return value'); + argTypes = requireArgumentTypes(argCount, argTypes, 'method ' + humanName); + invoker = FUNCTION_TABLE[invoker]; + var humanName = classType.name + '.' + 'operator_call'; + + classType.Handle.prototype.operator_call = 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 + 1); + args[0] = this.ptr; + 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_class_field( classType, diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index ffbf94015..e20a5e688 100644 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -100,15 +100,6 @@ namespace emscripten { GenericFunction destructor, GenericFunction getPointee); - void _embind_register_function_ptr( - const char* name, - TYPEID functorType, - TYPEID returnType, - unsigned argCount, - TYPEID argTypes[], - GenericFunction destructor, - GenericFunction invoker); - void _embind_register_vector( TYPEID vectorType, TYPEID elementType, @@ -160,6 +151,14 @@ namespace emscripten { GenericFunction invoker, GenericFunction method); + void _embind_register_class_operator_call( + TYPEID classType, + TYPEID returnType, + unsigned argCount, + TYPEID argTypes[], + GenericFunction invoker + ); + void _embind_register_enum( TYPEID enumType, const char* name); @@ -255,7 +254,7 @@ namespace emscripten { //////////////////////////////////////////////////////////////////////////////// template - void function(const char* name, ReturnType (fn)(Args...)) { + void function(const char* name, ReturnType (fn)(Args...), Policies...) { using namespace internal; registerStandardTypes(); @@ -305,6 +304,15 @@ namespace emscripten { } }; + template + struct FunctorInvoker { + static void invoke( + const FunctorType& ptr, + typename internal::BindingType::WireType... args + ) { + ptr(internal::BindingType::fromWireType(args)...); + } + }; template struct MethodInvoker { @@ -567,26 +575,6 @@ namespace emscripten { reinterpret_cast(&internal::get_pointee)); } - //////////////////////////////////////////////////////////////////////////////// - // FUNCTION POINTERS - //////////////////////////////////////////////////////////////////////////////// - template - inline void register_function_ptr(const char* name) { - typedef std::function FunctorType; - - internal::registerStandardTypes(); - typename internal::WithPolicies::template ArgTypeList args; - internal::_embind_register_function_ptr( - name, - internal::TypeID::get(), - internal::TypeID::get(), - args.count, - args.types, - reinterpret_cast(&internal::raw_destructor), - reinterpret_cast(&internal::FunctorInvoker::invoke) - ); - } - //////////////////////////////////////////////////////////////////////////////// // VECTORS //////////////////////////////////////////////////////////////////////////////// @@ -706,6 +694,20 @@ namespace emscripten { reinterpret_cast(classMethod)); return *this; } + + template + class_& calloperator(Policies...) { + using namespace internal; + + typename WithPolicies::template ArgTypeList args; + _embind_register_class_operator_call( + TypeID::get(), + TypeID::get(), + args.count, + args.types, + reinterpret_cast(&internal::FunctorInvoker::invoke)); + return *this; + } }; //////////////////////////////////////////////////////////////////////////////// From 61b2ff66ffe1aba9550c828d5df9ecc0d3af8f7b Mon Sep 17 00:00:00 2001 From: mey Date: Fri, 16 Nov 2012 14:02:34 -0800 Subject: [PATCH 032/258] JSHint corrections. --- src/embind/embind.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index ff5adf645..6a59f11dd 100644 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -693,7 +693,7 @@ function __embind_register_class_operator_call( var rv = returnType.fromWireType(invoker.apply(null, args)); runDestructors(destructors); return rv; - } + }; } function __embind_register_class_field( From 338d59e81e5467df7021ad0854cee83b043c1204 Mon Sep 17 00:00:00 2001 From: Bill Welden Date: Sat, 10 Nov 2012 13:01:34 -0800 Subject: [PATCH 033/258] More files for upcast/downcast commit. --- src/embind/embind.js | 61 +++++++++++++++++++++++++- system/include/emscripten/bind.h | 74 +++++++++++++++++++++++++++----- 2 files changed, 123 insertions(+), 12 deletions(-) mode change 100644 => 100755 src/embind/embind.js mode change 100644 => 100755 system/include/emscripten/bind.h diff --git a/src/embind/embind.js b/src/embind/embind.js old mode 100644 new mode 100755 index 6a59f11dd..f491c1ddb --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -413,6 +413,7 @@ function __embind_register_smart_ptr( registerType(pointerType, name, { name: name, + Handle: Handle, fromWireType: function(ptr) { if (!getPointee(ptr)) { destructor(ptr); @@ -541,7 +542,7 @@ function __embind_register_class( return body.apply(this, arguments); }); constructor.prototype = Handle.prototype; - + registerType(classType, name, { name: name, constructor: constructor, @@ -643,6 +644,64 @@ function __embind_register_class_method( }; } +function __embind_register_cast_method( + classType, + methodName, + returnType, + invoker +) { + classType = requireRegisteredType(classType, 'class'); + methodName = Pointer_stringify(methodName); + var humanName = classType.name + '.' + methodName; + + returnType = requireRegisteredType(returnType, 'method ' + humanName + ' return value'); + invoker = FUNCTION_TABLE[invoker]; + + classType.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'); + } + + var args = new Array(1); + args[0] = this.ptr; + var rv = returnType.fromWireType(invoker.apply(null, args)); // in case ptr needs to be adjusted for multiple inheritance + return rv; + }; +} + +function __embind_register_pointer_cast_method( + classType, + methodName, + returnType, + invoker +) { + classType = requireRegisteredType(classType, 'class'); + methodName = Pointer_stringify(methodName); + var humanName = classType.name + '.' + methodName; + + returnType = requireRegisteredType(returnType, 'method ' + humanName + ' return value'); + invoker = FUNCTION_TABLE[invoker]; + + classType.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'); + } + 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 + return rv; + }; +} + function __embind_register_class_classmethod( classType, methodName, diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h old mode 100644 new mode 100755 index e20a5e688..ec3db2d24 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -133,6 +133,18 @@ namespace emscripten { size_t memberFunctionSize, void* memberFunction); + void _embind_register_cast_method( + TYPEID classType, + const char* methodName, + TYPEID returnType, + GenericFunction invoker); + + void _embind_register_pointer_cast_method( + TYPEID classType, + const char* methodName, + TYPEID returnType, + GenericFunction invoker); + void _embind_register_class_field( TYPEID classType, const char* fieldName, @@ -253,6 +265,17 @@ namespace emscripten { // FUNCTIONS //////////////////////////////////////////////////////////////////////////////// + + template + ToType& performCast(FromType& from) { + return *dynamic_cast(&from); + }; + + template + std::shared_ptr performPointerCast(std::shared_ptr from) { + return std::shared_ptr(from, dynamic_cast(from.get())); + }; + template void function(const char* name, ReturnType (fn)(Args...), Policies...) { using namespace internal; @@ -563,17 +586,35 @@ namespace emscripten { //////////////////////////////////////////////////////////////////////////////// template - inline void register_smart_ptr(const char* name) { - typedef typename PointerType::element_type PointeeType; + class register_smart_ptr { + public: + register_smart_ptr(const char* name) { + using namespace internal; + typedef typename PointerType::element_type PointeeType; - internal::registerStandardTypes(); - internal::_embind_register_smart_ptr( - internal::TypeID::get(), - internal::TypeID::get(), - name, - reinterpret_cast(&internal::raw_destructor), - reinterpret_cast(&internal::get_pointee)); - } + registerStandardTypes(); + _embind_register_smart_ptr( + TypeID::get(), + TypeID::get(), + name, + reinterpret_cast(&raw_destructor), + reinterpret_cast(&get_pointee)); + + } + + template + register_smart_ptr& cast(const char* methodName) { + using namespace internal; + typedef typename ReturnType::element_type ReturnPointeeType; + typedef typename PointerType::element_type PointeeType; + _embind_register_pointer_cast_method( + TypeID::get(), + methodName, + TypeID::get(), + reinterpret_cast(&performPointerCast)); + return *this; + } + }; //////////////////////////////////////////////////////////////////////////////// // VECTORS @@ -599,7 +640,6 @@ namespace emscripten { //////////////////////////////////////////////////////////////////////////////// // CLASSES //////////////////////////////////////////////////////////////////////////////// - // TODO: support class definitions without constructors. // TODO: support external class constructors template @@ -708,6 +748,18 @@ namespace emscripten { reinterpret_cast(&internal::FunctorInvoker::invoke)); return *this; } + + template + class_& cast(const char* methodName) { + using namespace internal; + + _embind_register_cast_method( + TypeID::get(), + methodName, + TypeID::get(), + reinterpret_cast(&performCast)); + return *this; + } }; //////////////////////////////////////////////////////////////////////////////// From 8b7abef9db95082d4791ce92f4b7b4a308e3ed25 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Mon, 5 Nov 2012 13:20:13 -0800 Subject: [PATCH 034/258] Returning an empty shared_ptr returns null to JavaScript. Similarly, passing null into a shared_ptr creates an empty shared_ptr. --- system/include/emscripten/wire.h | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 system/include/emscripten/wire.h diff --git a/system/include/emscripten/wire.h b/system/include/emscripten/wire.h old mode 100644 new mode 100755 From ad45b65333af729c8ffd99a8e5264e6d4d40b028 Mon Sep 17 00:00:00 2001 From: Todd Lee Date: Mon, 5 Nov 2012 14:16:46 -0800 Subject: [PATCH 035/258] Add support for std::vector. --- src/embind/embind.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index f491c1ddb..859181e79 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -505,7 +505,6 @@ function __embind_register_class( return h; }); - Handle.prototype.clone = function() { if (!this.ptr) { throw new BindingError(classType.name + ' instance already deleted'); From d0de75a62241931436b3902d5bab9d0942a1db6c Mon Sep 17 00:00:00 2001 From: mey Date: Fri, 9 Nov 2012 17:06:23 -0800 Subject: [PATCH 036/258] Exposing std::function to javascript. --- src/embind/embind.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/embind/embind.js b/src/embind/embind.js index 859181e79..e121732fb 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -505,6 +505,7 @@ function __embind_register_class( return h; }); + Handle.prototype.clone = function() { if (!this.ptr) { throw new BindingError(classType.name + ' instance already deleted'); From 87b760c334d3b3d1df4a1920f545930c790e2df6 Mon Sep 17 00:00:00 2001 From: Todd Lee Date: Mon, 5 Nov 2012 14:16:46 -0800 Subject: [PATCH 037/258] Add support for std::vector. --- system/include/emscripten/bind.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index ec3db2d24..7c59a0cee 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -612,7 +612,7 @@ namespace emscripten { methodName, TypeID::get(), reinterpret_cast(&performPointerCast)); - return *this; + return *this; } }; From ae311d2e8d84f0b2dc8347d3b29c500604c4ecfa Mon Sep 17 00:00:00 2001 From: Bill Welden Date: Mon, 26 Nov 2012 11:23:23 -0800 Subject: [PATCH 038/258] Allow raw pointer return values (requires allow_raw_pointer) --- src/embind/embind.js | 72 ++++++++++++++++---------------- system/include/emscripten/bind.h | 27 +++++------- system/include/emscripten/wire.h | 5 ++- 3 files changed, 49 insertions(+), 55 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index e121732fb..88a4ee8d4 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -154,7 +154,11 @@ 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); + if (i == 0) { + argTypeImpls[i] = requireRegisteredType(argType, name + " return value"); + } else { + argTypeImpls[i] = requireRegisteredType(argType, name + " parameter " + i); + } } return argTypeImpls; } @@ -167,30 +171,28 @@ function runDestructors(destructors) { } } -function makeInvoker(name, returnType, argCount, argTypes, invoker, fn) { +function makeInvoker(name, argCount, argTypes, invoker, fn) { return function() { - if (arguments.length !== argCount) { - throw new BindingError('function ' + name + ' called with ' + arguments.length + ' arguments, expected ' + argCount); + if (arguments.length !== argCount - 1) { + throw new BindingError('function ' + name + ' called with ' + arguments.length + ' arguments, expected ' + argCount - 1); } var destructors = []; - var args = new Array(argCount + 1); + var args = new Array(argCount); args[0] = fn; - for (var i = 0; i < argCount; ++i) { - args[i + 1] = argTypes[i].toWireType(destructors, arguments[i]); + for (var i = 1; i < argCount; ++i) { + args[i] = argTypes[i].toWireType(destructors, arguments[i-1]); } - var rv = returnType.fromWireType(invoker.apply(null, args)); + var rv = argTypes[0].fromWireType(invoker.apply(null, args)); runDestructors(destructors); return rv; }; } -function __embind_register_function(name, returnType, argCount, argTypes, invoker, fn) { +function __embind_register_function(name, argCount, argTypes, invoker, fn) { name = Pointer_stringify(name); - returnType = requireRegisteredType(returnType, "Function " + name + " return value"); invoker = FUNCTION_TABLE[invoker]; argTypes = requireArgumentTypes(argCount, argTypes, name); - - exposePublicSymbol(name, makeInvoker(name, returnType, argCount, argTypes, invoker, fn)); + exposePublicSymbol(name, makeInvoker(name, argCount, argTypes, invoker, fn)); } function __embind_register_tuple(tupleType, name, constructor, destructor) { @@ -558,6 +560,9 @@ function __embind_register_class( var pointerName = name + '*'; registerType(pointerType, pointerName, { name: pointerName, + fromWireType: function(ptr) { + return new Handle(ptr); + }, toWireType: function(destructors, o) { return o.ptr; } @@ -586,13 +591,13 @@ function __embind_register_class_constructor( 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); + if (arguments.length !== argCount - 1) { + throw new BindingError('emscripten binding ' + humanName + ' called with ' + arguments.length + ' arguments, expected ' + (argCount-1)); } var destructors = []; - var args = new Array(argCount); - for (var i = 0; i < argCount; ++i) { - args[i] = argTypes[i].toWireType(destructors, arguments[i]); + var args = new Array(argCount-1); + for (var i = 1; i < argCount; ++i) { + args[i-1] = argTypes[i].toWireType(destructors, arguments[i-1]); } var ptr = constructor.apply(null, args); @@ -605,7 +610,6 @@ function __embind_register_class_constructor( function __embind_register_class_method( classType, methodName, - returnType, argCount, argTypes, invoker, @@ -616,7 +620,6 @@ function __embind_register_class_method( 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); @@ -626,19 +629,19 @@ function __embind_register_class_method( 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); + if (arguments.length !== argCount - 1) { + throw new BindingError('emscripten binding method ' + humanName + ' called with ' + arguments.length + ' arguments, expected ' + (argCount-1)); } var destructors = []; - var args = new Array(argCount + 2); + var args = new Array(argCount + 1); args[0] = this.ptr; args[1] = memberFunction; - for (var i = 0; i < argCount; ++i) { - args[i + 2] = argTypes[i].toWireType(destructors, arguments[i]); + for (var i = 1; i < argCount; ++i) { + args[i + 1] = argTypes[i].toWireType(destructors, arguments[i-1]); } - var rv = returnType.fromWireType(invoker.apply(null, args)); + var rv = argTypes[0].fromWireType(invoker.apply(null, args)); runDestructors(destructors); return rv; }; @@ -705,7 +708,6 @@ function __embind_register_pointer_cast_method( function __embind_register_class_classmethod( classType, methodName, - returnType, argCount, argTypes, invoker, @@ -714,22 +716,18 @@ function __embind_register_class_classmethod( 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); invoker = FUNCTION_TABLE[invoker]; - - classType.constructor[methodName] = makeInvoker(humanName, returnType, argCount, argTypes, invoker, fn); + classType.constructor[methodName] = makeInvoker(humanName, argCount, argTypes, invoker, fn); } function __embind_register_class_operator_call( classType, - returnType, argCount, argTypes, invoker ) { classType = requireRegisteredType(classType, 'class'); - returnType = requireRegisteredType(returnType, 'method ' + humanName + ' return value'); argTypes = requireArgumentTypes(argCount, argTypes, 'method ' + humanName); invoker = FUNCTION_TABLE[invoker]; var humanName = classType.name + '.' + 'operator_call'; @@ -738,18 +736,18 @@ function __embind_register_class_operator_call( 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); + if (arguments.length !== argCount - 1) { + throw new BindingError('emscripten binding method ' + humanName + ' called with ' + arguments.length + ' arguments, expected ' + (argCount-1)); } var destructors = []; - var args = new Array(argCount + 1); + var args = new Array(argCount); args[0] = this.ptr; - for (var i = 0; i < argCount; ++i) { - args[i + 1] = argTypes[i].toWireType(destructors, arguments[i]); + for (var i = 1; i < argCount; ++i) { + args[i] = argTypes[i].toWireType(destructors, arguments[i-1]); } - var rv = returnType.fromWireType(invoker.apply(null, args)); + var rv = argTypes[0].fromWireType(invoker.apply(null, args)); runDestructors(destructors); return rv; }; diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index 7c59a0cee..081182595 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -48,7 +48,6 @@ namespace emscripten { void _embind_register_function( const char* name, - TYPEID returnType, unsigned argCount, TYPEID argTypes[], GenericFunction invoker, @@ -126,7 +125,6 @@ namespace emscripten { void _embind_register_class_method( TYPEID classType, const char* methodName, - TYPEID returnType, unsigned argCount, TYPEID argTypes[], GenericFunction invoker, @@ -157,7 +155,6 @@ namespace emscripten { void _embind_register_class_classmethod( TYPEID classType, const char* methodName, - TYPEID returnType, unsigned argCount, TYPEID argTypes[], GenericFunction invoker, @@ -165,7 +162,6 @@ namespace emscripten { void _embind_register_class_operator_call( TYPEID classType, - TYPEID returnType, unsigned argCount, TYPEID argTypes[], GenericFunction invoker @@ -206,7 +202,11 @@ namespace emscripten { template struct arg { - static constexpr int index = Index; + static constexpr int index = Index + 1; + }; + + struct ret_val { + static constexpr int index = 0; }; template @@ -282,10 +282,9 @@ namespace emscripten { registerStandardTypes(); - typename WithPolicies::template ArgTypeList args; + typename WithPolicies::template ArgTypeList args; _embind_register_function( name, - TypeID::get(), args.count, args.types, reinterpret_cast(&Invoker::invoke), @@ -661,7 +660,7 @@ namespace emscripten { class_& constructor(Policies...) { using namespace internal; - typename WithPolicies::template ArgTypeList args; + typename WithPolicies::template ArgTypeList args; _embind_register_class_constructor( TypeID::get(), args.count, @@ -674,11 +673,10 @@ namespace emscripten { class_& method(const char* methodName, ReturnType (ClassType::*memberFunction)(Args...), Policies...) { using namespace internal; - typename WithPolicies::template ArgTypeList args; + typename WithPolicies::template ArgTypeList args; _embind_register_class_method( TypeID::get(), methodName, - TypeID::get(), args.count, args.types, reinterpret_cast(&MethodInvoker::invoke), @@ -691,11 +689,10 @@ namespace emscripten { class_& method(const char* methodName, ReturnType (ClassType::*memberFunction)(Args...) const, Policies...) { using namespace internal; - typename WithPolicies::template ArgTypeList args; + typename WithPolicies::template ArgTypeList args; _embind_register_class_method( TypeID::get(), methodName, - TypeID::get(), args.count, args.types, reinterpret_cast(&ConstMethodInvoker::invoke), @@ -723,11 +720,10 @@ namespace emscripten { class_& classmethod(const char* methodName, ReturnType (*classMethod)(Args...), Policies...) { using namespace internal; - typename WithPolicies::template ArgTypeList args; + typename WithPolicies::template ArgTypeList args; _embind_register_class_classmethod( TypeID::get(), methodName, - TypeID::get(), args.count, args.types, reinterpret_cast(&internal::Invoker::invoke), @@ -739,10 +735,9 @@ namespace emscripten { class_& calloperator(Policies...) { using namespace internal; - typename WithPolicies::template ArgTypeList args; + typename WithPolicies::template ArgTypeList args; _embind_register_class_operator_call( TypeID::get(), - TypeID::get(), args.count, args.types, reinterpret_cast(&internal::FunctorInvoker::invoke)); diff --git a/system/include/emscripten/wire.h b/system/include/emscripten/wire.h index 99c4f926d..bb70f26c6 100755 --- a/system/include/emscripten/wire.h +++ b/system/include/emscripten/wire.h @@ -104,7 +104,6 @@ namespace emscripten { template static void fill(TYPEID* argTypes) { typedef typename ExecutePolicies::template With::type TransformT; - *argTypes = TypeID::get(); return ArgTypes::template fill(argTypes + 1); } @@ -204,7 +203,9 @@ namespace emscripten { template struct BindingType { typedef T* WireType; - + static WireType toWireType(T* p) { + return p; + } static T* fromWireType(WireType wt) { return wt; } From 50c29dc01ec880fbd8f708be7a3c9c865645d46f Mon Sep 17 00:00:00 2001 From: Bill Welden Date: Mon, 26 Nov 2012 12:22:24 -0800 Subject: [PATCH 039/258] Commit hook fix. --- src/embind/embind.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 88a4ee8d4..1cae55ffc 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -154,7 +154,7 @@ function requireArgumentTypes(argCount, argTypes, name) { var argTypeImpls = new Array(argCount); for (var i = 0; i < argCount; ++i) { var argType = HEAP32[(argTypes >> 2) + i]; - if (i == 0) { + if (i === 0) { argTypeImpls[i] = requireRegisteredType(argType, name + " return value"); } else { argTypeImpls[i] = requireRegisteredType(argType, name + " parameter " + i); From ab18c1107d8493b6a1ef0c19b5210a756cb4709c Mon Sep 17 00:00:00 2001 From: Todd Lee Date: Mon, 26 Nov 2012 16:49:52 -0800 Subject: [PATCH 040/258] there were some incorrect handling of memory alloc/dealloc when passing around vector of smart pointer (or whatever type that needs manual deletion) --- src/embind/embind.js | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 1cae55ffc..504db37cd 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -456,8 +456,21 @@ function __embind_register_vector( name: name, fromWireType: function(ptr) { var arr = []; - var n = length(ptr); + Object.defineProperty(arr, 'delete', { + writable: false, + enumerable: false, + configurable: false, + value: function() { + var needsToBeDeleted = elementType.hasOwnProperty('Handle'); + for (var i = 0; i < arr.length; i++) { + if (needsToBeDeleted) { + arr[i].delete(); + } + } + } + }); + var n = length(ptr); for (var i = 0; i < n; i++) { var v = elementType.fromWireType(getter(ptr, i)); arr.push(v); @@ -471,6 +484,8 @@ function __embind_register_vector( for (var val in o) { setter(vec, elementType.toWireType(destructors, o[val])); } + runDestructors(destructors); + destructors.push(destructor); destructors.push(vec); return vec; From 1b9928686e2a3bd0d6394d79cab7b110cf35990f Mon Sep 17 00:00:00 2001 From: Bill Welden Date: Tue, 27 Nov 2012 10:54:41 -0800 Subject: [PATCH 041/258] Automatic downcasting of function return values (first cut -- multiple inheritance still not supported). --- src/embind/embind.js | 139 +++++++++++++++++++++++++++++++ system/include/emscripten/bind.h | 8 +- 2 files changed, 146 insertions(+), 1 deletion(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 504db37cd..b50c7aca6 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -493,15 +493,21 @@ function __embind_register_vector( }); } +// hang onto your hats, guys, we're going to try to make this one registration work for the class and all its +// derived classes function __embind_register_class( classType, pointerType, constPointerType, name, + getDynamicPointerType, destructor ) { name = Pointer_stringify(name); destructor = FUNCTION_TABLE[destructor]; + if (getDynamicPointerType) { + getDynamicPointerType = FUNCTION_TABLE[getDynamicPointerType]; + } var Handle = createNamedFunction(name, function(ptr) { var h = function() { @@ -562,6 +568,7 @@ function __embind_register_class( registerType(classType, name, { name: name, + pointerType: pointerType, constructor: constructor, Handle: Handle, fromWireType: function(ptr) { @@ -576,6 +583,19 @@ function __embind_register_class( registerType(pointerType, pointerName, { name: pointerName, fromWireType: function(ptr) { + var dynamicType = getDynamicPointerType && getDynamicPointerType(ptr); + if (dynamicType === null || dynamicType === pointerType) { + return new Handle(ptr); + } + try { + dynamicType = requireRegisteredType(dynamicType); + } catch (err) { + return new Handle(ptr); // I suppose we could work our way up the inheritance tree... + } + dynamicType = requireRegisteredType(dynamicType.pointerType); + return dynamicType.fromWireTypeStatic(ptr); + }, + fromWireTypeStatic: function(ptr) { return new Handle(ptr); }, toWireType: function(destructors, o) { @@ -586,6 +606,19 @@ function __embind_register_class( var constPointerName = name + ' const*'; registerType(constPointerType, constPointerName, { name: constPointerName, + fromWireType: function(ptr) { + var dynamicType = getDynamicPointerType && getDynamicPointerType(ptr); + if (dynamicType === null || dynamicType === pointerType) { + return new Handle(ptr); + } + try { + dynamicType = requireRegisteredType(dynamicType); + } catch (err) { + return new Handle(ptr); // I suppose we could work our way up the inheritance tree... + } + dynamicType = requireRegisteredType(dynamicType.pointerType); + return dynamicType.fromWireType(ptr); + }, toWireType: function(destructors, o) { return o.ptr; } @@ -594,6 +627,112 @@ function __embind_register_class( exposePublicSymbol(name, constructor); } +//function __embind_register_class( +// classType, +// pointerType, +// constPointerType, +// name, +// destructor +//) { +// name = Pointer_stringify(name); +// destructor = FUNCTION_TABLE[destructor]; +// +// var Handle = createNamedFunction(name, function(ptr) { +// var h = function() { +// if(h.operator_call !== undefined) { +// return h.operator_call.apply(h, arguments); +// } else { +// throw new BindingError(name + ' does not define call operator'); +// } +// }; +// +// h.count = {value: 1}; +// h.ptr = ptr; +// +// for(var prop in Handle.prototype) { +// var dp = Object.getOwnPropertyDescriptor(Handle.prototype, prop); +// Object.defineProperty(h, prop, dp); +// } +// +// return h; +// }); +// +// 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; +// return body.apply(this, arguments); +// }); +// constructor.prototype = Handle.prototype; +// +// registerType(classType, name, { +// name: name, +// constructor: constructor, +// Handle: Handle, +// fromWireType: function(ptr) { +// return new Handle(ptr); +// }, +// toWireType: function(destructors, o) { +// return o.ptr; +// } +// }); +// +// var pointerName = name + '*'; +// registerType(pointerType, pointerName, { +// name: pointerName, +// fromWireType: function(ptr) { +// // based on the fully downcast type of the pointer, +// +// return new Handle(ptr); // if me +// }, +// toWireType: function(destructors, o) { +// return o.ptr; +// } +// }); +// +// var constPointerName = name + ' const*'; +// registerType(constPointerType, constPointerName, { +// name: constPointerName, +// fromWireType: function(ptr) { +// return new Handle(ptr); +// }, +// toWireType: function(destructors, o) { +// return o.ptr; +// } +// }); +// +// exposePublicSymbol(name, constructor); +//} +// function __embind_register_class_constructor( classType, argCount, diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index 081182595..3dc6b5e73 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -114,6 +114,7 @@ namespace emscripten { TYPEID pointerType, TYPEID constPointerType, const char* className, + GenericFunction getDynamicTypeInfo, GenericFunction destructor); void _embind_register_class_constructor( @@ -265,12 +266,16 @@ namespace emscripten { // FUNCTIONS //////////////////////////////////////////////////////////////////////////////// - template ToType& performCast(FromType& from) { return *dynamic_cast(&from); }; + template + internal::TYPEID getDynamicPointerType(PointerType *p) { + return reinterpret_cast(&typeid(*p)); + }; + template std::shared_ptr performPointerCast(std::shared_ptr from) { return std::shared_ptr(from, dynamic_cast(from.get())); @@ -653,6 +658,7 @@ namespace emscripten { TypeID>::get(), TypeID>::get(), name, + reinterpret_cast(&getDynamicPointerType), reinterpret_cast(&raw_destructor)); } From d161fccc9b662f073fd53d9de1d46f706de5ae10 Mon Sep 17 00:00:00 2001 From: Todd Lee Date: Wed, 28 Nov 2012 13:05:37 -0800 Subject: [PATCH 042/258] added two utility functions to emval. - check if a given key exists. - return length of an array. --- src/embind/emval.js | 13 +++++++++++++ system/include/emscripten/val.h | 10 ++++++++++ 2 files changed, 23 insertions(+) diff --git a/src/embind/emval.js b/src/embind/emval.js index f74c0fd54..a3157d2a7 100644 --- a/src/embind/emval.js +++ b/src/embind/emval.js @@ -53,6 +53,10 @@ function __emval_new_cstring(str) { return __emval_register(Pointer_stringify(str)); } +function __emval_has_property(handle, k) { + return _emval_handle_array[handle].value.hasOwnProperty(k); +} + function __emval_get_property(handle, k) { k = Pointer_stringify(k); return __emval_register(_emval_handle_array[handle].value[k]); @@ -66,6 +70,15 @@ function __emval_get_property_by_unsigned_long(handle, k) { return __emval_register(_emval_handle_array[handle].value[k]); } +function __emval_get_length(handle) { + var val = _emval_handle_array[handle].value; + if (Object.prototype.toString.call(val) === "[object Array]") { + return val.length; + } + + return 0; +} + function __emval_eval_global_method(handle, objectName, methodName) { var objectNameStr = Pointer_stringify(objectName); var methodNameStr = Pointer_stringify(methodName); diff --git a/system/include/emscripten/val.h b/system/include/emscripten/val.h index 68f5b63e3..945b0d8a6 100644 --- a/system/include/emscripten/val.h +++ b/system/include/emscripten/val.h @@ -14,12 +14,14 @@ namespace emscripten { EM_VAL _emval_new_object(); EM_VAL _emval_new_long(long value); EM_VAL _emval_new_cstring(const char* str); + bool _emval_has_property(EM_VAL object, const char* key); 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); EM_VAL _emval_eval_global_method(EM_VAL object, const char* objectName, const char* methodName); 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); + unsigned int _emval_get_length(EM_VAL object); void _emval_as(EM_VAL value, TYPEID returnType); EM_VAL _emval_call( EM_VAL value, @@ -78,6 +80,10 @@ namespace emscripten { return *this; } + bool exist(const char* key) const { + return internal::_emval_has_property(handle, key); + } + val get(const char* key) const { return val(internal::_emval_get_property(handle, key)); } @@ -111,6 +117,10 @@ namespace emscripten { internal::_emval_set_property_by_int(handle, key, v.handle); } + unsigned int length() { + return internal::_emval_get_length(handle); + } + template val operator()(Args... args) { using namespace internal; From 88b63143fa199f6cf93d10434df9e9b0740c6895 Mon Sep 17 00:00:00 2001 From: jinsuck Date: Wed, 28 Nov 2012 13:44:41 -0800 Subject: [PATCH 043/258] Upgrade libcxx from 11-month-ago version to the latest. The new version has memory leak fixed in the weak_ptr. Note: to make this compile, I made two modifications (actually copied from the previous version) -- system/include/libcxx/__locale line 28 and system/include/libcxx/locale line 229. --- system/include/emscripten/wire.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/system/include/emscripten/wire.h b/system/include/emscripten/wire.h index bb70f26c6..ec302d3ab 100755 --- a/system/include/emscripten/wire.h +++ b/system/include/emscripten/wire.h @@ -7,6 +7,8 @@ // // We'll call the on-the-wire type WireType. +#include + namespace emscripten { namespace internal { typedef const struct _TYPEID* TYPEID; From 73d2de4105fd8f7200fe42e868e53fe2378fab83 Mon Sep 17 00:00:00 2001 From: Todd Lee Date: Wed, 28 Nov 2012 15:18:07 -0800 Subject: [PATCH 044/258] use simpler way to get length of an array --- src/embind/emval.js | 9 --------- system/include/emscripten/val.h | 3 +-- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/src/embind/emval.js b/src/embind/emval.js index a3157d2a7..65becfa34 100644 --- a/src/embind/emval.js +++ b/src/embind/emval.js @@ -70,15 +70,6 @@ function __emval_get_property_by_unsigned_long(handle, k) { return __emval_register(_emval_handle_array[handle].value[k]); } -function __emval_get_length(handle) { - var val = _emval_handle_array[handle].value; - if (Object.prototype.toString.call(val) === "[object Array]") { - return val.length; - } - - return 0; -} - function __emval_eval_global_method(handle, objectName, methodName) { var objectNameStr = Pointer_stringify(objectName); var methodNameStr = Pointer_stringify(methodName); diff --git a/system/include/emscripten/val.h b/system/include/emscripten/val.h index 945b0d8a6..8db052e8c 100644 --- a/system/include/emscripten/val.h +++ b/system/include/emscripten/val.h @@ -21,7 +21,6 @@ namespace emscripten { EM_VAL _emval_eval_global_method(EM_VAL object, const char* objectName, const char* methodName); 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); - unsigned int _emval_get_length(EM_VAL object); void _emval_as(EM_VAL value, TYPEID returnType); EM_VAL _emval_call( EM_VAL value, @@ -118,7 +117,7 @@ namespace emscripten { } unsigned int length() { - return internal::_emval_get_length(handle); + return get("length").as(); } template From 026a4d49e496339c0f31b79ca3b399c53dac5f7f Mon Sep 17 00:00:00 2001 From: jinsuck Date: Fri, 30 Nov 2012 14:38:54 -0800 Subject: [PATCH 045/258] support creating val with null (needed for sending null parameter to a WebGL function) --- src/embind/emval.js | 4 ++++ system/include/emscripten/val.h | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/src/embind/emval.js b/src/embind/emval.js index 65becfa34..e8ee6baf5 100644 --- a/src/embind/emval.js +++ b/src/embind/emval.js @@ -45,6 +45,10 @@ function __emval_new_object() { return __emval_register({}); } +function __emval_new_null() { + return __emval_register(null); +} + function __emval_new_long(value) { return __emval_register(value); } diff --git a/system/include/emscripten/val.h b/system/include/emscripten/val.h index 8db052e8c..e2d353823 100644 --- a/system/include/emscripten/val.h +++ b/system/include/emscripten/val.h @@ -12,6 +12,7 @@ namespace emscripten { void _emval_incref(EM_VAL value); void _emval_decref(EM_VAL value); EM_VAL _emval_new_object(); + EM_VAL _emval_new_null(); EM_VAL _emval_new_long(long value); EM_VAL _emval_new_cstring(const char* str); bool _emval_has_property(EM_VAL object, const char* key); @@ -48,6 +49,10 @@ namespace emscripten { return val(internal::_emval_new_object()); }; + static val null() { + return val(internal::_emval_new_null()); + }; + static val take_ownership(internal::EM_VAL e) { return val(e); } From 15603bbafc0aa68f9d8d843e083b89bfc566533d Mon Sep 17 00:00:00 2001 From: mey Date: Fri, 30 Nov 2012 14:56:58 -0800 Subject: [PATCH 046/258] Switching vector class_ instead of bound as an explicit type. --- src/embind/embind.js | 69 +++++++++++++++++- system/include/emscripten/bind.h | 121 +++++++++++++++++++++++++------ system/include/emscripten/wire.h | 27 ++++--- 3 files changed, 179 insertions(+), 38 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index b50c7aca6..fa3649003 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -495,6 +495,7 @@ function __embind_register_vector( // hang onto your hats, guys, we're going to try to make this one registration work for the class and all its // derived classes + function __embind_register_class( classType, pointerType, @@ -882,9 +883,10 @@ function __embind_register_class_operator_call( invoker ) { classType = requireRegisteredType(classType, 'class'); + var humanName = classType.name + '.' + 'operator_call'; argTypes = requireArgumentTypes(argCount, argTypes, 'method ' + humanName); invoker = FUNCTION_TABLE[invoker]; - var humanName = classType.name + '.' + 'operator_call'; + classType.Handle.prototype.operator_call = function() { if (!this.ptr) { @@ -907,6 +909,71 @@ function __embind_register_class_operator_call( }; } +function __embind_register_class_operator_array_get( + classType, + elementType, + indexType, + invoker +) { + classType = requireRegisteredType(classType, 'class'); + indexType = requireRegisteredType(indexType, 'array access index ' + classType.name); + elementType = requireRegisteredType(elementType, 'array access element' + classType.name); + invoker = FUNCTION_TABLE[invoker]; + var humanName = classType.name + '.' + 'operator_array_get'; + + classType.Handle.prototype.array_get = function() { + if (!this.ptr) { + throw new BindingError('cannot call emscripten binding method ' + humanName + ' on deleted object'); + } + + if (arguments.length !== 1) { + throw new BindingError('emscripten binding method ' + humanName + ' called with ' + arguments.length + ' arguments, expected ' + 1); + } + + var destructors = []; + var args = new Array(2); + args[0] = this.ptr; + args[1] = indexType.toWireType(destructors, arguments[0]); + + var rv = elementType.fromWireType(invoker.apply(null, args)); + runDestructors(destructors); + return rv; + }; +} + +function __embind_register_class_operator_array_set( + classType, + elementType, + indexType, + invoker +) { + classType = requireRegisteredType(classType, 'class'); + indexType = requireRegisteredType(indexType, 'array access index ' + classType.name); + elementType = requireRegisteredType(elementType, 'vector ' + classType.name); + invoker = FUNCTION_TABLE[invoker]; + var humanName = classType.name + '.' + 'operator_array_get'; + + classType.Handle.prototype.array_set = function() { + if (!this.ptr) { + throw new BindingError('cannot call emscripten binding method ' + humanName + ' on deleted object'); + } + + if (arguments.length !== 2) { + throw new BindingError('emscripten binding method ' + humanName + ' called with ' + arguments.length + ' arguments, expected ' + 2); + } + + var destructors = []; + var args = new Array(2); + args[0] = this.ptr; + args[1] = indexType.toWireType(destructors, arguments[0]); + args[2] = elementType.toWireType(destructors, arguments[1]); + + var rv = elementType.fromWireType(invoker.apply(null, args)); + runDestructors(destructors); + return rv; + }; +} + function __embind_register_class_field( classType, fieldName, diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index 3dc6b5e73..6b8b6f101 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -165,8 +165,19 @@ namespace emscripten { TYPEID classType, unsigned argCount, TYPEID argTypes[], - GenericFunction invoker - ); + GenericFunction invoker); + + void _embind_register_class_operator_array_get( + TYPEID classType, + TYPEID elementType, + TYPEID indexType, + GenericFunction invoker); + + void _embind_register_class_operator_array_set( + TYPEID classType, + TYPEID elementType, + TYPEID indexType, + GenericFunction invoker); void _embind_register_enum( TYPEID enumType, @@ -341,6 +352,30 @@ namespace emscripten { } }; + template + struct ArrayAccessGetInvoker { + static typename internal::BindingType::WireType invoke( + ClassType* ptr, + size_t index, + typename internal::BindingType + ) { + return internal::BindingType::toWireType( + (*ptr)[index] + ); + } + }; + + template + struct ArrayAccessSetInvoker { + static void invoke( + ClassType* ptr, + size_t index, + typename internal::BindingType::WireType item + ) { + (*ptr)[index] = internal::BindingType::fromWireType(item); + } + }; + template struct MethodInvoker { typedef ReturnType (ClassType::*MemberPointer)(Args...); @@ -620,27 +655,6 @@ namespace emscripten { } }; - //////////////////////////////////////////////////////////////////////////////// - // VECTORS - //////////////////////////////////////////////////////////////////////////////// - - template - inline void register_vector(const char* name) { - typedef typename VectorType::value_type ElementType; - - internal::registerStandardTypes(); - internal::_embind_register_vector( - internal::TypeID::get(), - internal::TypeID::get(), - name, - reinterpret_cast(&internal::raw_constructor), - reinterpret_cast(&internal::raw_destructor), - reinterpret_cast(&internal::Vector::length), - reinterpret_cast(&internal::Vector::getAt), - reinterpret_cast(&internal::Vector::push_back) - ); - } - //////////////////////////////////////////////////////////////////////////////// // CLASSES //////////////////////////////////////////////////////////////////////////////// @@ -750,6 +764,44 @@ namespace emscripten { return *this; } + + /* + * void _embind_register_class_operator_array_get( + TYPEID classType, + TYPEID elementType, + GenericFunction invoker); + + void _embind_register_class_operator_array_set( + TYPEID classType, + TYPEID elementType, + GenericFunction invoker); + + ArrayAccessSetInvoker + */ + + template + class_& arrayoperatorget() { + using namespace internal; + + _embind_register_class_operator_array_get( + TypeID::get(), + TypeID::get(), + TypeID::get(), + reinterpret_cast(&internal::ArrayAccessGetInvoker::invoke)); + } + + template + class_& arrayoperatorset() { + using namespace internal; + + _embind_register_class_operator_array_set( + TypeID::get(), + TypeID::get(), + TypeID::get(), + reinterpret_cast(&internal::ArrayAccessSetInvoker::invoke)); + return *this; + } + template class_& cast(const char* methodName) { using namespace internal; @@ -763,6 +815,29 @@ namespace emscripten { } }; + //////////////////////////////////////////////////////////////////////////////// + // VECTORS + //////////////////////////////////////////////////////////////////////////////// + template + class_> register_vector(const char* name) { + using namespace std; + typedef vector VecType; + typedef typename vector::iterator IterType; + typedef typename vector::const_iterator ConstIterType; + + void (VecType::*push_back)(const T&) = &VecType::push_back; + const T& (VecType::*at)(size_t) const = &VecType::at; + auto c = class_>(name) + .template constructor<>() + .method("push_back", push_back) + .method("at", at) + .method("size", &vector::size) + .template arrayoperatorget() + .template arrayoperatorset() + ; + return c; + } + //////////////////////////////////////////////////////////////////////////////// // ENUMS //////////////////////////////////////////////////////////////////////////////// diff --git a/system/include/emscripten/wire.h b/system/include/emscripten/wire.h index ec302d3ab..5a2deb7a9 100755 --- a/system/include/emscripten/wire.h +++ b/system/include/emscripten/wire.h @@ -133,19 +133,18 @@ namespace emscripten { template struct BindingType; -#define EMSCRIPTEN_DEFINE_NATIVE_BINDING_TYPE(type) \ - template<> \ - struct BindingType { \ - typedef type WireType; \ - \ - constexpr static WireType toWireType(type v) { \ - return v; \ - } \ - constexpr static type fromWireType(WireType v) { \ - return v; \ - } \ - static void destroy(WireType) { \ - } \ +#define EMSCRIPTEN_DEFINE_NATIVE_BINDING_TYPE(type) \ + template<> \ + struct BindingType { \ + typedef type WireType; \ + constexpr static WireType toWireType(const type& v) { \ + return v; \ + } \ + constexpr static type fromWireType(WireType v) { \ + return v; \ + } \ + static void destroy(WireType) { \ + } \ } EMSCRIPTEN_DEFINE_NATIVE_BINDING_TYPE(char); @@ -276,7 +275,7 @@ namespace emscripten { }; static WireType toWireType(T v) { - return new T(v); + return new ActualT(v); } static Marshaller fromWireType(WireType p) { From b5d84da03b83039a382494e2cfd1a125f0fb7199 Mon Sep 17 00:00:00 2001 From: mey Date: Fri, 30 Nov 2012 15:16:21 -0800 Subject: [PATCH 047/258] Removing ability to return by reference: dangerous. --- system/include/emscripten/wire.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/include/emscripten/wire.h b/system/include/emscripten/wire.h index 5a2deb7a9..1173034f7 100755 --- a/system/include/emscripten/wire.h +++ b/system/include/emscripten/wire.h @@ -275,7 +275,7 @@ namespace emscripten { }; static WireType toWireType(T v) { - return new ActualT(v); + return new T(v); } static Marshaller fromWireType(WireType p) { From c9eb75d64ab5735f15012c5839980edf3f1dd2ee Mon Sep 17 00:00:00 2001 From: mey Date: Mon, 3 Dec 2012 16:25:17 -0800 Subject: [PATCH 048/258] adding function to convert JS array to vector. --- system/include/emscripten/bind.h | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index 6b8b6f101..281ae85be 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -764,21 +764,6 @@ namespace emscripten { return *this; } - - /* - * void _embind_register_class_operator_array_get( - TYPEID classType, - TYPEID elementType, - GenericFunction invoker); - - void _embind_register_class_operator_array_set( - TYPEID classType, - TYPEID elementType, - GenericFunction invoker); - - ArrayAccessSetInvoker - */ - template class_& arrayoperatorget() { using namespace internal; @@ -818,6 +803,18 @@ namespace emscripten { //////////////////////////////////////////////////////////////////////////////// // VECTORS //////////////////////////////////////////////////////////////////////////////// + template + std::vector vecFromJSArray(val v) { + auto l = v.get("length").as(); + + std::vector rv; + for(unsigned i = 0; i < l; ++i) { + rv.push_back(v.get(i).as()); + } + + return rv; + }; + template class_> register_vector(const char* name) { using namespace std; @@ -835,6 +832,7 @@ namespace emscripten { .template arrayoperatorget() .template arrayoperatorset() ; + return c; } From 1276f9f5b73b10c914aa8c2c1a675be526b57480 Mon Sep 17 00:00:00 2001 From: mey Date: Mon, 3 Dec 2012 17:26:59 -0800 Subject: [PATCH 049/258] Some additional clean up. --- src/embind/embind.js | 2 +- system/include/emscripten/bind.h | 10 ---------- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index fa3649003..45341700c 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -949,7 +949,7 @@ function __embind_register_class_operator_array_set( ) { classType = requireRegisteredType(classType, 'class'); indexType = requireRegisteredType(indexType, 'array access index ' + classType.name); - elementType = requireRegisteredType(elementType, 'vector ' + classType.name); + elementType = requireRegisteredType(elementType, 'array access element ' + classType.name); invoker = FUNCTION_TABLE[invoker]; var humanName = classType.name + '.' + 'operator_array_get'; diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index 281ae85be..96e8dda5a 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -99,16 +99,6 @@ namespace emscripten { GenericFunction destructor, GenericFunction getPointee); - void _embind_register_vector( - TYPEID vectorType, - TYPEID elementType, - const char* name, - GenericFunction constructor, - GenericFunction destructor, - GenericFunction length, - GenericFunction getter, - GenericFunction setter); - void _embind_register_class( TYPEID classType, TYPEID pointerType, From 03d90a32db03078ff2666b4d8ca63d5f804e57f3 Mon Sep 17 00:00:00 2001 From: Bill Welden Date: Tue, 4 Dec 2012 10:56:59 -0800 Subject: [PATCH 050/258] More work toward dynamic downcasting of pointers. --- src/embind/embind.js | 136 ++----------------------------- system/include/emscripten/bind.h | 12 ++- system/lib/embind/bind.cpp | 102 +++++++++++++++++++++++ 3 files changed, 113 insertions(+), 137 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 45341700c..7f4e0d2e2 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -493,23 +493,16 @@ function __embind_register_vector( }); } -// hang onto your hats, guys, we're going to try to make this one registration work for the class and all its -// derived classes - function __embind_register_class( classType, pointerType, constPointerType, name, - getDynamicPointerType, destructor ) { name = Pointer_stringify(name); destructor = FUNCTION_TABLE[destructor]; - if (getDynamicPointerType) { - getDynamicPointerType = FUNCTION_TABLE[getDynamicPointerType]; - } - + var Handle = createNamedFunction(name, function(ptr) { var h = function() { if(h.operator_call !== undefined) { @@ -521,7 +514,7 @@ function __embind_register_class( h.count = {value: 1}; h.ptr = ptr; - + for(var prop in Handle.prototype) { var dp = Object.getOwnPropertyDescriptor(Handle.prototype, prop); Object.defineProperty(h, prop, dp); @@ -566,6 +559,8 @@ function __embind_register_class( return body.apply(this, arguments); }); constructor.prototype = Handle.prototype; + constructor.classType = classType; + registerType(classType, name, { name: name, @@ -584,14 +579,14 @@ function __embind_register_class( registerType(pointerType, pointerName, { name: pointerName, fromWireType: function(ptr) { - var dynamicType = getDynamicPointerType && getDynamicPointerType(ptr); + var dynamicType = ___getDynamicPointerType(ptr); // !!! this won't work if pointer is not dynamic if (dynamicType === null || dynamicType === pointerType) { return new Handle(ptr); } try { dynamicType = requireRegisteredType(dynamicType); } catch (err) { - return new Handle(ptr); // I suppose we could work our way up the inheritance tree... + return new Handle(ptr); } dynamicType = requireRegisteredType(dynamicType.pointerType); return dynamicType.fromWireTypeStatic(ptr); @@ -607,19 +602,6 @@ function __embind_register_class( var constPointerName = name + ' const*'; registerType(constPointerType, constPointerName, { name: constPointerName, - fromWireType: function(ptr) { - var dynamicType = getDynamicPointerType && getDynamicPointerType(ptr); - if (dynamicType === null || dynamicType === pointerType) { - return new Handle(ptr); - } - try { - dynamicType = requireRegisteredType(dynamicType); - } catch (err) { - return new Handle(ptr); // I suppose we could work our way up the inheritance tree... - } - dynamicType = requireRegisteredType(dynamicType.pointerType); - return dynamicType.fromWireType(ptr); - }, toWireType: function(destructors, o) { return o.ptr; } @@ -628,112 +610,6 @@ function __embind_register_class( exposePublicSymbol(name, constructor); } -//function __embind_register_class( -// classType, -// pointerType, -// constPointerType, -// name, -// destructor -//) { -// name = Pointer_stringify(name); -// destructor = FUNCTION_TABLE[destructor]; -// -// var Handle = createNamedFunction(name, function(ptr) { -// var h = function() { -// if(h.operator_call !== undefined) { -// return h.operator_call.apply(h, arguments); -// } else { -// throw new BindingError(name + ' does not define call operator'); -// } -// }; -// -// h.count = {value: 1}; -// h.ptr = ptr; -// -// for(var prop in Handle.prototype) { -// var dp = Object.getOwnPropertyDescriptor(Handle.prototype, prop); -// Object.defineProperty(h, prop, dp); -// } -// -// return h; -// }); -// -// 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; -// return body.apply(this, arguments); -// }); -// constructor.prototype = Handle.prototype; -// -// registerType(classType, name, { -// name: name, -// constructor: constructor, -// Handle: Handle, -// fromWireType: function(ptr) { -// return new Handle(ptr); -// }, -// toWireType: function(destructors, o) { -// return o.ptr; -// } -// }); -// -// var pointerName = name + '*'; -// registerType(pointerType, pointerName, { -// name: pointerName, -// fromWireType: function(ptr) { -// // based on the fully downcast type of the pointer, -// -// return new Handle(ptr); // if me -// }, -// toWireType: function(destructors, o) { -// return o.ptr; -// } -// }); -// -// var constPointerName = name + ' const*'; -// registerType(constPointerType, constPointerName, { -// name: constPointerName, -// fromWireType: function(ptr) { -// return new Handle(ptr); -// }, -// toWireType: function(destructors, o) { -// return o.ptr; -// } -// }); -// -// exposePublicSymbol(name, constructor); -//} -// function __embind_register_class_constructor( classType, argCount, diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index 96e8dda5a..54c017ef0 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -104,7 +104,6 @@ namespace emscripten { TYPEID pointerType, TYPEID constPointerType, const char* className, - GenericFunction getDynamicTypeInfo, GenericFunction destructor); void _embind_register_class_constructor( @@ -267,16 +266,16 @@ namespace emscripten { // FUNCTIONS //////////////////////////////////////////////////////////////////////////////// + extern "C" { + int __getDynamicPointerType(int p); + int __dynamicPointerCast(int p, int from, int to); + } + template ToType& performCast(FromType& from) { return *dynamic_cast(&from); }; - template - internal::TYPEID getDynamicPointerType(PointerType *p) { - return reinterpret_cast(&typeid(*p)); - }; - template std::shared_ptr performPointerCast(std::shared_ptr from) { return std::shared_ptr(from, dynamic_cast(from.get())); @@ -662,7 +661,6 @@ namespace emscripten { TypeID>::get(), TypeID>::get(), name, - reinterpret_cast(&getDynamicPointerType), reinterpret_cast(&raw_destructor)); } diff --git a/system/lib/embind/bind.cpp b/system/lib/embind/bind.cpp index befce08b1..c7c1f4b44 100755 --- a/system/lib/embind/bind.cpp +++ b/system/lib/embind/bind.cpp @@ -1,7 +1,43 @@ #include +#include <..\lib\libcxxabi\src\private_typeinfo.h> +#include +#include +#include using namespace emscripten; +namespace __cxxabiv1 { + std::vector __getBaseClasses(const __class_type_info* cti) { + std::vector bases; + + const __si_class_type_info* scti = dynamic_cast(cti); + if (scti) { + bases.emplace_back(scti->__base_type); + } else { + const __vmi_class_type_info* vcti = dynamic_cast(cti); + if (vcti) { + for (int i = 0; i < vcti->__base_count; i++) { + bases.emplace_back(vcti->__base_info[i].__base_type); + } + } + } + return bases; + } + + void __getDerivationPaths1(const __class_type_info* dv, const __class_type_info* bs, std::vectorpath, std::vector>& paths) { + std::vector newPath(path); + newPath.emplace_back(dv); + if (dv == bs) { + paths.emplace_back(newPath); + } else { + std::vector bases = __getBaseClasses(dv); + for (int i = 0; i < bases.size(); i++) { + __getDerivationPaths1(bases[i], bs, newPath, paths); + } + } + } +} + namespace emscripten { namespace internal { void registerStandardTypes() { @@ -30,5 +66,71 @@ namespace emscripten { _embind_register_emval(TypeID::get(), "emscripten::val"); } } + + extern "C" { + // These three routines constitute an extension to the compiler's support for dynamic type conversion. + // They are used by embind.js to implement automatic downcasting of return values which are pointers to + // polymorphic objects. + + // __getDerivationPaths returns an array of arrays of type_info pointers (cast as integers to make + // the Javascript bindings simpler). Each element of the outer array is an array of type_info pointers + // identifying a derivation path from the derived type to the base type. If either of the type info + // pointer paramters does not specify a pointer to a class, or if there is no path from the derived type + // to the base type, this routine returns zero. + std::vector> __getDerivationPaths(int dv, const int bs) { + std::vector> paths; + + 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(dv1); + const __cxxabiv1::__class_type_info* bs2 = dynamic_cast(bs1); + + if (dv2 && bs2) { + __cxxabiv1::__getDerivationPaths1(dv2, bs2, std::vector(), paths); + } + + std::vector> pathsAsTypeInfo; + for (int i = 0; i < paths.size(); i++) { + std::vector pathAsTypeInfo; + for (int j = 0; j < paths[i].size(); j++) { + pathAsTypeInfo.emplace_back((int)paths[i][j]); + } + pathsAsTypeInfo.emplace_back(pathAsTypeInfo); + } + + return pathsAsTypeInfo; + } + + // We bind __getDerivationPaths in order to take advantage of the std::vector to Javascript array + // conversion for the return value. This has the unfortunate side-effect of exposing it to third party + // developers, but perhaps the double underscore will scare them away from calling it. + EMSCRIPTEN_BINDINGS(([]() { + function("__getDerivationPaths", &__getDerivationPaths); + })); + + // __getDynamicPointerType returns (for polymorphic types only!) the type of the instance actually + // pointed to. + int EMSCRIPTEN_KEEPALIVE __getDynamicPointerType(int p) { + void** vtable = *(void***)p; + return (int)static_cast(vtable[-1]); + } + + // Calls to __dynamic_cast are generated by the compiler to implement dynamic_cast<>() -- its prototype is + // not available through any header file. It is called directly here because it allows run-time + // specification of the target pointer type (which must be specified at compile time when using + // dynamic_cast<>(). + void* __dynamic_cast(void*, const std::type_info*, const std::type_info*, int); + + // __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) { + // 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); + } + } } } + + + From 51cdfb5fdb74871c2d0ebb23635f5d28dfdacb43 Mon Sep 17 00:00:00 2001 From: Bill Welden Date: Tue, 4 Dec 2012 11:55:29 -0800 Subject: [PATCH 051/258] Fix a jshint issue --- src/embind/embind.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/embind/embind.js b/src/embind/embind.js index 7f4e0d2e2..2c5d20c06 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -575,6 +575,7 @@ function __embind_register_class( } }); + /*global ___getDynamicPointerType: false*/ var pointerName = name + '*'; registerType(pointerType, pointerName, { name: pointerName, From 119ab2f1c412c7eb4863f852a690cdf215339b07 Mon Sep 17 00:00:00 2001 From: Bill Welden Date: Wed, 5 Dec 2012 12:27:06 -0800 Subject: [PATCH 052/258] Better error reporting Fixed demangling routine and added it to RTL New __typeName method to retrieve char* type name from type info --- src/embind/embind.js | 8 ++++---- system/lib/embind/bind.cpp | 36 ++++++++++++++++++++++++++++-------- 2 files changed, 32 insertions(+), 12 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 2c5d20c06..e990c1a18 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -137,15 +137,15 @@ 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; +/*global ___typeName:false*/ +function typeName(type) { + return Pointer_stringify(___typeName(type)); } function requireRegisteredType(type, humanName) { var impl = typeRegistry[type]; if (undefined === impl) { - throw new BindingError(humanName + " has unknown type: " + typeName(type)); + throw new BindingError(humanName + " has unknown type " + typeName(type)); } return impl; } diff --git a/system/lib/embind/bind.cpp b/system/lib/embind/bind.cpp index c7c1f4b44..673fdf1a9 100755 --- a/system/lib/embind/bind.cpp +++ b/system/lib/embind/bind.cpp @@ -1,7 +1,10 @@ #include #include <..\lib\libcxxabi\src\private_typeinfo.h> +#include <..\lib\libcxxabi\include\cxxabi.h> #include #include +#include +#include #include using namespace emscripten; @@ -101,13 +104,6 @@ namespace emscripten { return pathsAsTypeInfo; } - // We bind __getDerivationPaths in order to take advantage of the std::vector to Javascript array - // conversion for the return value. This has the unfortunate side-effect of exposing it to third party - // developers, but perhaps the double underscore will scare them away from calling it. - EMSCRIPTEN_BINDINGS(([]() { - function("__getDerivationPaths", &__getDerivationPaths); - })); - // __getDynamicPointerType returns (for polymorphic types only!) the type of the instance actually // pointed to. int EMSCRIPTEN_KEEPALIVE __getDynamicPointerType(int p) { @@ -117,7 +113,7 @@ namespace emscripten { // Calls to __dynamic_cast are generated by the compiler to implement dynamic_cast<>() -- its prototype is // not available through any header file. It is called directly here because it allows run-time - // specification of the target pointer type (which must be specified at compile time when using + // specification of the target pointer type (which can only be specified at compile time when using // dynamic_cast<>(). void* __dynamic_cast(void*, const std::type_info*, const std::type_info*, int); @@ -128,6 +124,30 @@ namespace emscripten { // 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); } + + const char* EMSCRIPTEN_KEEPALIVE __typeName(int p) { + const std::type_info* ti = (const std::type_info*)p; + size_t nameLen = std::min(strlen(ti->name()), (unsigned int)1024); + char* name = (char *)malloc(nameLen+1); + int stat; + + __cxxabiv1::__cxa_demangle(ti->name(), name, &nameLen, &stat); + + if (stat != 0) { + strncpy(name, ti->name(), nameLen); + name[nameLen] = '\0'; + } + return name; + } + + EMSCRIPTEN_BINDINGS(([]() { + // We bind __getDerivationPaths in order to take advantage of the std::vector to Javascript array + // conversion for the return value. This has the unfortunate side-effect of exposing it to third party + // developers, but perhaps the double underscore will scare them away from calling it. + function("__getDerivationPaths", &__getDerivationPaths); + })); + + } } } From 5fba40c9d81d8dc5ccb5fb9d12fa44eb884d96a6 Mon Sep 17 00:00:00 2001 From: Bill Welden Date: Wed, 5 Dec 2012 12:56:41 -0800 Subject: [PATCH 053/258] Forward slashes! --- system/lib/embind/bind.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/system/lib/embind/bind.cpp b/system/lib/embind/bind.cpp index 673fdf1a9..1fcf987a6 100755 --- a/system/lib/embind/bind.cpp +++ b/system/lib/embind/bind.cpp @@ -1,6 +1,6 @@ #include -#include <..\lib\libcxxabi\src\private_typeinfo.h> -#include <..\lib\libcxxabi\include\cxxabi.h> +#include <../lib/libcxxabi/src/private_typeinfo.h> +#include <../lib/libcxxabi/include/cxxabi.h> #include #include #include From 77dd6c9e8355041fe9363c25b2e30dbc9836b13f Mon Sep 17 00:00:00 2001 From: Bill Welden Date: Thu, 6 Dec 2012 09:35:28 -0800 Subject: [PATCH 054/258] First fully functional automatic downcasting implementation. --- src/embind/embind.js | 33 ++++++++++++++++------ system/include/emscripten/bind.h | 2 ++ system/lib/embind/bind.cpp | 48 ++++++++++++++++++++------------ 3 files changed, 56 insertions(+), 27 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index e990c1a18..4ff840058 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -497,6 +497,7 @@ function __embind_register_class( classType, pointerType, constPointerType, + isPolymorphic, name, destructor ) { @@ -580,17 +581,31 @@ function __embind_register_class( registerType(pointerType, pointerName, { name: pointerName, fromWireType: function(ptr) { - var dynamicType = ___getDynamicPointerType(ptr); // !!! this won't work if pointer is not dynamic - if (dynamicType === null || dynamicType === pointerType) { + if (isPolymorphic) { + var toType = ___getDynamicPointerType(ptr); + var toTypeImpl = null; + if (toType === null || toType === pointerType) { + return new Handle(ptr); + } + var derivation = Module.__getDerivationPath(toType, classType); + 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.pointerType); + var castPtr = ___dynamicPointerCast(ptr, classType, candidate); + return toTypePointerImpl.fromWireTypeStatic(castPtr); + } else { return new Handle(ptr); } - try { - dynamicType = requireRegisteredType(dynamicType); - } catch (err) { - return new Handle(ptr); - } - dynamicType = requireRegisteredType(dynamicType.pointerType); - return dynamicType.fromWireTypeStatic(ptr); }, fromWireTypeStatic: function(ptr) { return new Handle(ptr); diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index 54c017ef0..7dc349491 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -103,6 +103,7 @@ namespace emscripten { TYPEID classType, TYPEID pointerType, TYPEID constPointerType, + bool isPolymorphic, const char* className, GenericFunction destructor); @@ -660,6 +661,7 @@ namespace emscripten { TypeID::get(), TypeID>::get(), TypeID>::get(), + std::is_polymorphic::value, name, reinterpret_cast(&raw_destructor)); } diff --git a/system/lib/embind/bind.cpp b/system/lib/embind/bind.cpp index 1fcf987a6..9663ad51b 100755 --- a/system/lib/embind/bind.cpp +++ b/system/lib/embind/bind.cpp @@ -27,7 +27,7 @@ namespace __cxxabiv1 { return bases; } - void __getDerivationPaths1(const __class_type_info* dv, const __class_type_info* bs, std::vectorpath, std::vector>& paths) { + void __getDerivationPaths(const __class_type_info* dv, const __class_type_info* bs, std::vectorpath, std::vector>& paths) { std::vector newPath(path); newPath.emplace_back(dv); if (dv == bs) { @@ -35,7 +35,7 @@ namespace __cxxabiv1 { } else { std::vector bases = __getBaseClasses(dv); for (int i = 0; i < bases.size(); i++) { - __getDerivationPaths1(bases[i], bs, newPath, paths); + __getDerivationPaths(bases[i], bs, newPath, paths); } } } @@ -75,12 +75,10 @@ namespace emscripten { // They are used by embind.js to implement automatic downcasting of return values which are pointers to // polymorphic objects. - // __getDerivationPaths returns an array of arrays of type_info pointers (cast as integers to make - // the Javascript bindings simpler). Each element of the outer array is an array of type_info pointers - // identifying a derivation path from the derived type to the base type. If either of the type info - // pointer paramters does not specify a pointer to a class, or if there is no path from the derived type - // to the base type, this routine returns zero. - std::vector> __getDerivationPaths(int dv, const int bs) { + // __getDerivationPath returns an array of type_info pointers describing the derivation chain starting with + // the derived type and proceeding toward (and ending with) the base type. Types are only included if they + // appear on all possible derivation paths. + std::vector __getDerivationPath(int dv, const int bs) { std::vector> paths; const std::type_info* dv1 = (std::type_info*)dv; @@ -89,19 +87,33 @@ namespace emscripten { const __cxxabiv1::__class_type_info* bs2 = dynamic_cast(bs1); if (dv2 && bs2) { - __cxxabiv1::__getDerivationPaths1(dv2, bs2, std::vector(), paths); + __cxxabiv1::__getDerivationPaths(dv2, bs2, std::vector(), paths); } - std::vector> pathsAsTypeInfo; - for (int i = 0; i < paths.size(); i++) { - std::vector pathAsTypeInfo; - for (int j = 0; j < paths[i].size(); j++) { - pathAsTypeInfo.emplace_back((int)paths[i][j]); + std::vector derivationPath; + if (paths.size() > 0) { + for (int j = 0; j < paths[0].size(); j++) { + bool disqualified = false; + for (int i = 1; i < paths.size(); i++) { + bool found = false; + for (int j2 = 0; j2 < paths[i].size(); j2++) { + if (paths[i][j2] == paths[0][j]) { + found = true; + break; + } + } + if (!found) { + disqualified = true; + break; + } + } + if (!disqualified) { + derivationPath.emplace_back((int)paths[0][j]); + } } - pathsAsTypeInfo.emplace_back(pathAsTypeInfo); } - return pathsAsTypeInfo; + return derivationPath; } // __getDynamicPointerType returns (for polymorphic types only!) the type of the instance actually @@ -141,10 +153,10 @@ namespace emscripten { } EMSCRIPTEN_BINDINGS(([]() { - // We bind __getDerivationPaths in order to take advantage of the std::vector to Javascript array + // We bind __getDerivationPath in order to take advantage of the std::vector to Javascript array // conversion for the return value. This has the unfortunate side-effect of exposing it to third party // developers, but perhaps the double underscore will scare them away from calling it. - function("__getDerivationPaths", &__getDerivationPaths); + function("__getDerivationPath", &__getDerivationPath); })); From c0fce04cc6ef9920f9d91dbd3720b58ae171e80c Mon Sep 17 00:00:00 2001 From: Bill Welden Date: Thu, 6 Dec 2012 09:37:21 -0800 Subject: [PATCH 055/258] Suppress a lint warning --- src/embind/embind.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/embind/embind.js b/src/embind/embind.js index 4ff840058..5ceaf2464 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -577,6 +577,7 @@ function __embind_register_class( }); /*global ___getDynamicPointerType: false*/ + /*global ___dynamicPointerCast: false*/ var pointerName = name + '*'; registerType(pointerType, pointerName, { name: pointerName, From a668fe09c459f0a5691d43b81a16f09cfb9fd43a Mon Sep 17 00:00:00 2001 From: jinsuck Date: Mon, 10 Dec 2012 08:57:06 -0800 Subject: [PATCH 056/258] remove an unused method --- src/embind/emval.js | 4 ---- system/include/emscripten/val.h | 4 ---- 2 files changed, 8 deletions(-) diff --git a/src/embind/emval.js b/src/embind/emval.js index e8ee6baf5..eb2cae0ee 100644 --- a/src/embind/emval.js +++ b/src/embind/emval.js @@ -57,10 +57,6 @@ function __emval_new_cstring(str) { return __emval_register(Pointer_stringify(str)); } -function __emval_has_property(handle, k) { - return _emval_handle_array[handle].value.hasOwnProperty(k); -} - function __emval_get_property(handle, k) { k = Pointer_stringify(k); return __emval_register(_emval_handle_array[handle].value[k]); diff --git a/system/include/emscripten/val.h b/system/include/emscripten/val.h index e2d353823..91becb4f6 100644 --- a/system/include/emscripten/val.h +++ b/system/include/emscripten/val.h @@ -84,10 +84,6 @@ namespace emscripten { return *this; } - bool exist(const char* key) const { - return internal::_emval_has_property(handle, key); - } - val get(const char* key) const { return val(internal::_emval_get_property(handle, key)); } From 4f143f4638aa8239ed5ae6a69a9cfa4997254572 Mon Sep 17 00:00:00 2001 From: jinsuck Date: Mon, 10 Dec 2012 09:11:06 -0800 Subject: [PATCH 057/258] Revert "remove an unused method" This reverts commit 7300ba73d3004b8d5c69bd8e3dfbc971c3147ad8. --- src/embind/emval.js | 4 ++++ system/include/emscripten/val.h | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/embind/emval.js b/src/embind/emval.js index eb2cae0ee..e8ee6baf5 100644 --- a/src/embind/emval.js +++ b/src/embind/emval.js @@ -57,6 +57,10 @@ function __emval_new_cstring(str) { return __emval_register(Pointer_stringify(str)); } +function __emval_has_property(handle, k) { + return _emval_handle_array[handle].value.hasOwnProperty(k); +} + function __emval_get_property(handle, k) { k = Pointer_stringify(k); return __emval_register(_emval_handle_array[handle].value[k]); diff --git a/system/include/emscripten/val.h b/system/include/emscripten/val.h index 91becb4f6..e2d353823 100644 --- a/system/include/emscripten/val.h +++ b/system/include/emscripten/val.h @@ -84,6 +84,10 @@ namespace emscripten { return *this; } + bool exist(const char* key) const { + return internal::_emval_has_property(handle, key); + } + val get(const char* key) const { return val(internal::_emval_get_property(handle, key)); } From 89299704617493eef217c68194053884e55a71f5 Mon Sep 17 00:00:00 2001 From: mey Date: Mon, 10 Dec 2012 13:51:57 -0800 Subject: [PATCH 058/258] Removing unused vector binding code that was missed during previous vector refactorings. --- system/include/emscripten/bind.h | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index 7dc349491..48da75289 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -464,33 +464,6 @@ namespace emscripten { setter(ptr, FieldBinding::fromWireType(value)); } }; - - template - struct Vector { - typedef typename VectorType::value_type ElementType; - typedef internal::BindingType FieldBinding; - typedef typename FieldBinding::WireType WireType; - - static int length( - VectorType* ptr - ) { - return (*ptr).size(); - } - - static WireType getAt( - VectorType* ptr, - int pos - ) { - return FieldBinding::toWireType((*ptr).at(pos)); - } - - static void push_back( - VectorType* ptr, - WireType val - ) { - (*ptr).push_back(FieldBinding::fromWireType(val)); - } - }; } //////////////////////////////////////////////////////////////////////////////// From 50ba3a6ff9933cca9a3f1f893648162c9910d9fb Mon Sep 17 00:00:00 2001 From: Bill Welden Date: Wed, 12 Dec 2012 13:35:33 -0800 Subject: [PATCH 059/258] Benchmark work on pointer casting. --- src/embind/embind.js | 21 ++++++----- system/include/emscripten/bind.h | 4 +-- system/lib/embind/bind.cpp | 60 +++++++++++++++++++++++++++++++- 3 files changed, 73 insertions(+), 12 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 5ceaf2464..643ce728b 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -372,7 +372,7 @@ function __embind_register_smart_ptr( getPointee ) { name = Pointer_stringify(name); - pointeeType = requireRegisteredType(pointeeType, 'class'); + var pointeeTypeImpl = requireRegisteredType(pointeeType, 'class'); destructor = FUNCTION_TABLE[destructor]; getPointee = FUNCTION_TABLE[getPointee]; @@ -384,11 +384,11 @@ function __embind_register_smart_ptr( // TODO: test for SmartPtr.prototype.constructor property? // We likely want it distinct from pointeeType.prototype.constructor - Handle.prototype = Object.create(pointeeType.Handle.prototype); + Handle.prototype = Object.create(pointeeTypeImpl.Handle.prototype); Handle.prototype.clone = function() { if (!this.ptr) { - throw new BindingError(pointeeType.name + ' instance already deleted'); + throw new BindingError(pointeeTypeImpl.name + ' instance already deleted'); } var clone = Object.create(Handle.prototype); @@ -402,7 +402,7 @@ function __embind_register_smart_ptr( Handle.prototype['delete'] = function() { if (!this.ptr) { - throw new BindingError(pointeeType.name + ' instance already deleted'); + throw new BindingError(pointeeTypeImpl.name + ' instance already deleted'); } this.count.value -= 1; @@ -695,20 +695,21 @@ function __embind_register_class_method( }; } +/*global ___staticPointerCast: false*/ function __embind_register_cast_method( classType, methodName, returnType, invoker ) { - classType = requireRegisteredType(classType, 'class'); + var classTypeImpl = requireRegisteredType(classType, 'class'); methodName = Pointer_stringify(methodName); - var humanName = classType.name + '.' + methodName; + var humanName = classTypeImpl.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() { + classTypeImpl.Handle.prototype[methodName] = function() { if (!this.ptr) { throw new BindingError('cannot call emscripten binding method ' + humanName + ' on deleted object'); } @@ -718,7 +719,9 @@ function __embind_register_cast_method( var args = new Array(1); args[0] = this.ptr; - var rv = returnType.fromWireType(invoker.apply(null, args)); // in case ptr needs to be adjusted for multiple inheritance + 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 + this.count.value ++; return rv; }; } diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index 48da75289..48a0fd58a 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -279,7 +279,7 @@ namespace emscripten { template std::shared_ptr performPointerCast(std::shared_ptr from) { - return std::shared_ptr(from, dynamic_cast(from.get())); + return std::shared_ptr(from, static_cast(from.get())); }; template @@ -613,7 +613,7 @@ namespace emscripten { TypeID::get(), methodName, TypeID::get(), - reinterpret_cast(&performPointerCast)); + reinterpret_cast(&performPointerCast)); return *this; } }; diff --git a/system/lib/embind/bind.cpp b/system/lib/embind/bind.cpp index 9663ad51b..de03052a4 100755 --- a/system/lib/embind/bind.cpp +++ b/system/lib/embind/bind.cpp @@ -27,6 +27,21 @@ namespace __cxxabiv1 { return bases; } + int __getBaseOffset(const __class_type_info* ctiDv, const __class_type_info* ctiBs) { + int offset = 0; + + const __vmi_class_type_info* vcti = dynamic_cast(ctiDv); + if (vcti) { + for (int i = 0; i < vcti->__base_count; i++) { + if (vcti->__base_info[i].__base_type == ctiBs) { + offset = vcti->__base_info[i].__offset_flags >> __base_class_type_info::__offset_shift; + break; + } + } + } + return offset; + } + void __getDerivationPaths(const __class_type_info* dv, const __class_type_info* bs, std::vectorpath, std::vector>& paths) { std::vector newPath(path); newPath.emplace_back(dv); @@ -39,6 +54,14 @@ namespace __cxxabiv1 { } } } + + int __pathOffset(std::vector path) { + int offset = 0; + for (int i = 0; i < path.size()-1; i++) { + offset += __getBaseOffset(path[i], path[i+1]); + } + return offset; + } } namespace emscripten { @@ -112,10 +135,45 @@ namespace emscripten { } } } - return derivationPath; } + void* EMSCRIPTEN_KEEPALIVE __staticPointerCast(void* p, int dv, int bs) { + std::vector> 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(dv1); + const __cxxabiv1::__class_type_info* bs2 = dynamic_cast(bs1); + + if (dv2 && bs2) { + __cxxabiv1::__getDerivationPaths(dv2, bs2, std::vector(), paths); + if (paths.size() == 0) { + __cxxabiv1::__getDerivationPaths(bs2, dv2, std::vector(), paths); + direction = -1; + } + } + + int offset = -1; + for (int i = 0; i < paths.size(); i++) { + if (offset < 0) { + offset = __cxxabiv1::__pathOffset(paths[i]); + } else { + if (offset != __cxxabiv1::__pathOffset(paths[i])) { + return (void *)-2; // ambiguous cast -- throw instead? + } + } + } + if (offset < 0) { + return (void *)-1; // types are not related -- throw instead? + } + if (p == 0) { + return (void *)0; + } + return (void *)((int)p + offset * direction); + } + // __getDynamicPointerType returns (for polymorphic types only!) the type of the instance actually // pointed to. int EMSCRIPTEN_KEEPALIVE __getDynamicPointerType(int p) { From eab7bc729e8c881ffdba9f47c00ad245709fa932 Mon Sep 17 00:00:00 2001 From: Todd Lee Date: Mon, 17 Dec 2012 18:25:27 -0800 Subject: [PATCH 060/258] remove unnecessary code --- system/include/emscripten/val.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/system/include/emscripten/val.h b/system/include/emscripten/val.h index e2d353823..de69d00f3 100644 --- a/system/include/emscripten/val.h +++ b/system/include/emscripten/val.h @@ -121,10 +121,6 @@ namespace emscripten { internal::_emval_set_property_by_int(handle, key, v.handle); } - unsigned int length() { - return get("length").as(); - } - template val operator()(Args... args) { using namespace internal; From a7733270b037dcb2e2cf97ecd17ce76f3d4a2da9 Mon Sep 17 00:00:00 2001 From: jinsuck Date: Tue, 18 Dec 2012 13:28:45 -0800 Subject: [PATCH 061/258] add const to val --- system/include/emscripten/val.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/system/include/emscripten/val.h b/system/include/emscripten/val.h index de69d00f3..9e067a92f 100644 --- a/system/include/emscripten/val.h +++ b/system/include/emscripten/val.h @@ -141,7 +141,7 @@ namespace emscripten { } template - val call(const char* name, Args... args) { + val call(const char* name, Args... args) const { using namespace internal; WithPolicies<>::ArgTypeList argList; @@ -162,7 +162,7 @@ namespace emscripten { } template - void call_void(const char* name, Args... args) { + void call_void(const char* name, Args... args) const { using namespace internal; WithPolicies<>::ArgTypeList argList; From 0e3ca9c9d67dfa7efd95d97d3c1a5d0edc6fdb24 Mon Sep 17 00:00:00 2001 From: Todd Lee Date: Tue, 18 Dec 2012 15:02:40 -0800 Subject: [PATCH 062/258] add ability for descendent to clone interface --- system/include/emscripten/bind.h | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index 48a0fd58a..56a3b62cf 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -887,9 +887,7 @@ namespace emscripten { wrapper() {} // to avoid error "call to implicitly deleted construrtor..." wrapper(InterfaceType* interface) { - // why dynamic_cast causes javascript crash? - wrapper* iw = static_cast*>(interface); - jsobj = iw->jsobj; + cloneInterface(interface); } // Not necessary in any example so far, but appeases a compiler warning. @@ -919,6 +917,13 @@ namespace emscripten { return Caller::call(*jsobj, name, args...); } + protected: + void cloneInterface(InterfaceType* interface) { + // why dynamic_cast causes javascript crash? + wrapper* iw = static_cast*>(interface); + jsobj = iw->jsobj; + } + private: // this class only exists because you can't partially specialize function templates template From 40773165f4b7481b1e62533cd94c75fa2d73622d Mon Sep 17 00:00:00 2001 From: jinsuck Date: Wed, 19 Dec 2012 16:56:06 -0800 Subject: [PATCH 063/258] add const to a function in val --- system/include/emscripten/val.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/include/emscripten/val.h b/system/include/emscripten/val.h index 9e067a92f..8f7df8827 100644 --- a/system/include/emscripten/val.h +++ b/system/include/emscripten/val.h @@ -109,7 +109,7 @@ namespace emscripten { return val(internal::_emval_get_property_by_unsigned_long(handle, key)); } - val eval_global_method(const char* objectName, const char* methodName) { + val eval_global_method(const char* objectName, const char* methodName) const { return val(internal::_emval_eval_global_method(handle, objectName, methodName)); } From eca17e7c1b78673821cef5d093768d5dfbff329d Mon Sep 17 00:00:00 2001 From: Todd Lee Date: Thu, 20 Dec 2012 10:47:30 -0800 Subject: [PATCH 064/258] stringify key name before checking if the key exists --- src/embind/emval.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/embind/emval.js b/src/embind/emval.js index e8ee6baf5..49424631f 100644 --- a/src/embind/emval.js +++ b/src/embind/emval.js @@ -58,6 +58,7 @@ function __emval_new_cstring(str) { } function __emval_has_property(handle, k) { + k = Pointer_stringify(k); return _emval_handle_array[handle].value.hasOwnProperty(k); } From 77f2b4588b65c4c3641131ee71657eaaa8d3dcaf Mon Sep 17 00:00:00 2001 From: jinsuck Date: Thu, 20 Dec 2012 19:20:29 -0800 Subject: [PATCH 065/258] add a method to conveniently clone to shared pointer of wrapper --- system/include/emscripten/bind.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index 56a3b62cf..3f857a2ab 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -902,6 +902,11 @@ namespace emscripten { return std::shared_ptr(ip); } + template + static std::shared_ptr cloneToSharedWrapperPtr(InterfaceType& i) { + return std::dynamic_pointer_cast(cloneToSharedPtr(i)); + } + void initialize(internal::EM_VAL handle) { if (jsobj) { internal::_embind_fatal_error( From ea98beba7402b83a05434342d5b2af85aae3dd42 Mon Sep 17 00:00:00 2001 From: jinsuck Date: Fri, 21 Dec 2012 15:24:48 -0800 Subject: [PATCH 066/258] remove meaningless casting back and forth --- system/include/emscripten/bind.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index 3f857a2ab..90dca77cb 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -904,7 +904,7 @@ namespace emscripten { template static std::shared_ptr cloneToSharedWrapperPtr(InterfaceType& i) { - return std::dynamic_pointer_cast(cloneToSharedPtr(i)); + return std::make_shared(&i); } void initialize(internal::EM_VAL handle) { From 2308f2a5b19e92d9f974b4a7ee733d5c6f3da4bb Mon Sep 17 00:00:00 2001 From: Bill Welden Date: Sat, 22 Dec 2012 10:19:52 -0800 Subject: [PATCH 067/258] More work on auto downcasting. --- src/embind/embind.js | 131 ++++++++++++++++++++++++++----- system/include/emscripten/bind.h | 26 +++--- 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 643ce728b..285e632a0 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 90dca77cb..c37aed24c 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 - ToType& performCast(FromType& from) { - return *dynamic_cast(&from); + ToType& performRawStaticCast(FromType& from) { + return *static_cast(&from); }; template - std::shared_ptr performPointerCast(std::shared_ptr from) { + std::shared_ptr performSharedStaticCast(std::shared_ptr from) { return std::shared_ptr(from, static_cast(from.get())); }; @@ -598,6 +602,7 @@ namespace emscripten { _embind_register_smart_ptr( TypeID::get(), TypeID::get(), + std::is_polymorphic::value, name, reinterpret_cast(&raw_destructor), reinterpret_cast(&get_pointee)); @@ -611,9 +616,11 @@ namespace emscripten { typedef typename PointerType::element_type PointeeType; _embind_register_pointer_cast_method( TypeID::get(), - methodName, TypeID::get(), - reinterpret_cast(&performPointerCast)); + TypeID::get(), + std::is_polymorphic::value, + methodName, + reinterpret_cast(&performSharedStaticCast)); return *this; } }; @@ -756,9 +763,10 @@ namespace emscripten { _embind_register_cast_method( TypeID::get(), + std::is_polymorphic::value, methodName, TypeID::get(), - reinterpret_cast(&performCast)); + reinterpret_cast(&performRawStaticCast)); return *this; } }; diff --git a/system/lib/embind/bind.cpp b/system/lib/embind/bind.cpp index de03052a4..bc1e09ee4 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> 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(dv1); - const __cxxabiv1::__class_type_info* bs2 = dynamic_cast(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(from1); + const __cxxabiv1::__class_type_info* to2 = dynamic_cast(to1); - if (dv2 && bs2) { - __cxxabiv1::__getDerivationPaths(dv2, bs2, std::vector(), paths); + if (from2 && to2) { + __cxxabiv1::__getDerivationPaths(from2, to2, std::vector(), paths); if (paths.size() == 0) { - __cxxabiv1::__getDerivationPaths(bs2, dv2, std::vector(), paths); + __cxxabiv1::__getDerivationPaths(to2, from2, std::vector(), 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) { From fa4795fe6ca52b5c0b217be6108bf3c933dd84d2 Mon Sep 17 00:00:00 2001 From: Bill Welden Date: Mon, 24 Dec 2012 08:57:53 -0800 Subject: [PATCH 068/258] Continuing merge of auto downcasting. --- src/embind/embind.js | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 285e632a0..6683f5484 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -174,7 +174,7 @@ function runDestructors(destructors) { function makeInvoker(name, argCount, argTypes, invoker, fn) { return function() { if (arguments.length !== argCount - 1) { - throw new BindingError('function ' + name + ' called with ' + arguments.length + ' arguments, expected ' + argCount - 1); + throw new BindingError('function ' + name + ' called with ' + arguments.length + ' arguments, expected ' + (argCount - 1)); } var destructors = []; var args = new Array(argCount); @@ -369,6 +369,7 @@ function __embind_register_struct_field( }; } +/*global ___dynamicPointerCast: false*/ function __embind_register_smart_ptr( pointerType, pointeeType, @@ -539,6 +540,7 @@ function __embind_register_vector( } // TODO: null pointers are always zero (not a Handle) in Javascript +/*global ___staticPointerCast: false*/ function __embind_register_class( classType, pointerType, @@ -625,11 +627,11 @@ function __embind_register_class( }); /*global ___getDynamicPointerType: false*/ - /*global ___dynamicPointerCast: false*/ var pointerName = name + '*'; registerType(pointerType, pointerName, { name: pointerName, fromWireTypeAutoDowncast: function(ptr) { + var handle; if (isPolymorphic) { var toType = ___getDynamicPointerType(ptr); var toTypeImpl = null; @@ -650,13 +652,13 @@ function __embind_register_class( return new Handle(ptr); } var toTypePointerImpl = requireRegisteredType(toTypeImpl.pointerType); - var handle = toTypePointerImpl.fromWireType(ptr); + handle = toTypePointerImpl.fromWireType(ptr); handle.ptr = ___staticPointerCast(handle.ptr, classType, candidate); // todo: can come back -1 or -2!! Throw appropriate exception return handle; } else { handle = new Handle(ptr); - } + } return handle; }, fromWireType: function(ptr) { @@ -750,7 +752,6 @@ function __embind_register_class_method( }; } -/*global ___staticPointerCast: false*/ function __embind_register_cast_method( classType, isPolymorphic, @@ -778,11 +779,11 @@ function __embind_register_cast_method( var derivation = Module.__getDerivationPath(returnType, runtimeType); // downcast is valid var size = derivation.size(); derivation.delete(); - if (size == 0) { + if (size === 0) { derivation = Module.__getDerivationPath(runtimeType, returnType); // upcast is valid size = derivation.size(); derivation.delete(); - if (size == 0) { + if (size === 0) { // todo: return zero return returnTypeImpl.fromWireType(0); } @@ -826,11 +827,11 @@ function __embind_register_pointer_cast_method( var derivation = Module.__getDerivationPath(returnPointeeType, runtimeType); // downcast is valid var size = derivation.size(); derivation.delete(); - if (size == 0) { + if (size === 0) { derivation = Module.__getDerivationPath(runtimeType, returnPointeeType); // upcast is valid size = derivation.size(); derivation.delete(); - if (size == 0) { + if (size === 0) { return 0; } } From 55c72d46d7c90a0e282b119c17496b2909784144 Mon Sep 17 00:00:00 2001 From: Bill Welden Date: Mon, 31 Dec 2012 07:40:08 -0800 Subject: [PATCH 069/258] Refactoring preparatory to code clean-up (no functional changes, all tests pass). --- src/embind/embind.js | 595 +++++++++++++++++++++++-------------------- src/embind/emval.js | 0 2 files changed, 322 insertions(+), 273 deletions(-) mode change 100644 => 100755 src/embind/emval.js diff --git a/src/embind/embind.js b/src/embind/embind.js index 6683f5484..aa4dfdc75 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -3,6 +3,10 @@ /*global FUNCTION_TABLE, HEAP32*/ /*global Pointer_stringify, writeStringToMemory*/ /*global __emval_register, _emval_handle_array, __emval_decref*/ +/*global ___getDynamicPointerType: false*/ +/*global ___dynamicPointerCast: false*/ +/*global ___typeName:false*/ +/*global ___staticPointerCast: false*/ function exposePublicSymbol(name, value) { if (Module.hasOwnProperty(name)) { @@ -32,112 +36,133 @@ function _embind_repr(v) { var typeRegistry = {}; -function registerType(type, name, info) { +function registerType(type, name, registeredInstance) { if (!type) { throw new BindingError('type "' + name + '" must have a positive integer typeid pointer'); } if (typeRegistry.hasOwnProperty(type)) { throw new BindingError("Cannot register type '" + name + "' twice"); } - typeRegistry[type] = info; + registeredInstance.type = type; + registeredInstance.name = name; + typeRegistry[type] = registeredInstance; } +function RegisteredVoid() { +} + +RegisteredVoid.prototype.fromWireType = function() { + return undefined; +}; + function __embind_register_void(voidType, name) { name = Pointer_stringify(name); - registerType(voidType, name, { - name: name, - fromWireType: function() { - return undefined; - } - }); + registerType(voidType, name, new RegisteredVoid()); } -function __embind_register_bool(boolType, name, trueValue, falseValue) { - name = Pointer_stringify(name); - registerType(boolType, name, { - name: name, - toWireType: function(destructors, o) { - return o ? trueValue : falseValue; - }, - fromWireType: function(wt) { +function RegisteredBool(trueValue, falseValue) { + this.trueValue = trueValue; + this.falseValue = falseValue; +} + +RegisteredBool.prototype.toWireType = function(destructors, o) { + return o ? this.trueValue : this.falseValue; +}; + +RegisteredBool.prototype.fromWireType = function(wt) { // ambiguous emscripten ABI: sometimes return values are // true or false, and sometimes integers (0 or 1) return !!wt; - }, - }); +}; + +function __embind_register_bool(boolType, name, trueValue, falseValue) { + name = Pointer_stringify(name); + registerType(boolType, name, new RegisteredBool(trueValue, falseValue)); } +function RegisteredInteger() { +} + +RegisteredInteger.prototype.toWireType = function(destructors, value) { + if (typeof value !== "number") { + throw new TypeError('Cannot convert "' + _embind_repr(value) + '" to ' + this.name); + } + return value | 0; +}; + +RegisteredInteger.prototype.fromWireType = function(value) { + return value; +}; + function __embind_register_integer(primitiveType, name) { name = Pointer_stringify(name); - registerType(primitiveType, name, { - 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; - } - }); + registerType(primitiveType, name, new RegisteredInteger()); } +function RegisteredFloat() { +} + +RegisteredFloat.prototype.toWireType = function(destructors, value) { + if (typeof value !== "number") { + throw new TypeError('Cannot convert "' + _embind_repr(value) + '" to ' +this.name); + } + return value; +}; + +RegisteredFloat.prototype.fromWireType = function(value) { + return value; +}; + function __embind_register_float(primitiveType, name) { name = Pointer_stringify(name); - registerType(primitiveType, name, { - 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; - } - }); + registerType(primitiveType, name, new RegisteredFloat()); } +function RegisteredString(stringType, name) { + +} + +RegisteredString.prototype.toWireType = function(destructors, value) { + var ptr = _malloc(value.length + 1); + writeStringToMemory(value, ptr); + destructors.push(_free); + destructors.push(ptr); + return ptr; +}; + +RegisteredString.prototype.fromWireType = function(value) { + var rv = Pointer_stringify(value); + _free(value); + return rv; +}; + function __embind_register_cstring(stringType, name) { name = Pointer_stringify(name); - registerType(stringType, name, { - 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; - } - }); + registerType(stringType, name, new RegisteredString()); } +function RegisteredEmval() { +} + +RegisteredEmval.prototype.toWireType = function(destructors, value) { + return __emval_register(value); +}; + +RegisteredEmval.prototype.fromWireType = function(handle) { + var rv = _emval_handle_array[handle].value; + __emval_decref(handle); + return rv; +}; + function __embind_register_emval(emvalType, name) { name = Pointer_stringify(name); - registerType(emvalType, name, { - 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; - } - }); + registerType(emvalType, name, new RegisteredEmval()); } var BindingError = Error; /** @expose */ Module.BindingError = BindingError; -/*global ___typeName:false*/ function typeName(type) { return Pointer_stringify(___typeName(type)); } @@ -183,7 +208,7 @@ function makeInvoker(name, argCount, argTypes, invoker, fn) { args[i] = argTypes[i].toWireType(destructors, arguments[i-1]); } var rv = invoker.apply(null, args); - if (argTypes[0].hasOwnProperty('fromWireTypeAutoDowncast')) { + if (argTypes[0].fromWireTypeAutoDowncast) { rv = argTypes[0].fromWireTypeAutoDowncast(rv); } else { rv = argTypes[0].fromWireType(rv); @@ -200,39 +225,41 @@ function __embind_register_function(name, argCount, argTypes, invoker, fn) { exposePublicSymbol(name, makeInvoker(name, argCount, argTypes, invoker, fn)); } +function RegisteredTuple(constructor, destructor) { + this.constructor = constructor; + this.destructor = destructor; + this.elements = []; +} + +RegisteredTuple.prototype.toWireType = function(destructors, o) { + var len = this.elements.length; + if (len !== o.length) { + throw new TypeError("Incorrect number of tuple elements"); + } + var ptr = this.constructor(); + for (var i = 0; i < len; ++i) { + this.elements[i].write(ptr, o[i]); + } + destructors.push(this.destructor); + destructors.push(ptr); + return ptr; +}; + +RegisteredTuple.prototype.fromWireType = function(ptr) { + var len = this.elements.length; + var rv = new Array(len); + for (var i = 0; i < len; ++i) { + rv[i] = this.elements[i].read(ptr); + } + this.destructor(ptr); + return rv; +}; + function __embind_register_tuple(tupleType, name, constructor, destructor) { name = Pointer_stringify(name); constructor = FUNCTION_TABLE[constructor]; destructor = FUNCTION_TABLE[destructor]; - - var elements = []; - - registerType(tupleType, name, { - 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; - } - }); + registerType(tupleType, name, new RegisteredTuple(constructor, destructor)); } function copyMemberPointer(memberPointer, memberPointerSize) { @@ -302,6 +329,38 @@ function __embind_register_tuple_element_accessor( }); } +function RegisteredStruct(constructor, destructor) { + this.constructor = constructor; + this.destructor = destructor; + this.fields = {}; +} + +RegisteredStruct.prototype.toWireType = function(destructors, o) { + var fields = this.fields; + for (var fieldName in fields) { + if (!(fieldName in o)) { + throw new TypeError('Missing field'); + } + } + var ptr = this.constructor(); + for (fieldName in fields) { + fields[fieldName].write(ptr, o[fieldName]); + } + destructors.push(this.destructor); + destructors.push(ptr); + return ptr; +}; + +RegisteredStruct.prototype.fromWireType = function(ptr) { + var fields = this.fields; + var rv = {}; + for (var i in fields) { + rv[i] = fields[i].read(ptr); + } + this.destructor(ptr); + return rv; +}; + function __embind_register_struct( structType, name, @@ -312,33 +371,7 @@ function __embind_register_struct( constructor = FUNCTION_TABLE[constructor]; destructor = FUNCTION_TABLE[destructor]; - registerType(structType, name, { - 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; - } - }); + registerType(structType, name, new RegisteredStruct(constructor, destructor)); } function __embind_register_struct_field( @@ -369,7 +402,69 @@ function __embind_register_struct_field( }; } -/*global ___dynamicPointerCast: false*/ +function RegisteredSmartPointer(getPointee, Handle, destructor, isPolymorphic, pointeeType) { + this.getPointee = getPointee; + this.Handle = Handle; + this.destructor = destructor; + this.isPolymorphic = isPolymorphic; + this.pointeeType = pointeeType; +} + +RegisteredSmartPointer.prototype.toWireType = function(destructors, o) { + if (null === o) { + return 0; + } else { + return o.smartPointer; + } +}; + +RegisteredSmartPointer.prototype.fromWireType = function(ptr) { + if (!this.getPointee(ptr)) { + this.destructor(ptr); + return null; + } + return new this.Handle(ptr); +}; + +RegisteredSmartPointer.prototype.fromWireTypeAutoDowncast = function(ptr) { + if (!this.getPointee(ptr)) { + this.destructor(ptr); + return null; + } + if (this.isPolymorphic) { + // todo: clean up this code + var pointee = this.getPointee(ptr); + var toType = ___getDynamicPointerType(pointee); + var toTypeImpl = null; + if (toType === null || toType === this.pointeeType) { + return new this.Handle(ptr); + } + // todo: getDerivationPath is expensive -- cache the result + var derivation = Module.__getDerivationPath(toType, this.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 this.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 this.Handle(ptr); + } +}; + function __embind_register_smart_ptr( pointerType, pointeeType, @@ -421,62 +516,58 @@ function __embind_register_smart_ptr( this.ptr = undefined; }; - 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); - return null; - } - return new Handle(ptr); - }, - toWireType: function(destructors, o) { - if (null === o) { - return 0; - } else { - return o.smartPointer; + registerType(pointerType, name, new RegisteredSmartPointer(getPointee, Handle, destructor, isPolymorphic, pointeeType)); +} + +function RegisteredRawPointer(isPolymorphic, classType, Handle) { + this.isPolymorphic = isPolymorphic; + this.classType = classType; + this.Handle = Handle; +} + +RegisteredRawPointer.prototype.toWireType = function(destructors, o) { + return o.ptr; +}; + +RegisteredRawPointer.prototype.fromWireType = function(ptr) { + return new this.Handle(ptr); +}; + +RegisteredRawPointer.prototype.fromWireTypeAutoDowncast = function(ptr) { + if (this.isPolymorphic) { + var toType = ___getDynamicPointerType(ptr); + var toTypeImpl = null; + if (toType === null || toType === this.pointerType) { + return new this.Handle(ptr); + } + var derivation = Module.__getDerivationPath(toType, this.classType); + 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 this.Handle(ptr); + } + var toTypePointerImpl = requireRegisteredType(toTypeImpl.type); + var handle = toTypePointerImpl.fromWireType(ptr); + handle.ptr = ___staticPointerCast(handle.ptr, this.classType, candidate); + // todo: can come back -1 or -2!! Throw appropriate exception + return handle; + } else { + handle = new this.Handle(ptr); + } + return handle; +}; + +function RegisteredClassInstance(pointerType, constructor, Handle) { + this.pointerType = pointerType; + this.constructor = constructor; + this.Handle = Handle; } function __embind_register_vector( @@ -539,6 +630,22 @@ function __embind_register_vector( }); } +RegisteredClassInstance.prototype.toWireType = function(destructors, o) { + return o.ptr; +}; + +RegisteredClassInstance.prototype.fromWireType = function(ptr) { + return new this.Handle(ptr); +}; + +function RegisteredRawConstPointer() { +} + +RegisteredRawConstPointer.prototype.toWireType = function(destructors, o) { + return o.ptr; +}; + +>>>>>>> Refactoring preparatory to code clean-up (no functional changes, all tests pass). // TODO: null pointers are always zero (not a Handle) in Javascript /*global ___staticPointerCast: false*/ function __embind_register_class( @@ -612,70 +719,9 @@ function __embind_register_class( constructor.prototype = Handle.prototype; constructor.classType = classType; - - registerType(classType, name, { - name: name, - pointerType: pointerType, - constructor: constructor, - Handle: Handle, - fromWireType: function(ptr) { - return new Handle(ptr); - }, - toWireType: function(destructors, o) { - return o.ptr; - } - }); - - /*global ___getDynamicPointerType: false*/ - var pointerName = name + '*'; - registerType(pointerType, pointerName, { - name: pointerName, - fromWireTypeAutoDowncast: function(ptr) { - var handle; - if (isPolymorphic) { - var toType = ___getDynamicPointerType(ptr); - var toTypeImpl = null; - if (toType === null || toType === pointerType) { - return new Handle(ptr); - } - var derivation = Module.__getDerivationPath(toType, classType); - 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.pointerType); - handle = toTypePointerImpl.fromWireType(ptr); - handle.ptr = ___staticPointerCast(handle.ptr, classType, candidate); - // todo: can come back -1 or -2!! Throw appropriate exception - return handle; - } else { - handle = new Handle(ptr); - } - return handle; - }, - fromWireType: function(ptr) { - return new Handle(ptr); - }, - toWireType: function(destructors, o) { - return o.ptr; - } - }); - - var constPointerName = name + ' const*'; - registerType(constPointerType, constPointerName, { - name: constPointerName, - toWireType: function(destructors, o) { - return o.ptr; - } - }); + registerType(classType, name, new RegisteredClassInstance(pointerType, constructor, Handle)); + registerType(pointerType, name + '*', new RegisteredRawPointer(isPolymorphic, classType, Handle)); + registerType(constPointerType, name + ' const*', new RegisteredRawConstPointer()); exposePublicSymbol(name, constructor); } @@ -742,7 +788,7 @@ function __embind_register_class_method( args[i + 1] = argTypes[i].toWireType(destructors, arguments[i-1]); } var rv = invoker.apply(null, args); - if (argTypes[0].hasOwnProperty('fromWireTypeAutoDowncast')) { + if (argTypes[0].fromWireTypeAutoDowncast) { rv = argTypes[0].fromWireTypeAutoDowncast(rv); } else { rv = argTypes[0].fromWireType(rv); @@ -996,28 +1042,27 @@ function __embind_register_class_field( }); } +function RegisteredEnum() { + this.constructor = function() {}; + this.constructor.values = {}; +} + +RegisteredEnum.prototype.toWireType = function(destructors, c) { + return c.value; +}; + +RegisteredEnum.prototype.fromWireType = function(c) { + return this.constructor.values[c]; +}; + function __embind_register_enum( enumType, name ) { name = Pointer_stringify(name); - - function Enum() { - } - Enum.values = {}; - - registerType(enumType, name, { - name: name, - constructor: Enum, - toWireType: function(destructors, c) { - return c.value; - }, - fromWireType: function(c) { - return Enum.values[c]; - }, - }); - - exposePublicSymbol(name, Enum); + var newEnum = new RegisteredEnum(); + registerType(enumType, name, newEnum); + exposePublicSymbol(name, newEnum.constructor); } function __embind_register_enum_value( @@ -1038,6 +1083,19 @@ function __embind_register_enum_value( Enum[name] = Value; } +function RegisteredInterface(constructor, destructor) { + this.constructor = constructor; + this.destructor = destructor; +} + +RegisteredInterface.prototype.toWireType = function(destructors, o) { + var handle = __emval_register(o); + var ptr = this.constructor(handle); + destructors.push(this.destructor); + destructors.push(ptr); + return ptr; +}; + function __embind_register_interface( interfaceType, name, @@ -1048,15 +1106,6 @@ function __embind_register_interface( constructor = FUNCTION_TABLE[constructor]; destructor = FUNCTION_TABLE[destructor]; - registerType(interfaceType, name, { - name: name, - toWireType: function(destructors, o) { - var handle = __emval_register(o); - var ptr = constructor(handle); - destructors.push(destructor); - destructors.push(ptr); - return ptr; - }, - }); + registerType(interfaceType, name, new RegisteredInterface(constructor, destructor)); } diff --git a/src/embind/emval.js b/src/embind/emval.js old mode 100644 new mode 100755 From d069ed414249db41a4dfa91061e8979d6c9dccca Mon Sep 17 00:00:00 2001 From: Bill Welden Date: Mon, 31 Dec 2012 11:39:59 -0800 Subject: [PATCH 070/258] clear distinction between raw (C++) and cooked (Javascript class instances) types. --- src/embind/embind.js | 289 +++++++++++++++---------------- system/include/emscripten/bind.h | 8 +- 2 files changed, 144 insertions(+), 153 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index aa4dfdc75..fb56e7c0c 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -36,16 +36,42 @@ function _embind_repr(v) { var typeRegistry = {}; -function registerType(type, name, registeredInstance) { - if (!type) { +function registerType(rawType, name, registeredInstance) { + if (!rawType) { throw new BindingError('type "' + name + '" must have a positive integer typeid pointer'); } - if (typeRegistry.hasOwnProperty(type)) { + if (typeRegistry.hasOwnProperty(rawType)) { throw new BindingError("Cannot register type '" + name + "' twice"); } - registeredInstance.type = type; + registeredInstance.rawType = rawType; registeredInstance.name = name; - typeRegistry[type] = registeredInstance; + typeRegistry[rawType] = registeredInstance; + return registeredInstance; +} + +function requireRegisteredType(rawType, humanName) { + var impl = typeRegistry[rawType]; + if (undefined === impl) { + throw new BindingError(humanName + " has unknown type " + typeName(rawType)); + } + return impl; +} + +function typeName(rawType) { + return Pointer_stringify(___typeName(rawType)); +} + +function requireArgumentTypes(argCount, rawArgTypes, name) { + var argTypes = new Array(argCount); + for (var i = 0; i < argCount; ++i) { + var rawArgType = HEAP32[(rawArgTypes >> 2) + i]; + if (i === 0) { + argTypes[i] = requireRegisteredType(rawArgType, name + " return value"); + } else { + argTypes[i] = requireRegisteredType(rawArgType, name + " parameter " + i); + } + } + return argTypes; } function RegisteredVoid() { @@ -55,9 +81,9 @@ RegisteredVoid.prototype.fromWireType = function() { return undefined; }; -function __embind_register_void(voidType, name) { +function __embind_register_void(rawType, name) { name = Pointer_stringify(name); - registerType(voidType, name, new RegisteredVoid()); + registerType(rawType, name, new RegisteredVoid()); } function RegisteredBool(trueValue, falseValue) { @@ -75,9 +101,9 @@ RegisteredBool.prototype.fromWireType = function(wt) { return !!wt; }; -function __embind_register_bool(boolType, name, trueValue, falseValue) { +function __embind_register_bool(rawType, name, trueValue, falseValue) { name = Pointer_stringify(name); - registerType(boolType, name, new RegisteredBool(trueValue, falseValue)); + registerType(rawType, name, new RegisteredBool(trueValue, falseValue)); } function RegisteredInteger() { @@ -94,9 +120,9 @@ RegisteredInteger.prototype.fromWireType = function(value) { return value; }; -function __embind_register_integer(primitiveType, name) { +function __embind_register_integer(rawType, name) { name = Pointer_stringify(name); - registerType(primitiveType, name, new RegisteredInteger()); + registerType(rawType, name, new RegisteredInteger()); } function RegisteredFloat() { @@ -113,9 +139,9 @@ RegisteredFloat.prototype.fromWireType = function(value) { return value; }; -function __embind_register_float(primitiveType, name) { +function __embind_register_float(rawType, name) { name = Pointer_stringify(name); - registerType(primitiveType, name, new RegisteredFloat()); + registerType(rawType, name, new RegisteredFloat()); } function RegisteredString(stringType, name) { @@ -136,9 +162,9 @@ RegisteredString.prototype.fromWireType = function(value) { return rv; }; -function __embind_register_cstring(stringType, name) { +function __embind_register_cstring(rawType, name) { name = Pointer_stringify(name); - registerType(stringType, name, new RegisteredString()); + registerType(rawType, name, new RegisteredString()); } function RegisteredEmval() { @@ -154,40 +180,15 @@ RegisteredEmval.prototype.fromWireType = function(handle) { return rv; }; -function __embind_register_emval(emvalType, name) { +function __embind_register_emval(rawType, name) { name = Pointer_stringify(name); - registerType(emvalType, name, new RegisteredEmval()); + registerType(rawType, name, new RegisteredEmval()); } var BindingError = Error; /** @expose */ Module.BindingError = BindingError; -function typeName(type) { - return Pointer_stringify(___typeName(type)); -} - -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]; - if (i === 0) { - argTypeImpls[i] = requireRegisteredType(argType, name + " return value"); - } else { - argTypeImpls[i] = requireRegisteredType(argType, name + " parameter " + i); - } - } - return argTypeImpls; -} - function runDestructors(destructors) { while (destructors.length) { var ptr = destructors.pop(); @@ -218,10 +219,10 @@ function makeInvoker(name, argCount, argTypes, invoker, fn) { }; } -function __embind_register_function(name, argCount, argTypes, invoker, fn) { +function __embind_register_function(name, argCount, rawArgTypes, invoker, fn) { name = Pointer_stringify(name); invoker = FUNCTION_TABLE[invoker]; - argTypes = requireArgumentTypes(argCount, argTypes, name); + var argTypes = requireArgumentTypes(argCount, rawArgTypes, name); exposePublicSymbol(name, makeInvoker(name, argCount, argTypes, invoker, fn)); } @@ -255,11 +256,11 @@ RegisteredTuple.prototype.fromWireType = function(ptr) { return rv; }; -function __embind_register_tuple(tupleType, name, constructor, destructor) { +function __embind_register_tuple(type, name, constructor, destructor) { name = Pointer_stringify(name); constructor = FUNCTION_TABLE[constructor]; destructor = FUNCTION_TABLE[destructor]; - registerType(tupleType, name, new RegisteredTuple(constructor, destructor)); + registerType(type, name, new RegisteredTuple(constructor, destructor)); } function copyMemberPointer(memberPointer, memberPointerSize) { @@ -362,7 +363,7 @@ RegisteredStruct.prototype.fromWireType = function(ptr) { }; function __embind_register_struct( - structType, + type, name, constructor, destructor @@ -371,7 +372,7 @@ function __embind_register_struct( constructor = FUNCTION_TABLE[constructor]; destructor = FUNCTION_TABLE[destructor]; - registerType(structType, name, new RegisteredStruct(constructor, destructor)); + registerType(type, name, new RegisteredStruct(constructor, destructor)); } function __embind_register_struct_field( @@ -434,48 +435,43 @@ RegisteredSmartPointer.prototype.fromWireTypeAutoDowncast = function(ptr) { if (this.isPolymorphic) { // todo: clean up this code var pointee = this.getPointee(ptr); - var toType = ___getDynamicPointerType(pointee); - var toTypeImpl = null; - if (toType === null || toType === this.pointeeType) { + var toRawType = ___getDynamicPointerType(pointee); + if (toRawType === null || toRawType === this.pointeeType.rawType) { return new this.Handle(ptr); } // todo: getDerivationPath is expensive -- cache the result - var derivation = Module.__getDerivationPath(toType, this.pointeeType); - var candidate = null; + var derivation = Module.__getDerivationPath(toRawType, this.pointeeType.rawType); + var rawCandidateType = null; + var candidateType = null; for (var i = 0; i < derivation.size(); i++) { - candidate = derivation.at(i); - toTypeImpl = typeRegistry[candidate]; - if (toTypeImpl) { + rawCandidateType = derivation.at(i); + candidateType = typeRegistry[rawCandidateType]; + if (candidateType) { break; } } derivation.delete(); - if (toTypeImpl === null) { + if (candidateType === null) { return new this.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; + return candidateType.smartPointerType.fromWireType(ptr); } else { return new this.Handle(ptr); } }; function __embind_register_smart_ptr( - pointerType, - pointeeType, + rawType, + rawPointeeType, isPolymorphic, name, destructor, getPointee ) { name = Pointer_stringify(name); - var pointeeTypeImpl = requireRegisteredType(pointeeType, 'class'); - pointeeTypeImpl.smartPointerType = pointerType; + var pointeeType = requireRegisteredType(rawPointeeType, 'class'); destructor = FUNCTION_TABLE[destructor]; getPointee = FUNCTION_TABLE[getPointee]; @@ -487,11 +483,11 @@ function __embind_register_smart_ptr( // TODO: test for SmartPtr.prototype.constructor property? // We likely want it distinct from pointeeType.prototype.constructor - Handle.prototype = Object.create(pointeeTypeImpl.Handle.prototype); + Handle.prototype = Object.create(pointeeType.Handle.prototype); Handle.prototype.clone = function() { if (!this.ptr) { - throw new BindingError(pointeeTypeImpl.name + ' instance already deleted'); + throw new BindingError(pointeeType.name + ' instance already deleted'); } var clone = Object.create(Handle.prototype); @@ -505,7 +501,7 @@ function __embind_register_smart_ptr( Handle.prototype['delete'] = function() { if (!this.ptr) { - throw new BindingError(pointeeTypeImpl.name + ' instance already deleted'); + throw new BindingError(pointeeType.name + ' instance already deleted'); } this.count.value -= 1; @@ -515,13 +511,12 @@ function __embind_register_smart_ptr( this.smartPointer = undefined; this.ptr = undefined; }; - - registerType(pointerType, name, new RegisteredSmartPointer(getPointee, Handle, destructor, isPolymorphic, pointeeType)); + pointeeType.smartPointerType = registerType(rawType, name, new RegisteredSmartPointer(getPointee, Handle, destructor, isPolymorphic, pointeeType)); } function RegisteredRawPointer(isPolymorphic, classType, Handle) { this.isPolymorphic = isPolymorphic; - this.classType = classType; + this.pointeeType = classType; this.Handle = Handle; } @@ -535,27 +530,24 @@ RegisteredRawPointer.prototype.fromWireType = function(ptr) { RegisteredRawPointer.prototype.fromWireTypeAutoDowncast = function(ptr) { if (this.isPolymorphic) { - var toType = ___getDynamicPointerType(ptr); - var toTypeImpl = null; - if (toType === null || toType === this.pointerType) { + var toRawType = ___getDynamicPointerType(ptr); + if (toRawType === null || toRawType === this.pointeeType.rawType) { return new this.Handle(ptr); } - var derivation = Module.__getDerivationPath(toType, this.classType); - var candidate = null; + var derivation = Module.__getDerivationPath(toRawType, this.pointeeType.rawType); + var candidateType = null; for (var i = 0; i < derivation.size(); i++) { - candidate = derivation.at(i); - toTypeImpl = typeRegistry[candidate]; - if (toTypeImpl) { + candidateType = typeRegistry[derivation.at(i)]; + if (candidateType) { break; } } derivation.delete(); - if (toTypeImpl === null) { + if (candidateType === null) { return new this.Handle(ptr); } - var toTypePointerImpl = requireRegisteredType(toTypeImpl.type); - var handle = toTypePointerImpl.fromWireType(ptr); - handle.ptr = ___staticPointerCast(handle.ptr, this.classType, candidate); + var handle = candidateType.fromWireType(ptr); + handle.ptr = ___staticPointerCast(handle.ptr, this.pointeeType.rawType, candidateType.rawType); // todo: can come back -1 or -2!! Throw appropriate exception return handle; } else { @@ -564,8 +556,7 @@ RegisteredRawPointer.prototype.fromWireTypeAutoDowncast = function(ptr) { return handle; }; -function RegisteredClassInstance(pointerType, constructor, Handle) { - this.pointerType = pointerType; +function RegisteredClassInstance(constructor, Handle) { this.constructor = constructor; this.Handle = Handle; } @@ -649,9 +640,9 @@ RegisteredRawConstPointer.prototype.toWireType = function(destructors, o) { // TODO: null pointers are always zero (not a Handle) in Javascript /*global ___staticPointerCast: false*/ function __embind_register_class( - classType, - pointerType, - constPointerType, + rawType, + rawPointerType, + rawConstPointerType, isPolymorphic, name, destructor @@ -681,7 +672,7 @@ function __embind_register_class( Handle.prototype.clone = function() { if (!this.ptr) { - throw new BindingError(classType.name + ' instance already deleted'); + throw new BindingError(type.name + ' instance already deleted'); } var clone = Object.create(Handle.prototype); @@ -702,7 +693,7 @@ function __embind_register_class( // 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'); + throw new BindingError(type.name + ' instance already deleted'); // todo: but 'type' hasn't been resolved!?! } this.count.value -= 1; @@ -717,24 +708,24 @@ function __embind_register_class( return body.apply(this, arguments); }); constructor.prototype = Handle.prototype; - constructor.classType = classType; + constructor.rawType = rawType; - registerType(classType, name, new RegisteredClassInstance(pointerType, constructor, Handle)); - registerType(pointerType, name + '*', new RegisteredRawPointer(isPolymorphic, classType, Handle)); - registerType(constPointerType, name + ' const*', new RegisteredRawConstPointer()); + var classType = registerType(rawType, name, new RegisteredClassInstance(constructor, Handle)); + registerType(rawPointerType, name + '*', new RegisteredRawPointer(isPolymorphic, classType, Handle)); + registerType(rawConstPointerType, name + ' const*', new RegisteredRawConstPointer()); exposePublicSymbol(name, constructor); } function __embind_register_class_constructor( - classType, + rawClassType, argCount, - argTypes, + rawArgTypes, constructor ) { - classType = requireRegisteredType(classType, 'class'); + var classType = requireRegisteredType(rawClassType, 'class'); var humanName = 'constructor ' + classType.name; - argTypes = requireArgumentTypes(argCount, argTypes, humanName); + var argTypes = requireArgumentTypes(argCount, rawArgTypes, humanName); constructor = FUNCTION_TABLE[constructor]; classType.constructor.body = function() { @@ -755,19 +746,19 @@ function __embind_register_class_constructor( } function __embind_register_class_method( - classType, + rawClassType, methodName, argCount, - argTypes, + rawArgTypes, invoker, memberFunctionSize, memberFunction ) { - classType = requireRegisteredType(classType, 'class'); + var classType = requireRegisteredType(rawClassType, 'class'); methodName = Pointer_stringify(methodName); var humanName = classType.name + '.' + methodName; - argTypes = requireArgumentTypes(argCount, argTypes, 'method ' + humanName); + var argTypes = requireArgumentTypes(argCount, rawArgTypes, 'method ' + humanName); invoker = FUNCTION_TABLE[invoker]; memberFunction = copyMemberPointer(memberFunction, memberFunctionSize); @@ -798,21 +789,21 @@ function __embind_register_class_method( }; } -function __embind_register_cast_method( - classType, +function __embind_register_raw_cast_method( + rawClassType, isPolymorphic, methodName, - returnType, + rawReturnType, invoker ) { - var classTypeImpl = requireRegisteredType(classType, 'class'); + var classType = requireRegisteredType(rawClassType, 'class'); methodName = Pointer_stringify(methodName); - var humanName = classTypeImpl.name + '.' + methodName; + var humanName = classType.name + '.' + methodName; - var returnTypeImpl = requireRegisteredType(returnType, 'method ' + humanName + ' return value'); + var returnType = requireRegisteredType(rawReturnType, 'method ' + humanName + ' return value'); invoker = FUNCTION_TABLE[invoker]; - classTypeImpl.Handle.prototype[methodName] = function() { + classType.Handle.prototype[methodName] = function() { if (!this.ptr) { throw new BindingError('cannot call emscripten binding method ' + humanName + ' on deleted object'); } @@ -822,44 +813,44 @@ function __embind_register_cast_method( 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 derivation = Module.__getDerivationPath(rawReturnType, runtimeType); // downcast is valid var size = derivation.size(); derivation.delete(); if (size === 0) { - derivation = Module.__getDerivationPath(runtimeType, returnType); // upcast is valid + derivation = Module.__getDerivationPath(runtimeType, rawReturnType); // upcast is valid size = derivation.size(); derivation.delete(); if (size === 0) { // todo: return zero - return returnTypeImpl.fromWireType(0); + return returnType.fromWireType(0); } } } var args = new Array(1); args[0] = this.ptr; - var rv = returnTypeImpl.fromWireType(invoker.apply(null, args)); + var rv = returnType.fromWireType(invoker.apply(null, args)); rv.count = this.count; this.count.value ++; return rv; }; } -function __embind_register_pointer_cast_method( - pointerType, - returnType, +function __embind_register_smart_cast_method( + rawPointerType, + rawReturnType, returnPointeeType, isPolymorphic, methodName, invoker ) { - var pointerTypeImpl = requireRegisteredType(pointerType, 'smart pointer class'); + var pointerType = requireRegisteredType(rawPointerType, 'smart pointer class'); methodName = Pointer_stringify(methodName); - var humanName = pointerTypeImpl.name + '.' + methodName; + var humanName = pointerType.name + '.' + methodName; - var returnTypeImpl = requireRegisteredType(returnType, 'method ' + humanName + ' return value'); + var returnType = requireRegisteredType(rawReturnType, 'method ' + humanName + ' return value'); invoker = FUNCTION_TABLE[invoker]; - pointerTypeImpl.Handle.prototype[methodName] = function() { + pointerType.Handle.prototype[methodName] = function() { if (!this.ptr) { throw new BindingError('cannot call emscripten binding method ' + humanName + ' on deleted object'); } @@ -887,36 +878,36 @@ function __embind_register_pointer_cast_method( args[0] = newPtr; args[1] = this.smartPointer; invoker.apply(null,args); - var rv = returnTypeImpl.fromWireType(newPtr); + var rv = returnType.fromWireType(newPtr); return rv; }; } function __embind_register_class_classmethod( - classType, + rawClassType, methodName, argCount, - argTypes, + rawArgTypes, invoker, fn ) { - classType = requireRegisteredType(classType, 'class'); + var classType = requireRegisteredType(rawClassType, 'class'); methodName = Pointer_stringify(methodName); var humanName = classType.name + '.' + methodName; - argTypes = requireArgumentTypes(argCount, argTypes, 'classmethod ' + humanName); + var argTypes = requireArgumentTypes(argCount, rawArgTypes, 'classmethod ' + humanName); invoker = FUNCTION_TABLE[invoker]; classType.constructor[methodName] = makeInvoker(humanName, argCount, argTypes, invoker, fn); } function __embind_register_class_operator_call( - classType, + rawClassType, argCount, - argTypes, + rawArgTypes, invoker ) { - classType = requireRegisteredType(classType, 'class'); + var classType = requireRegisteredType(rawClassType, 'class'); var humanName = classType.name + '.' + 'operator_call'; - argTypes = requireArgumentTypes(argCount, argTypes, 'method ' + humanName); + var argTypes = requireArgumentTypes(argCount, rawArgTypes, 'method ' + humanName); invoker = FUNCTION_TABLE[invoker]; @@ -942,12 +933,12 @@ function __embind_register_class_operator_call( } function __embind_register_class_operator_array_get( - classType, + rawClassType, elementType, indexType, invoker ) { - classType = requireRegisteredType(classType, 'class'); + var classType = requireRegisteredType(rawClassType, 'class'); indexType = requireRegisteredType(indexType, 'array access index ' + classType.name); elementType = requireRegisteredType(elementType, 'array access element' + classType.name); invoker = FUNCTION_TABLE[invoker]; @@ -974,13 +965,13 @@ function __embind_register_class_operator_array_get( } function __embind_register_class_operator_array_set( - classType, + rawClassType, elementType, - indexType, + rawIndexType, invoker ) { - classType = requireRegisteredType(classType, 'class'); - indexType = requireRegisteredType(indexType, 'array access index ' + classType.name); + var classType = requireRegisteredType(rawClassType, 'class'); + var indexType = requireRegisteredType(rawIndexType, 'array access index ' + classType.name); elementType = requireRegisteredType(elementType, 'array access element ' + classType.name); invoker = FUNCTION_TABLE[invoker]; var humanName = classType.name + '.' + 'operator_array_get'; @@ -1007,18 +998,18 @@ function __embind_register_class_operator_array_set( } function __embind_register_class_field( - classType, + rawClassType, fieldName, - fieldType, + rawFieldType, getter, setter, memberPointerSize, memberPointer ) { - classType = requireRegisteredType(classType, 'class'); + var classType = requireRegisteredType(rawClassType, 'class'); fieldName = Pointer_stringify(fieldName); var humanName = classType.name + '.' + fieldName; - fieldType = requireRegisteredType(fieldType, 'field ' + humanName); + var fieldType = requireRegisteredType(rawFieldType, 'field ' + humanName); getter = FUNCTION_TABLE[getter]; setter = FUNCTION_TABLE[setter]; memberPointer = copyMemberPointer(memberPointer, memberPointerSize); @@ -1056,28 +1047,28 @@ RegisteredEnum.prototype.fromWireType = function(c) { }; function __embind_register_enum( - enumType, + rawType, name ) { name = Pointer_stringify(name); var newEnum = new RegisteredEnum(); - registerType(enumType, name, newEnum); + registerType(rawType, name, newEnum); exposePublicSymbol(name, newEnum.constructor); } function __embind_register_enum_value( - enumType, + rawType, name, enumValue ) { - enumType = requireRegisteredType(enumType, 'enum'); + var type = requireRegisteredType(rawType, 'enum'); name = Pointer_stringify(name); - var Enum = enumType.constructor; + var Enum = type.constructor; - var Value = Object.create(enumType.constructor.prototype, { + var Value = Object.create(type.constructor.prototype, { value: {value: enumValue}, - constructor: {value: createNamedFunction(enumType.name + '_' + name, function() {})}, + constructor: {value: createNamedFunction(type.name + '_' + name, function() {})}, }); Enum.values[enumValue] = Value; Enum[name] = Value; @@ -1097,7 +1088,7 @@ RegisteredInterface.prototype.toWireType = function(destructors, o) { }; function __embind_register_interface( - interfaceType, + rawType, name, constructor, destructor @@ -1106,6 +1097,6 @@ function __embind_register_interface( constructor = FUNCTION_TABLE[constructor]; destructor = FUNCTION_TABLE[destructor]; - registerType(interfaceType, name, new RegisteredInterface(constructor, destructor)); + registerType(rawType, name, new RegisteredInterface(constructor, destructor)); } diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index c37aed24c..a5985af50 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -123,14 +123,14 @@ namespace emscripten { size_t memberFunctionSize, void* memberFunction); - void _embind_register_cast_method( + void _embind_register_raw_cast_method( TYPEID classType, bool isPolymorphic, const char* methodName, TYPEID returnType, GenericFunction invoker); - void _embind_register_pointer_cast_method( + void _embind_register_smart_cast_method( TYPEID pointerType, TYPEID returnType, TYPEID returnPointeeType, @@ -614,7 +614,7 @@ namespace emscripten { using namespace internal; typedef typename ReturnType::element_type ReturnPointeeType; typedef typename PointerType::element_type PointeeType; - _embind_register_pointer_cast_method( + _embind_register_smart_cast_method( TypeID::get(), TypeID::get(), TypeID::get(), @@ -761,7 +761,7 @@ namespace emscripten { class_& cast(const char* methodName) { using namespace internal; - _embind_register_cast_method( + _embind_register_raw_cast_method( TypeID::get(), std::is_polymorphic::value, methodName, From eac098fc2418d7c3c543daebdff8fd6c341312ce Mon Sep 17 00:00:00 2001 From: Bill Welden Date: Wed, 2 Jan 2013 12:57:31 -0800 Subject: [PATCH 071/258] Major re-factoring for auto downcasting --- src/embind/embind.js | 323 +++++++++++++++++++++---------------- system/lib/embind/bind.cpp | 4 +- 2 files changed, 184 insertions(+), 143 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index fb56e7c0c..c0bceb9b4 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -219,16 +219,16 @@ function makeInvoker(name, argCount, argTypes, invoker, fn) { }; } -function __embind_register_function(name, argCount, rawArgTypes, invoker, fn) { +function __embind_register_function(name, argCount, rawArgTypes, rawInvoker, fn) { name = Pointer_stringify(name); - invoker = FUNCTION_TABLE[invoker]; + rawInvoker = FUNCTION_TABLE[rawInvoker]; var argTypes = requireArgumentTypes(argCount, rawArgTypes, name); - exposePublicSymbol(name, makeInvoker(name, argCount, argTypes, invoker, fn)); + exposePublicSymbol(name, makeInvoker(name, argCount, argTypes, rawInvoker, fn)); } -function RegisteredTuple(constructor, destructor) { - this.constructor = constructor; - this.destructor = destructor; +function RegisteredTuple(rawConstructor, rawDestructor) { + this.rawConstructor = rawConstructor; + this.rawDestructor = rawDestructor; this.elements = []; } @@ -237,11 +237,11 @@ RegisteredTuple.prototype.toWireType = function(destructors, o) { if (len !== o.length) { throw new TypeError("Incorrect number of tuple elements"); } - var ptr = this.constructor(); + var ptr = this.rawConstructor(); for (var i = 0; i < len; ++i) { this.elements[i].write(ptr, o[i]); } - destructors.push(this.destructor); + destructors.push(this.rawDestructor); destructors.push(ptr); return ptr; }; @@ -252,15 +252,15 @@ RegisteredTuple.prototype.fromWireType = function(ptr) { for (var i = 0; i < len; ++i) { rv[i] = this.elements[i].read(ptr); } - this.destructor(ptr); + this.rawDestructor(ptr); return rv; }; -function __embind_register_tuple(type, name, constructor, destructor) { +function __embind_register_tuple(rawType, name, rawConstructor, rawDestructor) { name = Pointer_stringify(name); - constructor = FUNCTION_TABLE[constructor]; - destructor = FUNCTION_TABLE[destructor]; - registerType(type, name, new RegisteredTuple(constructor, destructor)); + rawConstructor = FUNCTION_TABLE[rawConstructor]; + rawDestructor = FUNCTION_TABLE[rawDestructor]; + registerType(rawType, name, new RegisteredTuple(rawConstructor, rawDestructor)); } function copyMemberPointer(memberPointer, memberPointerSize) { @@ -273,55 +273,55 @@ function copyMemberPointer(memberPointer, memberPointerSize) { } function __embind_register_tuple_element( - tupleType, - elementType, + rawTupleType, + rawType, getter, setter, memberPointerSize, memberPointer ) { - tupleType = requireRegisteredType(tupleType, 'tuple'); - elementType = requireRegisteredType(elementType, "element " + tupleType.name + "[" + tupleType.elements.length + "]"); + var tupleType = requireRegisteredType(rawTupleType, 'tuple'); + var type = requireRegisteredType(rawType, "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)); + return type.fromWireType(getter(ptr, memberPointer)); }, write: function(ptr, o) { var destructors = []; - setter(ptr, memberPointer, elementType.toWireType(destructors, o)); + setter(ptr, memberPointer, type.toWireType(destructors, o)); runDestructors(destructors); } }); } function __embind_register_tuple_element_accessor( - tupleType, - elementType, - staticGetter, + rawTupleType, + rawElementType, + rawStaticGetter, getterSize, getter, - staticSetter, + rawStaticSetter, setterSize, setter ) { - tupleType = requireRegisteredType(tupleType, 'tuple'); - elementType = requireRegisteredType(elementType, "element " + tupleType.name + "[" + tupleType.elements.length + "]"); - staticGetter = FUNCTION_TABLE[staticGetter]; + var tupleType = requireRegisteredType(rawTupleType, 'tuple'); + var elementType = requireRegisteredType(rawElementType, "element " + tupleType.name + "[" + tupleType.elements.length + "]"); + rawStaticGetter = FUNCTION_TABLE[rawStaticGetter]; getter = copyMemberPointer(getter, getterSize); - staticSetter = FUNCTION_TABLE[staticSetter]; + rawStaticSetter = FUNCTION_TABLE[rawStaticSetter]; setter = copyMemberPointer(setter, setterSize); tupleType.elements.push({ read: function(ptr) { - return elementType.fromWireType(staticGetter(ptr, HEAP32[getter >> 2])); + return elementType.fromWireType(rawStaticGetter(ptr, HEAP32[getter >> 2])); }, write: function(ptr, o) { var destructors = []; - staticSetter( + rawStaticSetter( ptr, HEAP32[setter >> 2], elementType.toWireType(destructors, o)); @@ -330,9 +330,9 @@ function __embind_register_tuple_element_accessor( }); } -function RegisteredStruct(constructor, destructor) { - this.constructor = constructor; - this.destructor = destructor; +function RegisteredStruct(rawConstructor, rawDestructor) { + this.rawConstructor = rawConstructor; + this.rawDestructor = rawDestructor; this.fields = {}; } @@ -343,11 +343,11 @@ RegisteredStruct.prototype.toWireType = function(destructors, o) { throw new TypeError('Missing field'); } } - var ptr = this.constructor(); + var ptr = this.rawConstructor(); for (fieldName in fields) { fields[fieldName].write(ptr, o[fieldName]); } - destructors.push(this.destructor); + destructors.push(this.rawDestructor); destructors.push(ptr); return ptr; }; @@ -358,68 +358,85 @@ RegisteredStruct.prototype.fromWireType = function(ptr) { for (var i in fields) { rv[i] = fields[i].read(ptr); } - this.destructor(ptr); + this.rawDestructor(ptr); return rv; }; function __embind_register_struct( - type, + rawType, name, - constructor, - destructor + rawConstructor, + rawDestructor ) { name = Pointer_stringify(name); - constructor = FUNCTION_TABLE[constructor]; - destructor = FUNCTION_TABLE[destructor]; + rawConstructor = FUNCTION_TABLE[rawConstructor]; + rawDestructor = FUNCTION_TABLE[rawDestructor]; - registerType(type, name, new RegisteredStruct(constructor, destructor)); + registerType(rawType, name, new RegisteredStruct(rawConstructor, rawDestructor)); } function __embind_register_struct_field( - structType, + rawStructType, fieldName, - fieldType, - getter, - setter, + rawFieldType, + rawGetter, + rawSetter, memberPointerSize, memberPointer ) { - structType = requireRegisteredType(structType, 'struct'); + var structType = requireRegisteredType(rawStructType, 'struct'); fieldName = Pointer_stringify(fieldName); - fieldType = requireRegisteredType(fieldType, 'field "' + structType.name + '.' + fieldName + '"'); - getter = FUNCTION_TABLE[getter]; - setter = FUNCTION_TABLE[setter]; + var fieldType = requireRegisteredType(rawFieldType, 'field "' + structType.name + '.' + fieldName + '"'); + rawGetter = FUNCTION_TABLE[rawGetter]; + rawSetter = FUNCTION_TABLE[rawSetter]; memberPointer = copyMemberPointer(memberPointer, memberPointerSize); structType.fields[fieldName] = { read: function(ptr) { - return fieldType.fromWireType(getter(ptr, memberPointer)); + return fieldType.fromWireType(rawGetter(ptr, memberPointer)); }, write: function(ptr, o) { var destructors = []; - setter(ptr, memberPointer, fieldType.toWireType(destructors, o)); + rawSetter(ptr, memberPointer, fieldType.toWireType(destructors, o)); runDestructors(destructors); } }; } -function RegisteredSmartPointer(getPointee, Handle, destructor, isPolymorphic, pointeeType) { - this.getPointee = getPointee; +function RegisteredPointer(Handle, isPolymorphic, isSmartPointer, rawGetPointee, rawDestructor) { this.Handle = Handle; - this.destructor = destructor; this.isPolymorphic = isPolymorphic; - this.pointeeType = pointeeType; + this.isSmartPointer = isSmartPointer; + this.rawGetPointee = rawGetPointee; + this.rawDestructor = rawDestructor; } -RegisteredSmartPointer.prototype.toWireType = function(destructors, o) { +RegisteredPointer.prototype.toWireType = function(destructors, o) { if (null === o) { return 0; } else { - return o.smartPointer; + if (this.isSmartPointer) { + return o.smartPointer; + } else { + return o.ptr; // this allows passing a smart pointer to a raw pointer parameter (but it's not much of a conversion!)s/r + } } }; -RegisteredSmartPointer.prototype.fromWireType = function(ptr) { +RegisteredPointer.prototype.getPointee = function(ptr) { + if (this.rawGetPointee) { + ptr = this.rawGetPointee(ptr); + } + return ptr; +}; + +RegisteredPointer.prototype.destructor = function(ptr) { + if (this.rawDestructor) { + this.rawDestructor(ptr); + } +}; + +RegisteredPointer.prototype.fromWireType = function(ptr) { if (!this.getPointee(ptr)) { this.destructor(ptr); return null; @@ -427,39 +444,56 @@ RegisteredSmartPointer.prototype.fromWireType = function(ptr) { return new this.Handle(ptr); }; -RegisteredSmartPointer.prototype.fromWireTypeAutoDowncast = function(ptr) { - if (!this.getPointee(ptr)) { - this.destructor(ptr); - return null; - } +RegisteredPointer.prototype.getDynamicRawPointerType = function(ptr) { + var type = null; if (this.isPolymorphic) { - // todo: clean up this code - var pointee = this.getPointee(ptr); - var toRawType = ___getDynamicPointerType(pointee); - if (toRawType === null || toRawType === this.pointeeType.rawType) { - return new this.Handle(ptr); + if (this.rawGetPointee) { + type = ___getDynamicPointerType(this.rawGetPointee(ptr)); + } else { + type = ___getDynamicPointerType(ptr); } - // todo: getDerivationPath is expensive -- cache the result - var derivation = Module.__getDerivationPath(toRawType, this.pointeeType.rawType); - var rawCandidateType = null; - var candidateType = null; + } + return type; +}; + +RegisteredPointer.prototype.getDynamicDowncastType = function(ptr) { + var downcastType = null; + var type = this.getDynamicRawPointerType(ptr); + if (type && type != this.pointeeType.rawType) { + var derivation = Module.__getDerivationPath(type, this.pointeeType.rawType); for (var i = 0; i < derivation.size(); i++) { - rawCandidateType = derivation.at(i); - candidateType = typeRegistry[rawCandidateType]; - if (candidateType) { + downcastType = typeRegistry[derivation.at(i)]; + if (downcastType) { break; } } derivation.delete(); - if (candidateType === null) { - return new this.Handle(ptr); - } + } + return downcastType; +}; + +RegisteredPointer.prototype.fromWireTypeAutoDowncast = function(ptr) { // ptr is a raw pointer (or a raw smartpointer) + var handle; + if (!this.getPointee(ptr)) { + this.destructor(ptr); + return null; + } + var toType = this.getDynamicDowncastType(ptr); + if (toType) { // todo: need to clone the ptr here (really??) // todo: we need to release the pre-cast pointer, don't we? how did this get past the tests? - return candidateType.smartPointerType.fromWireType(ptr); + var fromType = this.pointeeType; + if (this.isSmartPointer) { + handle = toType.smartPointerType.fromWireType(ptr); + } else { + handle = toType.fromWireType(ptr); + } + // todo: staticPointerCast can return -1 or -2!! Throw appropriate exception + handle.ptr = ___staticPointerCast(handle.ptr, fromType.rawType, toType.rawType); } else { - return new this.Handle(ptr); + handle = this.fromWireType(ptr); } + return handle; }; function __embind_register_smart_ptr( @@ -467,18 +501,18 @@ function __embind_register_smart_ptr( rawPointeeType, isPolymorphic, name, - destructor, - getPointee + rawDestructor, + rawGetPointee ) { name = Pointer_stringify(name); var pointeeType = requireRegisteredType(rawPointeeType, 'class'); - destructor = FUNCTION_TABLE[destructor]; - getPointee = FUNCTION_TABLE[getPointee]; + rawDestructor = FUNCTION_TABLE[rawDestructor]; + rawGetPointee = FUNCTION_TABLE[rawGetPointee]; var Handle = createNamedFunction(name, function(ptr) { this.count = {value: 1}; this.smartPointer = ptr; // std::shared_ptr* - this.ptr = getPointee(ptr); // T* + this.ptr = rawGetPointee(ptr); // T* }); // TODO: test for SmartPtr.prototype.constructor property? @@ -506,12 +540,14 @@ function __embind_register_smart_ptr( this.count.value -= 1; if (0 === this.count.value) { - destructor(this.smartPointer); + rawDestructor(this.smartPointer); } this.smartPointer = undefined; this.ptr = undefined; }; - pointeeType.smartPointerType = registerType(rawType, name, new RegisteredSmartPointer(getPointee, Handle, destructor, isPolymorphic, pointeeType)); + var registeredPointer = new RegisteredPointer(Handle, isPolymorphic, true, rawGetPointee, rawDestructor); + registeredPointer.pointeeType = pointeeType; + pointeeType.smartPointerType = registerType(rawType, name, registeredPointer); } function RegisteredRawPointer(isPolymorphic, classType, Handle) { @@ -636,7 +672,6 @@ RegisteredRawConstPointer.prototype.toWireType = function(destructors, o) { return o.ptr; }; ->>>>>>> Refactoring preparatory to code clean-up (no functional changes, all tests pass). // TODO: null pointers are always zero (not a Handle) in Javascript /*global ___staticPointerCast: false*/ function __embind_register_class( @@ -645,10 +680,10 @@ function __embind_register_class( rawConstPointerType, isPolymorphic, name, - destructor + rawDestructor ) { name = Pointer_stringify(name); - destructor = FUNCTION_TABLE[destructor]; + rawDestructor = FUNCTION_TABLE[rawDestructor]; var Handle = createNamedFunction(name, function(ptr) { var h = function() { @@ -698,35 +733,42 @@ function __embind_register_class( this.count.value -= 1; if (0 === this.count.value) { - destructor(this.count.ptr); + rawDestructor(this.count.ptr); } this.ptr = undefined; }; - var constructor = createNamedFunction(name, function() { - var body = constructor.body; + var registeredClass = new RegisteredPointer(Handle, isPolymorphic, false); + var type = registerType(rawType, name, registeredClass); + registeredClass.pointeeType = type; + var registeredClass = new RegisteredPointer(Handle, isPolymorphic, false); + registerType(rawPointerType, name + '*', registeredClass); + registeredClass.pointeeType = type; + // todo: implement const pointers (no modification Javascript side) + var registeredClass = new RegisteredPointer(Handle, isPolymorphic, false); + registerType(rawConstPointerType, name + ' const*', registeredClass); + registeredClass.pointeeType = type; + + type.constructor = createNamedFunction(type.name, function() { + var body = type.constructor.body; return body.apply(this, arguments); }); - constructor.prototype = Handle.prototype; - constructor.rawType = rawType; + type.constructor.prototype = type.Handle.prototype; + type.constructor.type = type; - var classType = registerType(rawType, name, new RegisteredClassInstance(constructor, Handle)); - registerType(rawPointerType, name + '*', new RegisteredRawPointer(isPolymorphic, classType, Handle)); - registerType(rawConstPointerType, name + ' const*', new RegisteredRawConstPointer()); - - exposePublicSymbol(name, constructor); + exposePublicSymbol(name, type.constructor); } function __embind_register_class_constructor( rawClassType, argCount, rawArgTypes, - constructor + rawConstructor ) { var classType = requireRegisteredType(rawClassType, 'class'); var humanName = 'constructor ' + classType.name; var argTypes = requireArgumentTypes(argCount, rawArgTypes, humanName); - constructor = FUNCTION_TABLE[constructor]; + rawConstructor = FUNCTION_TABLE[rawConstructor]; classType.constructor.body = function() { if (arguments.length !== argCount - 1) { @@ -738,7 +780,7 @@ function __embind_register_class_constructor( args[i-1] = argTypes[i].toWireType(destructors, arguments[i-1]); } - var ptr = constructor.apply(null, args); + var ptr = rawConstructor.apply(null, args); runDestructors(destructors); return classType.Handle.call(this, ptr); @@ -750,7 +792,7 @@ function __embind_register_class_method( methodName, argCount, rawArgTypes, - invoker, + rawInvoker, memberFunctionSize, memberFunction ) { @@ -759,7 +801,7 @@ function __embind_register_class_method( var humanName = classType.name + '.' + methodName; var argTypes = requireArgumentTypes(argCount, rawArgTypes, 'method ' + humanName); - invoker = FUNCTION_TABLE[invoker]; + rawInvoker = FUNCTION_TABLE[rawInvoker]; memberFunction = copyMemberPointer(memberFunction, memberFunctionSize); classType.Handle.prototype[methodName] = function() { @@ -778,7 +820,7 @@ function __embind_register_class_method( for (var i = 1; i < argCount; ++i) { args[i + 1] = argTypes[i].toWireType(destructors, arguments[i-1]); } - var rv = invoker.apply(null, args); + var rv = rawInvoker.apply(null, args); if (argTypes[0].fromWireTypeAutoDowncast) { rv = argTypes[0].fromWireTypeAutoDowncast(rv); } else { @@ -794,14 +836,14 @@ function __embind_register_raw_cast_method( isPolymorphic, methodName, rawReturnType, - invoker + rawInvoker ) { var classType = requireRegisteredType(rawClassType, 'class'); methodName = Pointer_stringify(methodName); var humanName = classType.name + '.' + methodName; var returnType = requireRegisteredType(rawReturnType, 'method ' + humanName + ' return value'); - invoker = FUNCTION_TABLE[invoker]; + rawInvoker = FUNCTION_TABLE[rawInvoker]; classType.Handle.prototype[methodName] = function() { if (!this.ptr) { @@ -828,7 +870,7 @@ function __embind_register_raw_cast_method( } var args = new Array(1); args[0] = this.ptr; - var rv = returnType.fromWireType(invoker.apply(null, args)); + var rv = returnType.fromWireType(rawInvoker.apply(null, args)); rv.count = this.count; this.count.value ++; return rv; @@ -841,14 +883,14 @@ function __embind_register_smart_cast_method( returnPointeeType, isPolymorphic, methodName, - invoker + rawInvoker ) { var pointerType = requireRegisteredType(rawPointerType, 'smart pointer class'); methodName = Pointer_stringify(methodName); var humanName = pointerType.name + '.' + methodName; var returnType = requireRegisteredType(rawReturnType, 'method ' + humanName + ' return value'); - invoker = FUNCTION_TABLE[invoker]; + rawInvoker = FUNCTION_TABLE[rawInvoker]; pointerType.Handle.prototype[methodName] = function() { if (!this.ptr) { @@ -877,9 +919,8 @@ function __embind_register_smart_cast_method( var newPtr = _malloc(8); args[0] = newPtr; args[1] = this.smartPointer; - invoker.apply(null,args); - var rv = returnType.fromWireType(newPtr); - return rv; + rawInvoker.apply(null,args); + return returnType.fromWireType(newPtr); }; } @@ -888,27 +929,27 @@ function __embind_register_class_classmethod( methodName, argCount, rawArgTypes, - invoker, + rawInvoker, fn ) { var classType = requireRegisteredType(rawClassType, 'class'); methodName = Pointer_stringify(methodName); var humanName = classType.name + '.' + methodName; var argTypes = requireArgumentTypes(argCount, rawArgTypes, 'classmethod ' + humanName); - invoker = FUNCTION_TABLE[invoker]; - classType.constructor[methodName] = makeInvoker(humanName, argCount, argTypes, invoker, fn); + rawInvoker = FUNCTION_TABLE[rawInvoker]; + classType.constructor[methodName] = makeInvoker(humanName, argCount, argTypes, rawInvoker, fn); } function __embind_register_class_operator_call( rawClassType, argCount, rawArgTypes, - invoker + rawInvoker ) { var classType = requireRegisteredType(rawClassType, 'class'); var humanName = classType.name + '.' + 'operator_call'; var argTypes = requireArgumentTypes(argCount, rawArgTypes, 'method ' + humanName); - invoker = FUNCTION_TABLE[invoker]; + rawInvoker = FUNCTION_TABLE[rawInvoker]; classType.Handle.prototype.operator_call = function() { @@ -926,7 +967,7 @@ function __embind_register_class_operator_call( args[i] = argTypes[i].toWireType(destructors, arguments[i-1]); } - var rv = argTypes[0].fromWireType(invoker.apply(null, args)); + var rv = argTypes[0].fromWireType(rawInvoker.apply(null, args)); runDestructors(destructors); return rv; }; @@ -936,12 +977,12 @@ function __embind_register_class_operator_array_get( rawClassType, elementType, indexType, - invoker + rawInvoker ) { var classType = requireRegisteredType(rawClassType, 'class'); indexType = requireRegisteredType(indexType, 'array access index ' + classType.name); elementType = requireRegisteredType(elementType, 'array access element' + classType.name); - invoker = FUNCTION_TABLE[invoker]; + rawInvoker = FUNCTION_TABLE[rawInvoker]; var humanName = classType.name + '.' + 'operator_array_get'; classType.Handle.prototype.array_get = function() { @@ -958,7 +999,7 @@ function __embind_register_class_operator_array_get( args[0] = this.ptr; args[1] = indexType.toWireType(destructors, arguments[0]); - var rv = elementType.fromWireType(invoker.apply(null, args)); + var rv = elementType.fromWireType(rawInvoker.apply(null, args)); runDestructors(destructors); return rv; }; @@ -968,12 +1009,12 @@ function __embind_register_class_operator_array_set( rawClassType, elementType, rawIndexType, - invoker + rawInvoker ) { var classType = requireRegisteredType(rawClassType, 'class'); var indexType = requireRegisteredType(rawIndexType, 'array access index ' + classType.name); elementType = requireRegisteredType(elementType, 'array access element ' + classType.name); - invoker = FUNCTION_TABLE[invoker]; + rawInvoker = FUNCTION_TABLE[rawInvoker]; var humanName = classType.name + '.' + 'operator_array_get'; classType.Handle.prototype.array_set = function() { @@ -991,7 +1032,7 @@ function __embind_register_class_operator_array_set( args[1] = indexType.toWireType(destructors, arguments[0]); args[2] = elementType.toWireType(destructors, arguments[1]); - var rv = elementType.fromWireType(invoker.apply(null, args)); + var rv = elementType.fromWireType(rawInvoker.apply(null, args)); runDestructors(destructors); return rv; }; @@ -1057,32 +1098,32 @@ function __embind_register_enum( } function __embind_register_enum_value( - rawType, + rawEnumType, name, enumValue ) { - var type = requireRegisteredType(rawType, 'enum'); + var enumType = requireRegisteredType(rawEnumType, 'enum'); name = Pointer_stringify(name); - var Enum = type.constructor; + var Enum = enumType.constructor; - var Value = Object.create(type.constructor.prototype, { + var Value = Object.create(enumType.constructor.prototype, { value: {value: enumValue}, - constructor: {value: createNamedFunction(type.name + '_' + name, function() {})}, + constructor: {value: createNamedFunction(enumType.name + '_' + name, function() {})}, }); Enum.values[enumValue] = Value; Enum[name] = Value; } -function RegisteredInterface(constructor, destructor) { - this.constructor = constructor; - this.destructor = destructor; +function RegisteredInterface(rawConstructor, rawDestructor) { + this.rawConstructor = rawConstructor; + this.rawDestructor = rawDestructor; } RegisteredInterface.prototype.toWireType = function(destructors, o) { var handle = __emval_register(o); - var ptr = this.constructor(handle); - destructors.push(this.destructor); + var ptr = this.rawConstructor(handle); + destructors.push(this.rawDestructor); destructors.push(ptr); return ptr; }; @@ -1090,13 +1131,13 @@ RegisteredInterface.prototype.toWireType = function(destructors, o) { function __embind_register_interface( rawType, name, - constructor, - destructor + rawConstructor, + rawDestructor ) { name = Pointer_stringify(name); - constructor = FUNCTION_TABLE[constructor]; - destructor = FUNCTION_TABLE[destructor]; + rawConstructor = FUNCTION_TABLE[rawConstructor]; + rawDestructor = FUNCTION_TABLE[rawDestructor]; - registerType(rawType, name, new RegisteredInterface(constructor, destructor)); + registerType(rawType, name, new RegisteredInterface(rawConstructor, rawDestructor)); } diff --git a/system/lib/embind/bind.cpp b/system/lib/embind/bind.cpp index bc1e09ee4..c69c43992 100755 --- a/system/lib/embind/bind.cpp +++ b/system/lib/embind/bind.cpp @@ -161,12 +161,12 @@ namespace emscripten { offset = __cxxabiv1::__pathOffset(paths[i]); } else { if (offset != __cxxabiv1::__pathOffset(paths[i])) { - return (void *)-2; // ambiguous cast -- throw instead? + return (void *)-2; } } } if (offset < 0) { - return (void *)-1; // types are not related -- throw instead? + return (void *)-1; } if (p == 0) { return (void *)0; From ff79babe5d207214631fbb8520caf93bcd7dab82 Mon Sep 17 00:00:00 2001 From: Bill Welden Date: Wed, 2 Jan 2013 13:12:05 -0800 Subject: [PATCH 072/258] Fixed a lint error --- src/embind/embind.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index c0bceb9b4..c9a6a4f6e 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -459,7 +459,7 @@ RegisteredPointer.prototype.getDynamicRawPointerType = function(ptr) { RegisteredPointer.prototype.getDynamicDowncastType = function(ptr) { var downcastType = null; var type = this.getDynamicRawPointerType(ptr); - if (type && type != this.pointeeType.rawType) { + if (type && type !== this.pointeeType.rawType) { var derivation = Module.__getDerivationPath(type, this.pointeeType.rawType); for (var i = 0; i < derivation.size(); i++) { downcastType = typeRegistry[derivation.at(i)]; From 32ccee42ba92e8efc9b467af5e90bf424e0bb016 Mon Sep 17 00:00:00 2001 From: mey Date: Wed, 2 Jan 2013 13:53:03 -0800 Subject: [PATCH 073/258] Adding a register map function, fixing some errors in array access functions and invokers. --- system/include/emscripten/bind.h | 38 +++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index a5985af50..a2c717f19 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -346,27 +347,26 @@ namespace emscripten { } }; - template + template struct ArrayAccessGetInvoker { static typename internal::BindingType::WireType invoke( ClassType* ptr, - size_t index, - typename internal::BindingType + typename internal::BindingType::WireType index ) { return internal::BindingType::toWireType( - (*ptr)[index] + (*ptr)[internal::BindingType::fromWireType(index)] ); } }; - template + template struct ArrayAccessSetInvoker { static void invoke( ClassType* ptr, - size_t index, + typename internal::BindingType::WireType index, typename internal::BindingType::WireType item ) { - (*ptr)[index] = internal::BindingType::fromWireType(item); + (*ptr)[internal::BindingType::fromWireType(index)] = internal::BindingType::fromWireType(item); } }; @@ -742,7 +742,7 @@ namespace emscripten { TypeID::get(), TypeID::get(), TypeID::get(), - reinterpret_cast(&internal::ArrayAccessGetInvoker::invoke)); + reinterpret_cast(&internal::ArrayAccessGetInvoker::invoke)); } template @@ -753,7 +753,7 @@ namespace emscripten { TypeID::get(), TypeID::get(), TypeID::get(), - reinterpret_cast(&internal::ArrayAccessSetInvoker::invoke)); + reinterpret_cast(&internal::ArrayAccessSetInvoker::invoke)); return *this; } @@ -790,8 +790,6 @@ namespace emscripten { class_> register_vector(const char* name) { using namespace std; typedef vector VecType; - typedef typename vector::iterator IterType; - typedef typename vector::const_iterator ConstIterType; void (VecType::*push_back)(const T&) = &VecType::push_back; const T& (VecType::*at)(size_t) const = &VecType::at; @@ -807,6 +805,24 @@ namespace emscripten { return c; } + //////////////////////////////////////////////////////////////////////////////// + // MAPS + //////////////////////////////////////////////////////////////////////////////// + template + class_> register_map(const char* name) { + using namespace std; + typedef map MapType; + + auto c = class_(name) + .method("size", &MapType::size) + .template arrayoperatorget() + .template arrayoperatorset() + ; + + return c; + } + + //////////////////////////////////////////////////////////////////////////////// // ENUMS //////////////////////////////////////////////////////////////////////////////// From 5ea90f9345ebfb271dfd960f77fb0c669d30fca6 Mon Sep 17 00:00:00 2001 From: Bill Welden Date: Thu, 3 Jan 2013 08:35:48 -0800 Subject: [PATCH 074/258] Support for automatic upcasting of pointer parameters passed to C++. --- src/embind/embind.js | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index c9a6a4f6e..b9a16fbb1 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -206,7 +206,11 @@ function makeInvoker(name, argCount, argTypes, invoker, fn) { var args = new Array(argCount); args[0] = fn; for (var i = 1; i < argCount; ++i) { - args[i] = argTypes[i].toWireType(destructors, arguments[i-1]); + if (argTypes[i].toWireTypeAutoUpcast) { + args[i] = argTypes[i].toWireTypeAutoUpcast(destructors, arguments[i-1]); + } else { + args[i] = argTypes[i].toWireType(destructors, arguments[i-1]); + } } var rv = invoker.apply(null, args); if (argTypes[0].fromWireTypeAutoDowncast) { @@ -411,6 +415,7 @@ function RegisteredPointer(Handle, isPolymorphic, isSmartPointer, rawGetPointee, this.rawDestructor = rawDestructor; } +// todo: this will go away RegisteredPointer.prototype.toWireType = function(destructors, o) { if (null === o) { return 0; @@ -423,6 +428,21 @@ RegisteredPointer.prototype.toWireType = function(destructors, o) { } }; +// todo: distinguish ptr and rawPtr +RegisteredPointer.prototype.toWireTypeAutoUpcast = function(destructors, o) { + if (this.isSmartPointer) { + return this.toWireType(destructors, o); // for now + } else { + if (o.pointeeType.isPolymorphic) { + var dynamicType = o.pointeeType.getDynamicRawPointerType(o.ptr); + return ___staticPointerCast(o.ptr, dynamicType, this.pointeeType.rawType); + } else { + return ___staticPointerCast(o.ptr, o.pointeeType.rawType, this.pointeeType.rawType); + } + // todo: this cast can fail + } + }; + RegisteredPointer.prototype.getPointee = function(ptr) { if (this.rawGetPointee) { ptr = this.rawGetPointee(ptr); @@ -444,6 +464,7 @@ RegisteredPointer.prototype.fromWireType = function(ptr) { return new this.Handle(ptr); }; +// todo: could this return the actual type if not polymorphic? RegisteredPointer.prototype.getDynamicRawPointerType = function(ptr) { var type = null; if (this.isPolymorphic) { @@ -496,6 +517,7 @@ RegisteredPointer.prototype.fromWireTypeAutoDowncast = function(ptr) { // ptr is return handle; }; +// todo: don't need isPolymorphic parameter any more function __embind_register_smart_ptr( rawType, rawPointeeType, @@ -513,6 +535,7 @@ function __embind_register_smart_ptr( this.count = {value: 1}; this.smartPointer = ptr; // std::shared_ptr* this.ptr = rawGetPointee(ptr); // T* + this.pointeeType = pointeeType; }); // TODO: test for SmartPtr.prototype.constructor property? @@ -673,7 +696,6 @@ RegisteredRawConstPointer.prototype.toWireType = function(destructors, o) { }; // TODO: null pointers are always zero (not a Handle) in Javascript -/*global ___staticPointerCast: false*/ function __embind_register_class( rawType, rawPointerType, @@ -696,6 +718,7 @@ function __embind_register_class( h.count = {value: 1, ptr: ptr }; h.ptr = ptr; + h.pointeeType = type; // set below for(var prop in Handle.prototype) { var dp = Object.getOwnPropertyDescriptor(Handle.prototype, prop); @@ -738,6 +761,7 @@ function __embind_register_class( this.ptr = undefined; }; + // todo: clean this up! var registeredClass = new RegisteredPointer(Handle, isPolymorphic, false); var type = registerType(rawType, name, registeredClass); registeredClass.pointeeType = type; From cd3be4e930df1358d9b07746f774a6ad00f3f39c Mon Sep 17 00:00:00 2001 From: Todd Lee Date: Tue, 8 Jan 2013 00:31:46 -0800 Subject: [PATCH 075/258] clean-up old interface binding implementation --- system/include/emscripten/bind.h | 78 ++++++++++---------------------- system/lib/embind/bind.cpp | 4 +- 2 files changed, 28 insertions(+), 54 deletions(-) diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index a2c717f19..592928f5e 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -902,42 +902,17 @@ namespace emscripten { } //////////////////////////////////////////////////////////////////////////////// - // INTERFACES + // NEW INTERFACE //////////////////////////////////////////////////////////////////////////////// - template - class wrapper : public InterfaceType { + class JSInterface { public: - wrapper() {} // to avoid error "call to implicitly deleted construrtor..." - - wrapper(InterfaceType* interface) { - cloneInterface(interface); + JSInterface(internal::EM_VAL handle) { + initialize(handle); } - // Not necessary in any example so far, but appeases a compiler warning. - virtual ~wrapper() {} - - typedef InterfaceType interface; - - template - static std::shared_ptr cloneToSharedPtr(InterfaceType& i) { - ConcreteWrapperType* cw = new ConcreteWrapperType(&i); - InterfaceType* ip = dynamic_cast(cw); - return std::shared_ptr(ip); - } - - template - static std::shared_ptr cloneToSharedWrapperPtr(InterfaceType& i) { - return std::make_shared(&i); - } - - 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); + JSInterface(JSInterface& obj) { + jsobj = obj.jsobj; } template @@ -946,14 +921,20 @@ namespace emscripten { return Caller::call(*jsobj, name, args...); } - protected: - void cloneInterface(InterfaceType* interface) { - // why dynamic_cast causes javascript crash? - wrapper* iw = static_cast*>(interface); - jsobj = iw->jsobj; + static std::shared_ptr cloneToSharedPtr(JSInterface& i) { + return std::make_shared(i); } private: + void initialize(internal::EM_VAL handle) { + if (jsobj) { + internal::_embind_fatal_error( + "Cannot initialize interface wrapper twice", + "JSInterface"); + } + jsobj = val::take_ownership(handle); + } + // this class only exists because you can't partially specialize function templates template struct Caller { @@ -972,8 +953,7 @@ namespace emscripten { void assertInitialized() { if (!jsobj) { internal::_embind_fatal_error( - "Cannot invoke call on uninitialized interface wrapper.", - typeid(InterfaceType).name()); + "Cannot invoke call on uninitialized Javascript interface wrapper.", "JSInterface"); } } @@ -981,25 +961,17 @@ namespace emscripten { }; namespace internal { - template - WrapperType* create_interface_wrapper(EM_VAL e) { - WrapperType* p = new WrapperType; - p->initialize(e); - return p; - } + extern JSInterface* create_js_interface(EM_VAL e); } - template - class interface { + class register_js_interface { public: - typedef typename WrapperType::interface InterfaceType; - - interface(const char* name) { + register_js_interface() { _embind_register_interface( - internal::TypeID::get(), - name, - reinterpret_cast(&internal::create_interface_wrapper), - reinterpret_cast(&internal::raw_destructor)); + internal::TypeID::get(), + "JSInterface", + reinterpret_cast(&internal::create_js_interface), + reinterpret_cast(&internal::raw_destructor)); } }; } diff --git a/system/lib/embind/bind.cpp b/system/lib/embind/bind.cpp index c69c43992..469245058 100755 --- a/system/lib/embind/bind.cpp +++ b/system/lib/embind/bind.cpp @@ -220,8 +220,10 @@ namespace emscripten { // developers, but perhaps the double underscore will scare them away from calling it. function("__getDerivationPath", &__getDerivationPath); })); + } - + JSInterface* create_js_interface(EM_VAL e) { + return new JSInterface(e); } } } From cd535236f6bb28f7a3856fff148a49c261563db5 Mon Sep 17 00:00:00 2001 From: Bill Welden Date: Tue, 8 Jan 2013 07:45:50 -0800 Subject: [PATCH 076/258] Auto upcast of pointer parameters to C++ routines. --- src/embind/embind.js | 41 +++++++++++++++++++++----------- system/include/emscripten/bind.h | 27 +++++++++++++++++---- system/lib/embind/bind.cpp | 7 ++++-- 3 files changed, 55 insertions(+), 20 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index b9a16fbb1..e02b9e014 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -407,11 +407,12 @@ function __embind_register_struct_field( }; } -function RegisteredPointer(Handle, isPolymorphic, isSmartPointer, rawGetPointee, rawDestructor) { +function RegisteredPointer(Handle, isPolymorphic, isSmartPointer, rawGetPointee, rawConstructor, rawDestructor) { this.Handle = Handle; this.isPolymorphic = isPolymorphic; this.isSmartPointer = isSmartPointer; this.rawGetPointee = rawGetPointee; + this.rawConstructor = rawConstructor; this.rawDestructor = rawDestructor; } @@ -428,19 +429,29 @@ RegisteredPointer.prototype.toWireType = function(destructors, o) { } }; -// todo: distinguish ptr and rawPtr -RegisteredPointer.prototype.toWireTypeAutoUpcast = function(destructors, o) { - if (this.isSmartPointer) { - return this.toWireType(destructors, o); // for now - } else { - if (o.pointeeType.isPolymorphic) { - var dynamicType = o.pointeeType.getDynamicRawPointerType(o.ptr); - return ___staticPointerCast(o.ptr, dynamicType, this.pointeeType.rawType); - } else { - return ___staticPointerCast(o.ptr, o.pointeeType.rawType, this.pointeeType.rawType); - } - // todo: this cast can fail +RegisteredPointer.prototype.toWireTypeAutoUpcast = function(destructors, handle) { + var fromRawType; + if (!handle) { + return null; } + if (handle.pointeeType.isPolymorphic) { + fromRawType = handle.pointeeType.getDynamicRawPointerType(handle.ptr); + } else { + fromRawType = handle.pointeeType.rawType; + } + if (fromRawType == this.pointeeType.rawType) { + return this.isSmartPointer ? handle.smartPointer : handle.ptr; + } + var ptr = ___staticPointerCast(handle.ptr, fromRawType, this.pointeeType.rawType); + if (this.isSmartPointer) { + var smartPtr = _malloc(16); + // todo: this does not create a pointer that shares the reference count !?!? + handle.pointeeType.smartPointerType.rawConstructor(smartPtr, ptr); + ptr = smartPtr; + destructors.push(handle.pointeeType.smartPointerType.rawDestructor); + destructors.push(ptr); + } + return ptr; }; RegisteredPointer.prototype.getPointee = function(ptr) { @@ -523,11 +534,13 @@ function __embind_register_smart_ptr( rawPointeeType, isPolymorphic, name, + rawConstructor, rawDestructor, rawGetPointee ) { name = Pointer_stringify(name); var pointeeType = requireRegisteredType(rawPointeeType, 'class'); + rawConstructor = FUNCTION_TABLE[rawConstructor]; rawDestructor = FUNCTION_TABLE[rawDestructor]; rawGetPointee = FUNCTION_TABLE[rawGetPointee]; @@ -568,7 +581,7 @@ function __embind_register_smart_ptr( this.smartPointer = undefined; this.ptr = undefined; }; - var registeredPointer = new RegisteredPointer(Handle, isPolymorphic, true, rawGetPointee, rawDestructor); + var registeredPointer = new RegisteredPointer(Handle, isPolymorphic, true, rawGetPointee, rawConstructor, rawDestructor); registeredPointer.pointeeType = pointeeType; pointeeType.smartPointerType = registerType(rawType, name, registeredPointer); } diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index 592928f5e..bbce9a939 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -98,6 +98,7 @@ namespace emscripten { TYPEID pointeeType, bool isPolymorphic, const char* pointerName, + GenericFunction constructor, GenericFunction destructor, GenericFunction getPointee); @@ -282,9 +283,18 @@ namespace emscripten { return *static_cast(&from); }; + template + struct performShared { + static std::shared_ptr cast(std::shared_ptr from) { + return std::dynamic_pointer_cast(from); + }; + }; + template - std::shared_ptr performSharedStaticCast(std::shared_ptr from) { - return std::shared_ptr(from, static_cast(from.get())); + struct performShared { + static std::shared_ptr cast(std::shared_ptr from) { + return std::shared_ptr(from, static_cast(from.get())); + }; }; template @@ -312,6 +322,14 @@ namespace emscripten { ); } + template + void nullDeallocator(PointerType* p) {} + + template + typename std::shared_ptr raw_smart_pointer_constructor(PointerType *ptr, void (PointerType*)) { + return std::shared_ptr(ptr, nullDeallocator); + } + template void raw_destructor(ClassType* ptr) { delete ptr; @@ -604,6 +622,7 @@ namespace emscripten { TypeID::get(), std::is_polymorphic::value, name, + reinterpret_cast(&raw_smart_pointer_constructor), reinterpret_cast(&raw_destructor), reinterpret_cast(&get_pointee)); @@ -620,8 +639,8 @@ namespace emscripten { TypeID::get(), std::is_polymorphic::value, methodName, - reinterpret_cast(&performSharedStaticCast)); - return *this; + reinterpret_cast(&performShared::value>::cast)); + return *this; } }; diff --git a/system/lib/embind/bind.cpp b/system/lib/embind/bind.cpp index 469245058..a4b67fa5e 100755 --- a/system/lib/embind/bind.cpp +++ b/system/lib/embind/bind.cpp @@ -190,8 +190,6 @@ 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 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. int ret = (int)__staticPointerCast((void *)p, __getDynamicPointerType(p), to); if (ret < 0) { return 0; @@ -214,11 +212,16 @@ namespace emscripten { return name; } + int EMSCRIPTEN_KEEPALIVE __peek32(int p) { + return *(int *)p; + } + EMSCRIPTEN_BINDINGS(([]() { // We bind __getDerivationPath in order to take advantage of the std::vector to Javascript array // conversion for the return value. This has the unfortunate side-effect of exposing it to third party // developers, but perhaps the double underscore will scare them away from calling it. function("__getDerivationPath", &__getDerivationPath); + function("__peek32", &__peek32); })); } From 27100ac1f5bce45edf19c52d17f76a0c319aba9e Mon Sep 17 00:00:00 2001 From: Bill Welden Date: Tue, 8 Jan 2013 07:47:45 -0800 Subject: [PATCH 077/258] Fix a lint error. --- src/embind/embind.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index e02b9e014..1f8a66547 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -439,7 +439,7 @@ RegisteredPointer.prototype.toWireTypeAutoUpcast = function(destructors, handle) } else { fromRawType = handle.pointeeType.rawType; } - if (fromRawType == this.pointeeType.rawType) { + if (fromRawType === this.pointeeType.rawType) { return this.isSmartPointer ? handle.smartPointer : handle.ptr; } var ptr = ___staticPointerCast(handle.ptr, fromRawType, this.pointeeType.rawType); From f4480b5d9b8281911249eca0bff4fa86f249a467 Mon Sep 17 00:00:00 2001 From: Bill Welden Date: Tue, 8 Jan 2013 09:44:16 -0800 Subject: [PATCH 078/258] Revert "Fix a lint error." This reverts commit f7b63f2ed5f8fea7ed9c8d5b9116bc10bfbab7b7. --- src/embind/embind.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 1f8a66547..e02b9e014 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -439,7 +439,7 @@ RegisteredPointer.prototype.toWireTypeAutoUpcast = function(destructors, handle) } else { fromRawType = handle.pointeeType.rawType; } - if (fromRawType === this.pointeeType.rawType) { + if (fromRawType == this.pointeeType.rawType) { return this.isSmartPointer ? handle.smartPointer : handle.ptr; } var ptr = ___staticPointerCast(handle.ptr, fromRawType, this.pointeeType.rawType); From 84b49c917c177e9fa49798b45a543f3ce68bf30b Mon Sep 17 00:00:00 2001 From: Bill Welden Date: Tue, 8 Jan 2013 09:44:28 -0800 Subject: [PATCH 079/258] Revert "Auto upcast of pointer parameters to C++ routines." This reverts commit 64cc5c47bed294d5efa2433655552530c0242503. --- src/embind/embind.js | 39 +++++++++++--------------------- system/include/emscripten/bind.h | 27 ++++------------------ system/lib/embind/bind.cpp | 7 ++---- 3 files changed, 19 insertions(+), 54 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index e02b9e014..b9a16fbb1 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -407,12 +407,11 @@ function __embind_register_struct_field( }; } -function RegisteredPointer(Handle, isPolymorphic, isSmartPointer, rawGetPointee, rawConstructor, rawDestructor) { +function RegisteredPointer(Handle, isPolymorphic, isSmartPointer, rawGetPointee, rawDestructor) { this.Handle = Handle; this.isPolymorphic = isPolymorphic; this.isSmartPointer = isSmartPointer; this.rawGetPointee = rawGetPointee; - this.rawConstructor = rawConstructor; this.rawDestructor = rawDestructor; } @@ -429,29 +428,19 @@ RegisteredPointer.prototype.toWireType = function(destructors, o) { } }; -RegisteredPointer.prototype.toWireTypeAutoUpcast = function(destructors, handle) { - var fromRawType; - if (!handle) { - return null; - } - if (handle.pointeeType.isPolymorphic) { - fromRawType = handle.pointeeType.getDynamicRawPointerType(handle.ptr); - } else { - fromRawType = handle.pointeeType.rawType; - } - if (fromRawType == this.pointeeType.rawType) { - return this.isSmartPointer ? handle.smartPointer : handle.ptr; - } - var ptr = ___staticPointerCast(handle.ptr, fromRawType, this.pointeeType.rawType); +// todo: distinguish ptr and rawPtr +RegisteredPointer.prototype.toWireTypeAutoUpcast = function(destructors, o) { if (this.isSmartPointer) { - var smartPtr = _malloc(16); - // todo: this does not create a pointer that shares the reference count !?!? - handle.pointeeType.smartPointerType.rawConstructor(smartPtr, ptr); - ptr = smartPtr; - destructors.push(handle.pointeeType.smartPointerType.rawDestructor); - destructors.push(ptr); + return this.toWireType(destructors, o); // for now + } else { + if (o.pointeeType.isPolymorphic) { + var dynamicType = o.pointeeType.getDynamicRawPointerType(o.ptr); + return ___staticPointerCast(o.ptr, dynamicType, this.pointeeType.rawType); + } else { + return ___staticPointerCast(o.ptr, o.pointeeType.rawType, this.pointeeType.rawType); + } + // todo: this cast can fail } - return ptr; }; RegisteredPointer.prototype.getPointee = function(ptr) { @@ -534,13 +523,11 @@ function __embind_register_smart_ptr( rawPointeeType, isPolymorphic, name, - rawConstructor, rawDestructor, rawGetPointee ) { name = Pointer_stringify(name); var pointeeType = requireRegisteredType(rawPointeeType, 'class'); - rawConstructor = FUNCTION_TABLE[rawConstructor]; rawDestructor = FUNCTION_TABLE[rawDestructor]; rawGetPointee = FUNCTION_TABLE[rawGetPointee]; @@ -581,7 +568,7 @@ function __embind_register_smart_ptr( this.smartPointer = undefined; this.ptr = undefined; }; - var registeredPointer = new RegisteredPointer(Handle, isPolymorphic, true, rawGetPointee, rawConstructor, rawDestructor); + var registeredPointer = new RegisteredPointer(Handle, isPolymorphic, true, rawGetPointee, rawDestructor); registeredPointer.pointeeType = pointeeType; pointeeType.smartPointerType = registerType(rawType, name, registeredPointer); } diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index bbce9a939..592928f5e 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -98,7 +98,6 @@ namespace emscripten { TYPEID pointeeType, bool isPolymorphic, const char* pointerName, - GenericFunction constructor, GenericFunction destructor, GenericFunction getPointee); @@ -283,18 +282,9 @@ namespace emscripten { return *static_cast(&from); }; - template - struct performShared { - static std::shared_ptr cast(std::shared_ptr from) { - return std::dynamic_pointer_cast(from); - }; - }; - template - struct performShared { - static std::shared_ptr cast(std::shared_ptr from) { - return std::shared_ptr(from, static_cast(from.get())); - }; + std::shared_ptr performSharedStaticCast(std::shared_ptr from) { + return std::shared_ptr(from, static_cast(from.get())); }; template @@ -322,14 +312,6 @@ namespace emscripten { ); } - template - void nullDeallocator(PointerType* p) {} - - template - typename std::shared_ptr raw_smart_pointer_constructor(PointerType *ptr, void (PointerType*)) { - return std::shared_ptr(ptr, nullDeallocator); - } - template void raw_destructor(ClassType* ptr) { delete ptr; @@ -622,7 +604,6 @@ namespace emscripten { TypeID::get(), std::is_polymorphic::value, name, - reinterpret_cast(&raw_smart_pointer_constructor), reinterpret_cast(&raw_destructor), reinterpret_cast(&get_pointee)); @@ -639,8 +620,8 @@ namespace emscripten { TypeID::get(), std::is_polymorphic::value, methodName, - reinterpret_cast(&performShared::value>::cast)); - return *this; + reinterpret_cast(&performSharedStaticCast)); + return *this; } }; diff --git a/system/lib/embind/bind.cpp b/system/lib/embind/bind.cpp index a4b67fa5e..469245058 100755 --- a/system/lib/embind/bind.cpp +++ b/system/lib/embind/bind.cpp @@ -190,6 +190,8 @@ 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 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. int ret = (int)__staticPointerCast((void *)p, __getDynamicPointerType(p), to); if (ret < 0) { return 0; @@ -212,16 +214,11 @@ namespace emscripten { return name; } - int EMSCRIPTEN_KEEPALIVE __peek32(int p) { - return *(int *)p; - } - EMSCRIPTEN_BINDINGS(([]() { // We bind __getDerivationPath in order to take advantage of the std::vector to Javascript array // conversion for the return value. This has the unfortunate side-effect of exposing it to third party // developers, but perhaps the double underscore will scare them away from calling it. function("__getDerivationPath", &__getDerivationPath); - function("__peek32", &__peek32); })); } From cfb3df1b48f9e1b9c59fd27e0d2608c7fe29a78e Mon Sep 17 00:00:00 2001 From: Bill Welden Date: Tue, 8 Jan 2013 10:33:53 -0800 Subject: [PATCH 080/258] Revert "Revert "Auto upcast of pointer parameters to C++ routines."" This reverts commit 07e0daa5aab716b38acf9041a8baec3816976579. --- src/embind/embind.js | 41 +++++++++++++++++++++----------- system/include/emscripten/bind.h | 27 +++++++++++++++++---- system/lib/embind/bind.cpp | 7 ++++-- 3 files changed, 55 insertions(+), 20 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index b9a16fbb1..e02b9e014 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -407,11 +407,12 @@ function __embind_register_struct_field( }; } -function RegisteredPointer(Handle, isPolymorphic, isSmartPointer, rawGetPointee, rawDestructor) { +function RegisteredPointer(Handle, isPolymorphic, isSmartPointer, rawGetPointee, rawConstructor, rawDestructor) { this.Handle = Handle; this.isPolymorphic = isPolymorphic; this.isSmartPointer = isSmartPointer; this.rawGetPointee = rawGetPointee; + this.rawConstructor = rawConstructor; this.rawDestructor = rawDestructor; } @@ -428,19 +429,29 @@ RegisteredPointer.prototype.toWireType = function(destructors, o) { } }; -// todo: distinguish ptr and rawPtr -RegisteredPointer.prototype.toWireTypeAutoUpcast = function(destructors, o) { - if (this.isSmartPointer) { - return this.toWireType(destructors, o); // for now - } else { - if (o.pointeeType.isPolymorphic) { - var dynamicType = o.pointeeType.getDynamicRawPointerType(o.ptr); - return ___staticPointerCast(o.ptr, dynamicType, this.pointeeType.rawType); - } else { - return ___staticPointerCast(o.ptr, o.pointeeType.rawType, this.pointeeType.rawType); - } - // todo: this cast can fail +RegisteredPointer.prototype.toWireTypeAutoUpcast = function(destructors, handle) { + var fromRawType; + if (!handle) { + return null; } + if (handle.pointeeType.isPolymorphic) { + fromRawType = handle.pointeeType.getDynamicRawPointerType(handle.ptr); + } else { + fromRawType = handle.pointeeType.rawType; + } + if (fromRawType == this.pointeeType.rawType) { + return this.isSmartPointer ? handle.smartPointer : handle.ptr; + } + var ptr = ___staticPointerCast(handle.ptr, fromRawType, this.pointeeType.rawType); + if (this.isSmartPointer) { + var smartPtr = _malloc(16); + // todo: this does not create a pointer that shares the reference count !?!? + handle.pointeeType.smartPointerType.rawConstructor(smartPtr, ptr); + ptr = smartPtr; + destructors.push(handle.pointeeType.smartPointerType.rawDestructor); + destructors.push(ptr); + } + return ptr; }; RegisteredPointer.prototype.getPointee = function(ptr) { @@ -523,11 +534,13 @@ function __embind_register_smart_ptr( rawPointeeType, isPolymorphic, name, + rawConstructor, rawDestructor, rawGetPointee ) { name = Pointer_stringify(name); var pointeeType = requireRegisteredType(rawPointeeType, 'class'); + rawConstructor = FUNCTION_TABLE[rawConstructor]; rawDestructor = FUNCTION_TABLE[rawDestructor]; rawGetPointee = FUNCTION_TABLE[rawGetPointee]; @@ -568,7 +581,7 @@ function __embind_register_smart_ptr( this.smartPointer = undefined; this.ptr = undefined; }; - var registeredPointer = new RegisteredPointer(Handle, isPolymorphic, true, rawGetPointee, rawDestructor); + var registeredPointer = new RegisteredPointer(Handle, isPolymorphic, true, rawGetPointee, rawConstructor, rawDestructor); registeredPointer.pointeeType = pointeeType; pointeeType.smartPointerType = registerType(rawType, name, registeredPointer); } diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index 592928f5e..bbce9a939 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -98,6 +98,7 @@ namespace emscripten { TYPEID pointeeType, bool isPolymorphic, const char* pointerName, + GenericFunction constructor, GenericFunction destructor, GenericFunction getPointee); @@ -282,9 +283,18 @@ namespace emscripten { return *static_cast(&from); }; + template + struct performShared { + static std::shared_ptr cast(std::shared_ptr from) { + return std::dynamic_pointer_cast(from); + }; + }; + template - std::shared_ptr performSharedStaticCast(std::shared_ptr from) { - return std::shared_ptr(from, static_cast(from.get())); + struct performShared { + static std::shared_ptr cast(std::shared_ptr from) { + return std::shared_ptr(from, static_cast(from.get())); + }; }; template @@ -312,6 +322,14 @@ namespace emscripten { ); } + template + void nullDeallocator(PointerType* p) {} + + template + typename std::shared_ptr raw_smart_pointer_constructor(PointerType *ptr, void (PointerType*)) { + return std::shared_ptr(ptr, nullDeallocator); + } + template void raw_destructor(ClassType* ptr) { delete ptr; @@ -604,6 +622,7 @@ namespace emscripten { TypeID::get(), std::is_polymorphic::value, name, + reinterpret_cast(&raw_smart_pointer_constructor), reinterpret_cast(&raw_destructor), reinterpret_cast(&get_pointee)); @@ -620,8 +639,8 @@ namespace emscripten { TypeID::get(), std::is_polymorphic::value, methodName, - reinterpret_cast(&performSharedStaticCast)); - return *this; + reinterpret_cast(&performShared::value>::cast)); + return *this; } }; diff --git a/system/lib/embind/bind.cpp b/system/lib/embind/bind.cpp index 469245058..a4b67fa5e 100755 --- a/system/lib/embind/bind.cpp +++ b/system/lib/embind/bind.cpp @@ -190,8 +190,6 @@ 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 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. int ret = (int)__staticPointerCast((void *)p, __getDynamicPointerType(p), to); if (ret < 0) { return 0; @@ -214,11 +212,16 @@ namespace emscripten { return name; } + int EMSCRIPTEN_KEEPALIVE __peek32(int p) { + return *(int *)p; + } + EMSCRIPTEN_BINDINGS(([]() { // We bind __getDerivationPath in order to take advantage of the std::vector to Javascript array // conversion for the return value. This has the unfortunate side-effect of exposing it to third party // developers, but perhaps the double underscore will scare them away from calling it. function("__getDerivationPath", &__getDerivationPath); + function("__peek32", &__peek32); })); } From 2665a390d2ec975c9a516fc98761b96c07c38085 Mon Sep 17 00:00:00 2001 From: Bill Welden Date: Tue, 8 Jan 2013 10:34:03 -0800 Subject: [PATCH 081/258] Revert "Revert "Fix a lint error."" This reverts commit 6bcd2b18c56448502c69bbfc960a3044c8955fd8. --- src/embind/embind.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index e02b9e014..1f8a66547 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -439,7 +439,7 @@ RegisteredPointer.prototype.toWireTypeAutoUpcast = function(destructors, handle) } else { fromRawType = handle.pointeeType.rawType; } - if (fromRawType == this.pointeeType.rawType) { + if (fromRawType === this.pointeeType.rawType) { return this.isSmartPointer ? handle.smartPointer : handle.ptr; } var ptr = ___staticPointerCast(handle.ptr, fromRawType, this.pointeeType.rawType); From 34d32868ad6f6efdf466f90370a38dc26eb01159 Mon Sep 17 00:00:00 2001 From: Bill Welden Date: Tue, 8 Jan 2013 15:05:35 -0800 Subject: [PATCH 082/258] New CastError exception when explicit or implicit cast cannot be completed. --- src/embind/embind.js | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 1f8a66547..c755e22b9 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -74,6 +74,17 @@ function requireArgumentTypes(argCount, rawArgTypes, name) { return argTypes; } +function staticPointerCast(from, fromType, toType) { + if (!from) { + return from; + } + var to = ___staticPointerCast(from, fromType, toType); + if (to <= 0) { + throw new CastError("Pointer conversion is not available"); + } + return to; +} + function RegisteredVoid() { } @@ -186,8 +197,10 @@ function __embind_register_emval(rawType, name) { } var BindingError = Error; +var CastError = Error; /** @expose */ Module.BindingError = BindingError; +Module.CastError = CastError; function runDestructors(destructors) { while (destructors.length) { @@ -442,10 +455,10 @@ RegisteredPointer.prototype.toWireTypeAutoUpcast = function(destructors, handle) if (fromRawType === this.pointeeType.rawType) { return this.isSmartPointer ? handle.smartPointer : handle.ptr; } - var ptr = ___staticPointerCast(handle.ptr, fromRawType, this.pointeeType.rawType); + var ptr = staticPointerCast(handle.ptr, fromRawType, this.pointeeType.rawType); if (this.isSmartPointer) { var smartPtr = _malloc(16); - // todo: this does not create a pointer that shares the reference count !?!? + // todo: this does not create a pointer that shares the reference count ! handle.pointeeType.smartPointerType.rawConstructor(smartPtr, ptr); ptr = smartPtr; destructors.push(handle.pointeeType.smartPointerType.rawDestructor); @@ -520,8 +533,7 @@ RegisteredPointer.prototype.fromWireTypeAutoDowncast = function(ptr) { // ptr is } else { handle = toType.fromWireType(ptr); } - // todo: staticPointerCast can return -1 or -2!! Throw appropriate exception - handle.ptr = ___staticPointerCast(handle.ptr, fromType.rawType, toType.rawType); + handle.ptr = staticPointerCast(handle.ptr, fromType.rawType, toType.rawType); } else { handle = this.fromWireType(ptr); } @@ -900,14 +912,14 @@ function __embind_register_raw_cast_method( size = derivation.size(); derivation.delete(); if (size === 0) { - // todo: return zero - return returnType.fromWireType(0); + throw new CastError("Pointer conversion is not available"); } } } var args = new Array(1); args[0] = this.ptr; - var rv = returnType.fromWireType(rawInvoker.apply(null, args)); + var ptr = rawInvoker.apply(null, args); + var rv = returnType.fromWireType(ptr); rv.count = this.count; this.count.value ++; return rv; @@ -938,7 +950,6 @@ function __embind_register_smart_cast_method( } 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(); @@ -948,16 +959,16 @@ function __embind_register_smart_cast_method( size = derivation.size(); derivation.delete(); if (size === 0) { - return 0; + throw new CastError("Pointer conversion is not available"); } } } var args = new Array(2); - var newPtr = _malloc(8); - args[0] = newPtr; + var ptr = _malloc(8); + args[0] = ptr; args[1] = this.smartPointer; rawInvoker.apply(null,args); - return returnType.fromWireType(newPtr); + return returnType.fromWireType(ptr); }; } From c59bb0d3975d7e15faaab87f785ae66bf6730f35 Mon Sep 17 00:00:00 2001 From: Bill Welden Date: Thu, 10 Jan 2013 09:57:32 -0800 Subject: [PATCH 083/258] Minor clean-up of auto upcasting. --- src/embind/embind.js | 24 +++++------------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index c755e22b9..c37ef55bf 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -211,6 +211,9 @@ function runDestructors(destructors) { } function makeInvoker(name, argCount, argTypes, invoker, fn) { + if (!FUNCTION_TABLE[fn]) { + throw new BindingError('function '+name+' is not defined'); + } return function() { if (arguments.length !== argCount - 1) { throw new BindingError('function ' + name + ' called with ' + arguments.length + ' arguments, expected ' + (argCount - 1)); @@ -219,11 +222,7 @@ function makeInvoker(name, argCount, argTypes, invoker, fn) { var args = new Array(argCount); args[0] = fn; for (var i = 1; i < argCount; ++i) { - if (argTypes[i].toWireTypeAutoUpcast) { - args[i] = argTypes[i].toWireTypeAutoUpcast(destructors, arguments[i-1]); - } else { - args[i] = argTypes[i].toWireType(destructors, arguments[i-1]); - } + args[i] = argTypes[i].toWireType(destructors, arguments[i-1]); } var rv = invoker.apply(null, args); if (argTypes[0].fromWireTypeAutoDowncast) { @@ -429,20 +428,7 @@ function RegisteredPointer(Handle, isPolymorphic, isSmartPointer, rawGetPointee, this.rawDestructor = rawDestructor; } -// todo: this will go away -RegisteredPointer.prototype.toWireType = function(destructors, o) { - if (null === o) { - return 0; - } else { - if (this.isSmartPointer) { - return o.smartPointer; - } else { - return o.ptr; // this allows passing a smart pointer to a raw pointer parameter (but it's not much of a conversion!)s/r - } - } -}; - -RegisteredPointer.prototype.toWireTypeAutoUpcast = function(destructors, handle) { +RegisteredPointer.prototype.toWireType = function(destructors, handle) { var fromRawType; if (!handle) { return null; From d0969fc651367d801d28146ef6f4dbe1d26f2aa2 Mon Sep 17 00:00:00 2001 From: Bill Welden Date: Fri, 11 Jan 2013 14:28:56 -0800 Subject: [PATCH 084/258] o Temporary shared pointers created to hold upcast argument values now share ownership with the original pointer. o New test cases for edge cases. o Other minor clean-up. --- src/embind/embind.js | 10 +++------- system/include/emscripten/bind.h | 10 ++++------ 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index c37ef55bf..39e120e01 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -443,9 +443,9 @@ RegisteredPointer.prototype.toWireType = function(destructors, handle) { } var ptr = staticPointerCast(handle.ptr, fromRawType, this.pointeeType.rawType); if (this.isSmartPointer) { + // todo: if ptr == handle.ptr, there's no need to allocate a new smartPtr! var smartPtr = _malloc(16); - // todo: this does not create a pointer that shares the reference count ! - handle.pointeeType.smartPointerType.rawConstructor(smartPtr, ptr); + handle.pointeeType.smartPointerType.rawConstructor(smartPtr, ptr, handle.smartPointer); ptr = smartPtr; destructors.push(handle.pointeeType.smartPointerType.rawDestructor); destructors.push(ptr); @@ -511,8 +511,6 @@ RegisteredPointer.prototype.fromWireTypeAutoDowncast = function(ptr) { // ptr is } var toType = this.getDynamicDowncastType(ptr); if (toType) { - // todo: need to clone the ptr here (really??) - // todo: we need to release the pre-cast pointer, don't we? how did this get past the tests? var fromType = this.pointeeType; if (this.isSmartPointer) { handle = toType.smartPointerType.fromWireType(ptr); @@ -526,11 +524,9 @@ RegisteredPointer.prototype.fromWireTypeAutoDowncast = function(ptr) { // ptr is return handle; }; -// todo: don't need isPolymorphic parameter any more function __embind_register_smart_ptr( rawType, rawPointeeType, - isPolymorphic, name, rawConstructor, rawDestructor, @@ -579,7 +575,7 @@ function __embind_register_smart_ptr( this.smartPointer = undefined; this.ptr = undefined; }; - var registeredPointer = new RegisteredPointer(Handle, isPolymorphic, true, rawGetPointee, rawConstructor, rawDestructor); + var registeredPointer = new RegisteredPointer(Handle, pointeeType.isPolymorphic, true, rawGetPointee, rawConstructor, rawDestructor); registeredPointer.pointeeType = pointeeType; pointeeType.smartPointerType = registerType(rawType, name, registeredPointer); } diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index bbce9a939..652fb1b46 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -96,7 +96,6 @@ namespace emscripten { void _embind_register_smart_ptr( TYPEID pointerType, TYPEID pointeeType, - bool isPolymorphic, const char* pointerName, GenericFunction constructor, GenericFunction destructor, @@ -322,12 +321,12 @@ namespace emscripten { ); } - template - void nullDeallocator(PointerType* p) {} +// template +// void nullDeallocator(PointerType* p) {} template - typename std::shared_ptr raw_smart_pointer_constructor(PointerType *ptr, void (PointerType*)) { - return std::shared_ptr(ptr, nullDeallocator); + typename std::shared_ptr raw_smart_pointer_constructor(PointerType *ptr, std::shared_ptr basePtr, void (PointerType*)) { + return std::shared_ptr(basePtr, ptr); } template @@ -620,7 +619,6 @@ namespace emscripten { _embind_register_smart_ptr( TypeID::get(), TypeID::get(), - std::is_polymorphic::value, name, reinterpret_cast(&raw_smart_pointer_constructor), reinterpret_cast(&raw_destructor), From c5b8e64a0becd50f51ae50b1f4480f3274f680f6 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Tue, 15 Jan 2013 17:59:07 -0800 Subject: [PATCH 085/258] bring emscripten::val closer to the set of primitive operations provided by JS on values --- src/embind/emval.js | 12 ++++++------ system/include/emscripten/val.h | 32 +++++++++++++++++++++----------- 2 files changed, 27 insertions(+), 17 deletions(-) diff --git a/src/embind/emval.js b/src/embind/emval.js index 49424631f..3df5a5406 100755 --- a/src/embind/emval.js +++ b/src/embind/emval.js @@ -41,6 +41,10 @@ function __emval_decref(handle) { } } +function __emval_new_array() { + return __emval_register([]); +} + function __emval_new_object() { return __emval_register({}); } @@ -49,12 +53,8 @@ function __emval_new_null() { return __emval_register(null); } -function __emval_new_long(value) { - return __emval_register(value); -} - -function __emval_new_cstring(str) { - return __emval_register(Pointer_stringify(str)); +function __emval_new_cstring(v) { + return __emval_register(Pointer_stringify(v)); } function __emval_has_property(handle, k) { diff --git a/system/include/emscripten/val.h b/system/include/emscripten/val.h index 8f7df8827..65f522663 100644 --- a/system/include/emscripten/val.h +++ b/system/include/emscripten/val.h @@ -11,10 +11,13 @@ namespace emscripten { void _emval_incref(EM_VAL value); void _emval_decref(EM_VAL value); + + EM_VAL _emval_new_array(); EM_VAL _emval_new_object(); EM_VAL _emval_new_null(); - EM_VAL _emval_new_long(long value); - EM_VAL _emval_new_cstring(const char* str); + EM_VAL _emval_new_cstring(const char*); + void _emval_take_value(TYPEID type/*, ...*/); + bool _emval_has_property(EM_VAL object, const char* key); EM_VAL _emval_get_property(EM_VAL object, const char* key); EM_VAL _emval_get_property_by_long(EM_VAL object, long key); @@ -45,28 +48,35 @@ namespace emscripten { class val { public: + static val array() { + return val(internal::_emval_new_array()); + } + static val object() { return val(internal::_emval_new_object()); - }; + } static val null() { return val(internal::_emval_new_null()); - }; + } 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)) - {} + template + explicit val(const T& value) { + typedef internal::BindingType BT; + auto taker = reinterpret_cast(&internal::_emval_take_value); + handle = taker(internal::TypeID::get(), BT::toWireType(value)); + } val() = delete; + val(const char* v) + : handle(internal::_emval_new_cstring(v)) + {} + val(const val& v) : handle(v.handle) { From d893eed16b2257c3ffbb186c08d45a053cc5de68 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Tue, 15 Jan 2013 18:09:22 -0800 Subject: [PATCH 086/258] Forgot to implement take_value :) --- src/embind/emval.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/embind/emval.js b/src/embind/emval.js index 3df5a5406..1c4955c55 100755 --- a/src/embind/emval.js +++ b/src/embind/emval.js @@ -57,6 +57,12 @@ function __emval_new_cstring(v) { return __emval_register(Pointer_stringify(v)); } +function __emval_take_value(type, v) { + type = requireRegisteredType(type, '_emval_take_value'); + v = type.fromWireType(v); + return __emval_register(v); +} + function __emval_has_property(handle, k) { k = Pointer_stringify(k); return _emval_handle_array[handle].value.hasOwnProperty(k); From 6c56bdda8d4f9ab4eb31e86ce71488106b061a33 Mon Sep 17 00:00:00 2001 From: Bill Welden Date: Wed, 16 Jan 2013 14:19:19 -0800 Subject: [PATCH 087/258] It is now possible to access base class properties and methods from a derived class (multiple inheritance not yet supported). --- src/embind/embind.js | 88 +++++++++++++++++++++++++++++++++++++- system/lib/embind/bind.cpp | 14 +++++- 2 files changed, 98 insertions(+), 4 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 39e120e01..d6464d12d 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -36,6 +36,89 @@ function _embind_repr(v) { var typeRegistry = {}; +function resolveType(type) { + function createInheritedFunctionOrProperty(baseClassName, name, baseClassPrototype) { + if (!type.Handle.prototype.hasOwnProperty(name)) { + var desc = Object.getOwnPropertyDescriptor(baseClassPrototype, baseClassName); + if (desc) { // some names in the list may not be present in this particular base class + if ('get' in desc || 'put' in desc) { + Object.defineProperty(type.Handle.prototype, name, desc); + } else { + type.Handle.prototype[name] = createNamedFunction(name, function() { + return baseClassPrototype[baseClassName].apply(this, arguments); // todo: need to upcast "this" pointer + }); + } + } + } + } + if (!type.resolved) { + var i, j, rawBaseClassType, baseClassType, name; + var names = []; + var qualifiedNames = {}; + var rawBaseClassTypes = Module.__getBaseClasses(type.rawType); + for (i = 0; i < rawBaseClassTypes.size(); i++) { + rawBaseClassType = rawBaseClassTypes.at(i); + baseClassType = typeRegistry[rawBaseClassType]; + if (baseClassType) { + resolveType(baseClassType); + var proto = baseClassType.Handle.prototype; + for (name in proto) { + if (proto.hasOwnProperty(name)) { + var qualifiedName = baseClassType.name + "_" + name; + // todo: figure out how to exclude casting functions + if (['clone', 'move', 'delete'].indexOf(name) < 0) { + if (type.Handle.prototype.hasOwnProperty(qualifiedName)) { + if (names.indexOf(name) < 0) { + names.push(name); + } + if (names.indexOf(qualifiedName) < 0) { + qualifiedNames[name] = qualifiedName; + } + } else { + if (names.indexOf(name) >= 0) { + if (names.indexOf(qualifiedName) < 0) { + qualifiedNames[name] = qualifiedName; + } + } else { + names.push(name); + if (type.Handle.prototype.hasOwnProperty(name) && names.indexOf(qualifiedName) < 0) { + qualifiedNames[name] = qualifiedName; + } + } + } + } + } + } + } + } + for (i = 0; i < rawBaseClassTypes.size(); i++) { + rawBaseClassType = rawBaseClassTypes.at(i); + baseClassType = typeRegistry[rawBaseClassType]; + if (baseClassType) { + proto = baseClassType.Handle.prototype; + for (name in qualifiedNames) { + if (qualifiedNames.hasOwnProperty(name)) { + createInheritedFunctionOrProperty(name, qualifiedNames[name], proto); + } + } + for (j = 0; j < names.length; j++) { + name = names[j]; + if (!(name in qualifiedNames)) { + createInheritedFunctionOrProperty(name, name, proto); + } + } + } + } + type.resolved = true; + } +} + +function resolveBindings() { + for (var rawType in typeRegistry) { + resolveType(typeRegistry[rawType]); + } +} + function registerType(rawType, name, registeredInstance) { if (!rawType) { throw new BindingError('type "' + name + '" must have a positive integer typeid pointer'); @@ -731,8 +814,8 @@ function __embind_register_class( var dp = Object.getOwnPropertyDescriptor(Handle.prototype, prop); Object.defineProperty(h, prop, dp); } - - return h; + + return h; }); Handle.prototype.clone = function() { @@ -772,6 +855,7 @@ function __embind_register_class( var registeredClass = new RegisteredPointer(Handle, isPolymorphic, false); var type = registerType(rawType, name, registeredClass); registeredClass.pointeeType = type; + var registeredClass = new RegisteredPointer(Handle, isPolymorphic, false); registerType(rawPointerType, name + '*', registeredClass); registeredClass.pointeeType = type; diff --git a/system/lib/embind/bind.cpp b/system/lib/embind/bind.cpp index a4b67fa5e..5e82bb7eb 100755 --- a/system/lib/embind/bind.cpp +++ b/system/lib/embind/bind.cpp @@ -10,7 +10,7 @@ using namespace emscripten; namespace __cxxabiv1 { - std::vector __getBaseClasses(const __class_type_info* cti) { + std::vector __internalGetBaseClasses(const __class_type_info* cti) { std::vector bases; const __si_class_type_info* scti = dynamic_cast(cti); @@ -48,7 +48,7 @@ namespace __cxxabiv1 { if (dv == bs) { paths.emplace_back(newPath); } else { - std::vector bases = __getBaseClasses(dv); + std::vector bases = __internalGetBaseClasses(dv); for (int i = 0; i < bases.size(); i++) { __getDerivationPaths(bases[i], bs, newPath, paths); } @@ -212,6 +212,15 @@ namespace emscripten { return name; } + std::vector __getBaseClasses(int tp) { + std::vector baseTypes = __internalGetBaseClasses((const __cxxabiv1::__class_type_info*)tp); + std::vector bases; + for (int j = 0; j < baseTypes.size(); j++) { + bases.emplace_back((int)baseTypes[j]); + } + return bases; + } + int EMSCRIPTEN_KEEPALIVE __peek32(int p) { return *(int *)p; } @@ -221,6 +230,7 @@ namespace emscripten { // conversion for the return value. This has the unfortunate side-effect of exposing it to third party // developers, but perhaps the double underscore will scare them away from calling it. function("__getDerivationPath", &__getDerivationPath); + function("__getBaseClasses", &__getBaseClasses); function("__peek32", &__peek32); })); } From 939e9dbbbdfc9b917a43d25ffb15a92147f0d5eb Mon Sep 17 00:00:00 2001 From: Bill Welden Date: Wed, 16 Jan 2013 14:25:42 -0800 Subject: [PATCH 088/258] Fix for lint warning. --- src/embind/embind.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index d6464d12d..319306e2a 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -52,7 +52,7 @@ function resolveType(type) { } } if (!type.resolved) { - var i, j, rawBaseClassType, baseClassType, name; + var i, j, rawBaseClassType, baseClassType, name, proto; var names = []; var qualifiedNames = {}; var rawBaseClassTypes = Module.__getBaseClasses(type.rawType); @@ -61,7 +61,7 @@ function resolveType(type) { baseClassType = typeRegistry[rawBaseClassType]; if (baseClassType) { resolveType(baseClassType); - var proto = baseClassType.Handle.prototype; + proto = baseClassType.Handle.prototype; for (name in proto) { if (proto.hasOwnProperty(name)) { var qualifiedName = baseClassType.name + "_" + name; From 08f3047321014c06892e830ba45374fe93ad0ed4 Mon Sep 17 00:00:00 2001 From: Bill Welden Date: Fri, 18 Jan 2013 07:55:10 -0800 Subject: [PATCH 089/258] Work in progress for access to properties under multiple inheritance --- src/embind/embind.js | 99 +++++++++++++++++++++++++++++--------------- 1 file changed, 66 insertions(+), 33 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 319306e2a..3f50ef219 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -37,56 +37,80 @@ function _embind_repr(v) { var typeRegistry = {}; function resolveType(type) { - function createInheritedFunctionOrProperty(baseClassName, name, baseClassPrototype) { + function createInheritedFunctionOrProperty(baseClassName, name, baseClassPrototype, baseClassType) { if (!type.Handle.prototype.hasOwnProperty(name)) { var desc = Object.getOwnPropertyDescriptor(baseClassPrototype, baseClassName); if (desc) { // some names in the list may not be present in this particular base class - if ('get' in desc || 'put' in desc) { + if (baseClassPrototype.constructor.memberType[baseClassName] == 'field') { + var newDescriptor = { + enumerable: true, + get: function() { + var save = this.ptr; + var baseClassPtr = ___staticPointerCast(this.ptr, type.rawType, baseClassType.rawType); + try { + this.ptr = baseClassPtr; + return desc.get(); + } finally { + this.ptr = save; // todo: still not good, if the base class routine calls through the current handle + } + }, + set: function(v) { + var save = this.ptr; + var baseClassPtr = ___staticPointerCast(this.ptr, type.rawType, baseClassType.rawType); + try { + this.ptr = baseClassPtr; + desc.set(v); + } finally { + this.ptr = save; // todo: still not good, if the base class routine calls through the current handle + } + } + }; Object.defineProperty(type.Handle.prototype, name, desc); - } else { + } else if (baseClassPrototype.constructor.memberType[baseClassName] == 'method') { type.Handle.prototype[name] = createNamedFunction(name, function() { - return baseClassPrototype[baseClassName].apply(this, arguments); // todo: need to upcast "this" pointer + var save = this.ptr; + var baseClassPtr = ___staticPointerCast(this.ptr, type.rawType, baseClassType.rawType); + try { + this.ptr = baseClassPtr; + return baseClassPrototype[baseClassName].apply(this, arguments); + } finally { + this.ptr = save; // todo: still not good, if the base class routine calls through the current handle + } }); } } } } if (!type.resolved) { - var i, j, rawBaseClassType, baseClassType, name, proto; + var i, j, rawBaseClassType, baseClassType, name, baseProto; var names = []; + function addName(name) { + if (names.indexOf(name) < 0) { + names.push(name); + } + } var qualifiedNames = {}; + function addQualifiedName(name, qualifiedName) { + if (!(name in qualifiedNames)) { + qualifiedNames[name] = []; + } + if (qualifiedNames[name].indexOf(qualifiedName) < 0) { + qualifiedNames[name].push(qualifiedName); + } + } var rawBaseClassTypes = Module.__getBaseClasses(type.rawType); for (i = 0; i < rawBaseClassTypes.size(); i++) { rawBaseClassType = rawBaseClassTypes.at(i); baseClassType = typeRegistry[rawBaseClassType]; if (baseClassType) { resolveType(baseClassType); - proto = baseClassType.Handle.prototype; - for (name in proto) { - if (proto.hasOwnProperty(name)) { - var qualifiedName = baseClassType.name + "_" + name; - // todo: figure out how to exclude casting functions - if (['clone', 'move', 'delete'].indexOf(name) < 0) { - if (type.Handle.prototype.hasOwnProperty(qualifiedName)) { - if (names.indexOf(name) < 0) { - names.push(name); - } - if (names.indexOf(qualifiedName) < 0) { - qualifiedNames[name] = qualifiedName; - } - } else { - if (names.indexOf(name) >= 0) { - if (names.indexOf(qualifiedName) < 0) { - qualifiedNames[name] = qualifiedName; - } - } else { - names.push(name); - if (type.Handle.prototype.hasOwnProperty(name) && names.indexOf(qualifiedName) < 0) { - qualifiedNames[name] = qualifiedName; - } - } - } + baseProto = baseClassType.Handle.prototype; + for (name in baseProto) { + if (baseProto.hasOwnProperty(name) && baseClassType.Handle.memberType[name]) { + if (names.indexOf(name) >= 0 || type.Handle.prototype.hasOwnProperty(name)) { + addQualifiedName(name, baseClassType.name + "_" + name); } + addName(name); } } } @@ -95,16 +119,20 @@ function resolveType(type) { rawBaseClassType = rawBaseClassTypes.at(i); baseClassType = typeRegistry[rawBaseClassType]; if (baseClassType) { - proto = baseClassType.Handle.prototype; + var proto = baseClassType.Handle.prototype; for (name in qualifiedNames) { if (qualifiedNames.hasOwnProperty(name)) { - createInheritedFunctionOrProperty(name, qualifiedNames[name], proto); + for (j = 0; j < qualifiedNames[name].length; j++) { + if (qualifiedNames[name][j].indexOf(baseClassType.name + "_") == 0) { + createInheritedFunctionOrProperty(name, qualifiedNames[name][j], proto, baseClassType); + } + } } } for (j = 0; j < names.length; j++) { name = names[j]; if (!(name in qualifiedNames)) { - createInheritedFunctionOrProperty(name, name, proto); + createInheritedFunctionOrProperty(name, name, proto, baseClassType); } } } @@ -850,6 +878,7 @@ function __embind_register_class( } this.ptr = undefined; }; + Handle.memberType = {}; // todo: clean this up! var registeredClass = new RegisteredPointer(Handle, isPolymorphic, false); @@ -944,8 +973,10 @@ function __embind_register_class_method( runDestructors(destructors); return rv; }; + classType.Handle.memberType[methodName] = "method"; } +// todo: cast methods should require binding of their target types function __embind_register_raw_cast_method( rawClassType, isPolymorphic, @@ -992,6 +1023,7 @@ function __embind_register_raw_cast_method( }; } +// todo: cast methods should not be passed from the smart ptr to the contained object!! function __embind_register_smart_cast_method( rawPointerType, rawReturnType, @@ -1186,6 +1218,7 @@ function __embind_register_class_field( }, enumerable: true }); + classType.Handle.memberType[fieldName] = "field"; } function RegisteredEnum() { From 93b85047472fd07cb1f7530fca078ae6453a0968 Mon Sep 17 00:00:00 2001 From: Bill Welden Date: Fri, 18 Jan 2013 08:06:41 -0800 Subject: [PATCH 090/258] Fix lint warnings. --- src/embind/embind.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 3f50ef219..6f8cba714 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -41,7 +41,7 @@ function resolveType(type) { if (!type.Handle.prototype.hasOwnProperty(name)) { var desc = Object.getOwnPropertyDescriptor(baseClassPrototype, baseClassName); if (desc) { // some names in the list may not be present in this particular base class - if (baseClassPrototype.constructor.memberType[baseClassName] == 'field') { + if (baseClassPrototype.constructor.memberType[baseClassName] === 'field') { var newDescriptor = { enumerable: true, get: function() { @@ -66,7 +66,7 @@ function resolveType(type) { } }; Object.defineProperty(type.Handle.prototype, name, desc); - } else if (baseClassPrototype.constructor.memberType[baseClassName] == 'method') { + } else if (baseClassPrototype.constructor.memberType[baseClassName] === 'method') { type.Handle.prototype[name] = createNamedFunction(name, function() { var save = this.ptr; var baseClassPtr = ___staticPointerCast(this.ptr, type.rawType, baseClassType.rawType); @@ -84,20 +84,20 @@ function resolveType(type) { if (!type.resolved) { var i, j, rawBaseClassType, baseClassType, name, baseProto; var names = []; - function addName(name) { + var addName = function(name) { if (names.indexOf(name) < 0) { names.push(name); } - } + }; var qualifiedNames = {}; - function addQualifiedName(name, qualifiedName) { + var addQualifiedName = function(name, qualifiedName) { if (!(name in qualifiedNames)) { qualifiedNames[name] = []; } if (qualifiedNames[name].indexOf(qualifiedName) < 0) { qualifiedNames[name].push(qualifiedName); } - } + }; var rawBaseClassTypes = Module.__getBaseClasses(type.rawType); for (i = 0; i < rawBaseClassTypes.size(); i++) { rawBaseClassType = rawBaseClassTypes.at(i); @@ -123,7 +123,7 @@ function resolveType(type) { for (name in qualifiedNames) { if (qualifiedNames.hasOwnProperty(name)) { for (j = 0; j < qualifiedNames[name].length; j++) { - if (qualifiedNames[name][j].indexOf(baseClassType.name + "_") == 0) { + if (qualifiedNames[name][j].indexOf(baseClassType.name + "_") === 0) { createInheritedFunctionOrProperty(name, qualifiedNames[name][j], proto, baseClassType); } } From e5a50ea590133df344bde9e1fda0f98afa755378 Mon Sep 17 00:00:00 2001 From: Bill Welden Date: Fri, 18 Jan 2013 13:04:57 -0800 Subject: [PATCH 091/258] Reconstruct code after git merge issue. --- src/embind/embind.js | 126 ++++++++++++++++--------------------------- 1 file changed, 45 insertions(+), 81 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 6f8cba714..07629135a 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -36,103 +36,67 @@ function _embind_repr(v) { var typeRegistry = {}; -function resolveType(type) { - function createInheritedFunctionOrProperty(baseClassName, name, baseClassPrototype, baseClassType) { - if (!type.Handle.prototype.hasOwnProperty(name)) { - var desc = Object.getOwnPropertyDescriptor(baseClassPrototype, baseClassName); - if (desc) { // some names in the list may not be present in this particular base class - if (baseClassPrototype.constructor.memberType[baseClassName] === 'field') { - var newDescriptor = { - enumerable: true, - get: function() { - var save = this.ptr; - var baseClassPtr = ___staticPointerCast(this.ptr, type.rawType, baseClassType.rawType); - try { - this.ptr = baseClassPtr; - return desc.get(); - } finally { - this.ptr = save; // todo: still not good, if the base class routine calls through the current handle - } - }, - set: function(v) { - var save = this.ptr; - var baseClassPtr = ___staticPointerCast(this.ptr, type.rawType, baseClassType.rawType); - try { - this.ptr = baseClassPtr; - desc.set(v); - } finally { - this.ptr = save; // todo: still not good, if the base class routine calls through the current handle - } - } - }; - Object.defineProperty(type.Handle.prototype, name, desc); - } else if (baseClassPrototype.constructor.memberType[baseClassName] === 'method') { - type.Handle.prototype[name] = createNamedFunction(name, function() { - var save = this.ptr; - var baseClassPtr = ___staticPointerCast(this.ptr, type.rawType, baseClassType.rawType); - try { - this.ptr = baseClassPtr; - return baseClassPrototype[baseClassName].apply(this, arguments); - } finally { - this.ptr = save; // todo: still not good, if the base class routine calls through the current handle - } - }); +function createInheritedFunctionOrProperty(name, type, nameInBaseClass, baseClassType) { + function upcastingWrapper(method) { + return function() { + var baseClassPtr = ___staticPointerCast(this.ptr, type.rawType, baseClassType.rawType); + if (baseClassPtr === this.ptr) { + return method.apply(this, arguments); + } else { + var handle = this.clone(); + try { + handle.ptr = baseClassPtr; + return method.apply(handle, arguments); + } finally { + handle.delete(); } } - } + }; } + var baseClassPrototype = baseClassType.Handle.prototype; + if (baseClassPrototype.constructor.memberType[nameInBaseClass] === 'field') { + var baseClassDescriptor = Object.getOwnPropertyDescriptor(baseClassPrototype, nameInBaseClass); + Object.defineProperty(type.Handle.prototype, name, { + enumerable: true, + get: upcastingWrapper(baseClassDescriptor.get), + set: upcastingWrapper(baseClassDescriptor.set) + }); + } else if (baseClassPrototype.constructor.memberType[nameInBaseClass] === 'method') { + var baseClassMethod = baseClassPrototype[nameInBaseClass]; + type.Handle.prototype[name] = createNamedFunction(name, upcastingWrapper(baseClassMethod)); + } +} + +function resolveType(type) { if (!type.resolved) { - var i, j, rawBaseClassType, baseClassType, name, baseProto; - var names = []; - var addName = function(name) { - if (names.indexOf(name) < 0) { - names.push(name); - } - }; - var qualifiedNames = {}; - var addQualifiedName = function(name, qualifiedName) { - if (!(name in qualifiedNames)) { - qualifiedNames[name] = []; - } - if (qualifiedNames[name].indexOf(qualifiedName) < 0) { - qualifiedNames[name].push(qualifiedName); - } - }; + var baseClassType, name, baseProto; + var inheritedNames = {}; var rawBaseClassTypes = Module.__getBaseClasses(type.rawType); - for (i = 0; i < rawBaseClassTypes.size(); i++) { - rawBaseClassType = rawBaseClassTypes.at(i); + for (var i = 0; i < rawBaseClassTypes.size(); i++) { + var rawBaseClassType = rawBaseClassTypes.at(i); baseClassType = typeRegistry[rawBaseClassType]; if (baseClassType) { resolveType(baseClassType); baseProto = baseClassType.Handle.prototype; for (name in baseProto) { if (baseProto.hasOwnProperty(name) && baseClassType.Handle.memberType[name]) { - if (names.indexOf(name) >= 0 || type.Handle.prototype.hasOwnProperty(name)) { - addQualifiedName(name, baseClassType.name + "_" + name); + if (!(name in inheritedNames)) { + inheritedNames[name] = []; } - addName(name); + inheritedNames[name].push(baseClassType); } } } } - for (i = 0; i < rawBaseClassTypes.size(); i++) { - rawBaseClassType = rawBaseClassTypes.at(i); - baseClassType = typeRegistry[rawBaseClassType]; - if (baseClassType) { - var proto = baseClassType.Handle.prototype; - for (name in qualifiedNames) { - if (qualifiedNames.hasOwnProperty(name)) { - for (j = 0; j < qualifiedNames[name].length; j++) { - if (qualifiedNames[name][j].indexOf(baseClassType.name + "_") === 0) { - createInheritedFunctionOrProperty(name, qualifiedNames[name][j], proto, baseClassType); - } - } - } - } - for (j = 0; j < names.length; j++) { - name = names[j]; - if (!(name in qualifiedNames)) { - createInheritedFunctionOrProperty(name, name, proto, baseClassType); + for (name in inheritedNames) { + if (inheritedNames.hasOwnProperty(name)) { + if (!type.Handle.prototype.hasOwnProperty(name) && inheritedNames[name].length === 1) { + baseClassType = inheritedNames[name][0]; + createInheritedFunctionOrProperty(name, type, name, baseClassType); + } else { + for (var j = 0; j < inheritedNames[name].length; j++) { + baseClassType = inheritedNames[name][j]; + createInheritedFunctionOrProperty(baseClassType.name+"_"+name, type, name, baseClassType); } } } From 2b173301eface816c6f09f3e3d706571575a398a Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Mon, 21 Jan 2013 18:54:47 -0800 Subject: [PATCH 092/258] Simplify emscripten::val a bit, reducing it closer to its primitive operations. --- src/embind/emval.js | 14 ++++++++++---- system/include/emscripten/val.h | 19 ++++++++++++++----- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/src/embind/emval.js b/src/embind/emval.js index 1c4955c55..8ecef7f05 100755 --- a/src/embind/emval.js +++ b/src/embind/emval.js @@ -49,7 +49,11 @@ function __emval_new_object() { return __emval_register({}); } -function __emval_new_null() { +function __emval_undefined() { + return __emval_register(undefined); +} + +function __emval_null() { return __emval_register(null); } @@ -63,9 +67,11 @@ function __emval_take_value(type, v) { return __emval_register(v); } -function __emval_has_property(handle, k) { - k = Pointer_stringify(k); - return _emval_handle_array[handle].value.hasOwnProperty(k); +var global = Function('return this')(); + +function __emval_get_global(name) { + name = Pointer_stringify(name); + return __emval_register(global[name]); } function __emval_get_property(handle, k) { diff --git a/system/include/emscripten/val.h b/system/include/emscripten/val.h index 65f522663..b0f55ec6e 100644 --- a/system/include/emscripten/val.h +++ b/system/include/emscripten/val.h @@ -14,11 +14,12 @@ namespace emscripten { EM_VAL _emval_new_array(); EM_VAL _emval_new_object(); - EM_VAL _emval_new_null(); + EM_VAL _emval_undefined(); + EM_VAL _emval_null(); EM_VAL _emval_new_cstring(const char*); void _emval_take_value(TYPEID type/*, ...*/); - bool _emval_has_property(EM_VAL object, const char* key); + EM_VAL _emval_get_global(const char* name); 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); @@ -56,14 +57,22 @@ namespace emscripten { return val(internal::_emval_new_object()); } + static val undefined() { + return val(internal::_emval_undefined()); + } + static val null() { - return val(internal::_emval_new_null()); + return val(internal::_emval_null()); } static val take_ownership(internal::EM_VAL e) { return val(e); } + static val global(const char* name) { + return val(internal::_emval_get_global(name)); + } + template explicit val(const T& value) { typedef internal::BindingType BT; @@ -94,8 +103,8 @@ namespace emscripten { return *this; } - bool exist(const char* key) const { - return internal::_emval_has_property(handle, key); + bool hasOwnProperty(const char* key) const { + return val::global("Object").get("prototype").get("hasOwnProperty").call("call", *this, val(key)).as(); } val get(const char* key) const { From a8a7f1b36580147b192f6b95940c5e9653bddea2 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Mon, 21 Jan 2013 18:54:47 -0800 Subject: [PATCH 093/258] Simplify emscripten::val a bit, reducing it closer to its primitive operations. --- src/embind/emval.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/embind/emval.js b/src/embind/emval.js index 8ecef7f05..1953428d9 100755 --- a/src/embind/emval.js +++ b/src/embind/emval.js @@ -67,7 +67,7 @@ function __emval_take_value(type, v) { return __emval_register(v); } -var global = Function('return this')(); +var global = (function(){return Function;})()('return this')(); function __emval_get_global(name) { name = Pointer_stringify(name); From 03180c51275bc913d21460772ad73b7be815c146 Mon Sep 17 00:00:00 2001 From: Bill Welden Date: Tue, 22 Jan 2013 15:22:11 -0800 Subject: [PATCH 094/258] Removed order dependencies from emscripten bindings. --- src/embind/embind.js | 562 +++++++++++++++++++++++-------------------- 1 file changed, 301 insertions(+), 261 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 07629135a..86e24a916 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -1,4 +1,5 @@ /*global Module*/ +/*global Module*/ /*global _malloc, _free, _memcpy*/ /*global FUNCTION_TABLE, HEAP32*/ /*global Pointer_stringify, writeStringToMemory*/ @@ -35,6 +36,18 @@ function _embind_repr(v) { } var typeRegistry = {}; +var deferredRegistrations = []; + +function requestDeferredRegistration(registrationFunction) { + deferredRegistrations.push(registrationFunction); +} + +function performDeferredRegistrations(){ + while(deferredRegistrations.length > 0) { + var registrationFunction = deferredRegistrations.shift(); + registrationFunction(); + } +} function createInheritedFunctionOrProperty(name, type, nameInBaseClass, baseClassType) { function upcastingWrapper(method) { @@ -106,8 +119,11 @@ function resolveType(type) { } function resolveBindings() { + performDeferredRegistrations(); for (var rawType in typeRegistry) { - resolveType(typeRegistry[rawType]); + if (typeRegistry.hasOwnProperty(rawType)) { + resolveType(typeRegistry[rawType]); + } } } @@ -136,14 +152,21 @@ function typeName(rawType) { return Pointer_stringify(___typeName(rawType)); } -function requireArgumentTypes(argCount, rawArgTypes, name) { - var argTypes = new Array(argCount); - for (var i = 0; i < argCount; ++i) { - var rawArgType = HEAP32[(rawArgTypes >> 2) + i]; +function heap32VectorToArray(count, firstElement) { + var array = []; + for (var i = 0; i < count; i++) { + array.push(HEAP32[(firstElement >> 2) + i]); + } + return array; +} + +function requireArgumentTypes(rawArgTypes, name) { + var argTypes = []; + for (var i = 0; i < rawArgTypes.length; ++i) { if (i === 0) { - argTypes[i] = requireRegisteredType(rawArgType, name + " return value"); + argTypes[i] = requireRegisteredType(rawArgTypes[i], name + " return value"); } else { - argTypes[i] = requireRegisteredType(rawArgType, name + " parameter " + i); + argTypes[i] = requireRegisteredType(rawArgTypes[i], name + " parameter " + i); } } return argTypes; @@ -310,11 +333,14 @@ function makeInvoker(name, argCount, argTypes, invoker, fn) { }; } -function __embind_register_function(name, argCount, rawArgTypes, rawInvoker, fn) { +function __embind_register_function(name, argCount, rawArgTypesAddr, rawInvoker, fn) { + var rawArgTypes = heap32VectorToArray(argCount, rawArgTypesAddr); name = Pointer_stringify(name); rawInvoker = FUNCTION_TABLE[rawInvoker]; - var argTypes = requireArgumentTypes(argCount, rawArgTypes, name); - exposePublicSymbol(name, makeInvoker(name, argCount, argTypes, rawInvoker, fn)); + requestDeferredRegistration(function() { + var argTypes = requireArgumentTypes(rawArgTypes, name); + exposePublicSymbol(name, makeInvoker(name, argCount, argTypes, rawInvoker, fn)); + }); } function RegisteredTuple(rawConstructor, rawDestructor) { @@ -371,21 +397,22 @@ function __embind_register_tuple_element( memberPointerSize, memberPointer ) { - var tupleType = requireRegisteredType(rawTupleType, 'tuple'); - var type = requireRegisteredType(rawType, "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 type.fromWireType(getter(ptr, memberPointer)); - }, - write: function(ptr, o) { - var destructors = []; - setter(ptr, memberPointer, type.toWireType(destructors, o)); - runDestructors(destructors); - } + requestDeferredRegistration(function() { + var tupleType = requireRegisteredType(rawTupleType, 'tuple'); + var type = requireRegisteredType(rawType, "element " + tupleType.name + "[" + tupleType.elements.length + "]"); + tupleType.elements.push({ + read: function(ptr) { + return type.fromWireType(getter(ptr, memberPointer)); + }, + write: function(ptr, o) { + var destructors = []; + setter(ptr, memberPointer, type.toWireType(destructors, o)); + runDestructors(destructors); + } + }); }); } @@ -399,25 +426,26 @@ function __embind_register_tuple_element_accessor( setterSize, setter ) { - var tupleType = requireRegisteredType(rawTupleType, 'tuple'); - var elementType = requireRegisteredType(rawElementType, "element " + tupleType.name + "[" + tupleType.elements.length + "]"); rawStaticGetter = FUNCTION_TABLE[rawStaticGetter]; getter = copyMemberPointer(getter, getterSize); rawStaticSetter = FUNCTION_TABLE[rawStaticSetter]; setter = copyMemberPointer(setter, setterSize); - - tupleType.elements.push({ - read: function(ptr) { - return elementType.fromWireType(rawStaticGetter(ptr, HEAP32[getter >> 2])); - }, - write: function(ptr, o) { - var destructors = []; - rawStaticSetter( - ptr, - HEAP32[setter >> 2], - elementType.toWireType(destructors, o)); - runDestructors(destructors); - } + requestDeferredRegistration(function() { + var tupleType = requireRegisteredType(rawTupleType, 'tuple'); + var elementType = requireRegisteredType(rawElementType, "element " + tupleType.name + "[" + tupleType.elements.length + "]"); + tupleType.elements.push({ + read: function(ptr) { + return elementType.fromWireType(rawStaticGetter(ptr, HEAP32[getter >> 2])); + }, + write: function(ptr, o) { + var destructors = []; + rawStaticSetter( + ptr, + HEAP32[setter >> 2], + elementType.toWireType(destructors, o)); + runDestructors(destructors); + } + }); }); } @@ -475,23 +503,24 @@ function __embind_register_struct_field( memberPointerSize, memberPointer ) { - var structType = requireRegisteredType(rawStructType, 'struct'); fieldName = Pointer_stringify(fieldName); - var fieldType = requireRegisteredType(rawFieldType, 'field "' + structType.name + '.' + fieldName + '"'); rawGetter = FUNCTION_TABLE[rawGetter]; rawSetter = FUNCTION_TABLE[rawSetter]; memberPointer = copyMemberPointer(memberPointer, memberPointerSize); - - structType.fields[fieldName] = { - read: function(ptr) { - return fieldType.fromWireType(rawGetter(ptr, memberPointer)); - }, - write: function(ptr, o) { - var destructors = []; - rawSetter(ptr, memberPointer, fieldType.toWireType(destructors, o)); - runDestructors(destructors); - } - }; + requestDeferredRegistration(function() { + var structType = requireRegisteredType(rawStructType, 'struct'); + var fieldType = requireRegisteredType(rawFieldType, 'field "' + structType.name + '.' + fieldName + '"'); + structType.fields[fieldName] = { + read: function(ptr) { + return fieldType.fromWireType(rawGetter(ptr, memberPointer)); + }, + write: function(ptr, o) { + var destructors = []; + rawSetter(ptr, memberPointer, fieldType.toWireType(destructors, o)); + runDestructors(destructors); + } + }; + }); } function RegisteredPointer(Handle, isPolymorphic, isSmartPointer, rawGetPointee, rawConstructor, rawDestructor) { @@ -870,74 +899,77 @@ function __embind_register_class( function __embind_register_class_constructor( rawClassType, argCount, - rawArgTypes, + rawArgTypesAddr, rawConstructor ) { - var classType = requireRegisteredType(rawClassType, 'class'); - var humanName = 'constructor ' + classType.name; - var argTypes = requireArgumentTypes(argCount, rawArgTypes, humanName); + var rawArgTypes = heap32VectorToArray(argCount, rawArgTypesAddr); rawConstructor = FUNCTION_TABLE[rawConstructor]; + requestDeferredRegistration(function() { + var classType = requireRegisteredType(rawClassType, 'class'); + var humanName = 'constructor ' + classType.name; + var argTypes = requireArgumentTypes(rawArgTypes, humanName); + classType.constructor.body = function() { + if (arguments.length !== argCount - 1) { + throw new BindingError('emscripten binding ' + humanName + ' called with ' + arguments.length + ' arguments, expected ' + (argCount-1)); + } + var destructors = []; + var args = new Array(argCount-1); + for (var i = 1; i < argCount; ++i) { + args[i-1] = argTypes[i].toWireType(destructors, arguments[i-1]); + } - classType.constructor.body = function() { - if (arguments.length !== argCount - 1) { - throw new BindingError('emscripten binding ' + humanName + ' called with ' + arguments.length + ' arguments, expected ' + (argCount-1)); - } - var destructors = []; - var args = new Array(argCount-1); - for (var i = 1; i < argCount; ++i) { - args[i-1] = argTypes[i].toWireType(destructors, arguments[i-1]); - } + var ptr = rawConstructor.apply(null, args); + runDestructors(destructors); - var ptr = rawConstructor.apply(null, args); - runDestructors(destructors); - - return classType.Handle.call(this, ptr); - }; + return classType.Handle.call(this, ptr); + }; + }); } function __embind_register_class_method( rawClassType, methodName, argCount, - rawArgTypes, + rawArgTypesAddr, rawInvoker, memberFunctionSize, memberFunction ) { - var classType = requireRegisteredType(rawClassType, 'class'); + var rawArgTypes = heap32VectorToArray(argCount, rawArgTypesAddr); methodName = Pointer_stringify(methodName); - var humanName = classType.name + '.' + methodName; - - var argTypes = requireArgumentTypes(argCount, rawArgTypes, 'method ' + humanName); rawInvoker = FUNCTION_TABLE[rawInvoker]; memberFunction = copyMemberPointer(memberFunction, memberFunctionSize); + requestDeferredRegistration(function() { + var classType = requireRegisteredType(rawClassType, 'class'); + var humanName = classType.name + '.' + methodName; + var argTypes = requireArgumentTypes(rawArgTypes, 'method ' + humanName); + classType.Handle.prototype[methodName] = function() { - classType.Handle.prototype[methodName] = function() { - - if (!this.ptr) { - throw new BindingError('cannot call emscripten binding method ' + humanName + ' on deleted object'); - } - if (arguments.length !== argCount - 1) { - throw new BindingError('emscripten binding method ' + humanName + ' called with ' + arguments.length + ' arguments, expected ' + (argCount-1)); - } - - var destructors = []; - var args = new Array(argCount + 1); - args[0] = this.ptr; - args[1] = memberFunction; - for (var i = 1; i < argCount; ++i) { - args[i + 1] = argTypes[i].toWireType(destructors, arguments[i-1]); - } - var rv = rawInvoker.apply(null, args); - if (argTypes[0].fromWireTypeAutoDowncast) { - rv = argTypes[0].fromWireTypeAutoDowncast(rv); - } else { - rv = argTypes[0].fromWireType(rv); - } - runDestructors(destructors); - return rv; - }; - classType.Handle.memberType[methodName] = "method"; + if (!this.ptr) { + throw new BindingError('cannot call emscripten binding method ' + humanName + ' on deleted object'); + } + if (arguments.length !== argCount - 1) { + throw new BindingError('emscripten binding method ' + humanName + ' called with ' + arguments.length + ' arguments, expected ' + (argCount-1)); + } + + var destructors = []; + var args = new Array(argCount + 1); + args[0] = this.ptr; + args[1] = memberFunction; + for (var i = 1; i < argCount; ++i) { + args[i + 1] = argTypes[i].toWireType(destructors, arguments[i-1]); + } + var rv = rawInvoker.apply(null, args); + if (argTypes[0].fromWireTypeAutoDowncast) { + rv = argTypes[0].fromWireTypeAutoDowncast(rv); + } else { + rv = argTypes[0].fromWireType(rv); + } + runDestructors(destructors); + return rv; + }; + classType.Handle.memberType[methodName] = "method"; + }); } // todo: cast methods should require binding of their target types @@ -948,43 +980,43 @@ function __embind_register_raw_cast_method( rawReturnType, rawInvoker ) { - var classType = requireRegisteredType(rawClassType, 'class'); - methodName = Pointer_stringify(methodName); - var humanName = classType.name + '.' + methodName; - - var returnType = requireRegisteredType(rawReturnType, 'method ' + humanName + ' return value'); - rawInvoker = FUNCTION_TABLE[rawInvoker]; - - classType.Handle.prototype[methodName] = function() { - if (!this.ptr) { - throw new BindingError('cannot call emscripten binding method ' + humanName + ' on deleted object'); - } - if (arguments.length !== 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(rawReturnType, runtimeType); // downcast is valid - var size = derivation.size(); - derivation.delete(); - if (size === 0) { - derivation = Module.__getDerivationPath(runtimeType, rawReturnType); // upcast is valid - size = derivation.size(); + requestDeferredRegistration(function() { + var classType = requireRegisteredType(rawClassType, 'class'); + methodName = Pointer_stringify(methodName); + var humanName = classType.name + '.' + methodName; + var returnType = requireRegisteredType(rawReturnType, 'method ' + humanName + ' return value'); + rawInvoker = FUNCTION_TABLE[rawInvoker]; + classType.Handle.prototype[methodName] = function() { + if (!this.ptr) { + throw new BindingError('cannot call emscripten binding method ' + humanName + ' on deleted object'); + } + if (arguments.length !== 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(rawReturnType, runtimeType); // downcast is valid + var size = derivation.size(); derivation.delete(); if (size === 0) { - throw new CastError("Pointer conversion is not available"); + derivation = Module.__getDerivationPath(runtimeType, rawReturnType); // upcast is valid + size = derivation.size(); + derivation.delete(); + if (size === 0) { + throw new CastError("Pointer conversion is not available"); + } } } - } - var args = new Array(1); - args[0] = this.ptr; - var ptr = rawInvoker.apply(null, args); - var rv = returnType.fromWireType(ptr); - rv.count = this.count; - this.count.value ++; - return rv; - }; + var args = new Array(1); + args[0] = this.ptr; + var ptr = rawInvoker.apply(null, args); + var rv = returnType.fromWireType(ptr); + rv.count = this.count; + this.count.value ++; + return rv; + }; + }); } // todo: cast methods should not be passed from the smart ptr to the contained object!! @@ -996,91 +1028,96 @@ function __embind_register_smart_cast_method( methodName, rawInvoker ) { - var pointerType = requireRegisteredType(rawPointerType, 'smart pointer class'); - methodName = Pointer_stringify(methodName); - var humanName = pointerType.name + '.' + methodName; - - var returnType = requireRegisteredType(rawReturnType, 'method ' + humanName + ' return value'); - rawInvoker = FUNCTION_TABLE[rawInvoker]; - - pointerType.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 - 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(); + requestDeferredRegistration(function() { + var pointerType = requireRegisteredType(rawPointerType, 'smart pointer class'); + methodName = Pointer_stringify(methodName); + var humanName = pointerType.name + '.' + methodName; + var returnType = requireRegisteredType(rawReturnType, 'method ' + humanName + ' return value'); + rawInvoker = FUNCTION_TABLE[rawInvoker]; + pointerType.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 + var runtimeType = ___getDynamicPointerType(this.ptr); + var derivation = Module.__getDerivationPath(returnPointeeType, runtimeType); // downcast is valid + var size = derivation.size(); derivation.delete(); if (size === 0) { - throw new CastError("Pointer conversion is not available"); + derivation = Module.__getDerivationPath(runtimeType, returnPointeeType); // upcast is valid + size = derivation.size(); + derivation.delete(); + if (size === 0) { + throw new CastError("Pointer conversion is not available"); + } } } - } - var args = new Array(2); - var ptr = _malloc(8); - args[0] = ptr; - args[1] = this.smartPointer; - rawInvoker.apply(null,args); - return returnType.fromWireType(ptr); - }; + var args = new Array(2); + var ptr = _malloc(8); + args[0] = ptr; + args[1] = this.smartPointer; + rawInvoker.apply(null,args); + return returnType.fromWireType(ptr); + }; + }); } function __embind_register_class_classmethod( rawClassType, methodName, argCount, - rawArgTypes, + rawArgTypesAddr, rawInvoker, fn ) { - var classType = requireRegisteredType(rawClassType, 'class'); + var rawArgTypes = heap32VectorToArray(argCount, rawArgTypesAddr); methodName = Pointer_stringify(methodName); - var humanName = classType.name + '.' + methodName; - var argTypes = requireArgumentTypes(argCount, rawArgTypes, 'classmethod ' + humanName); rawInvoker = FUNCTION_TABLE[rawInvoker]; - classType.constructor[methodName] = makeInvoker(humanName, argCount, argTypes, rawInvoker, fn); + requestDeferredRegistration(function() { + var classType = requireRegisteredType(rawClassType, 'class'); + var humanName = classType.name + '.' + methodName; + var argTypes = requireArgumentTypes(rawArgTypes, 'classmethod ' + humanName); + classType.constructor[methodName] = makeInvoker(humanName, argCount, argTypes, rawInvoker, fn); + }) } function __embind_register_class_operator_call( rawClassType, argCount, - rawArgTypes, + rawArgTypesAddr, rawInvoker ) { - var classType = requireRegisteredType(rawClassType, 'class'); - var humanName = classType.name + '.' + 'operator_call'; - var argTypes = requireArgumentTypes(argCount, rawArgTypes, 'method ' + humanName); + var rawArgTypes = heap32VectorToArray(argCount, rawArgTypesAddr); rawInvoker = FUNCTION_TABLE[rawInvoker]; - - - classType.Handle.prototype.operator_call = function() { - if (!this.ptr) { - throw new BindingError('cannot call emscripten binding method ' + humanName + ' on deleted object'); - } - if (arguments.length !== argCount - 1) { - throw new BindingError('emscripten binding method ' + humanName + ' called with ' + arguments.length + ' arguments, expected ' + (argCount-1)); - } - - var destructors = []; - var args = new Array(argCount); - args[0] = this.ptr; - for (var i = 1; i < argCount; ++i) { - args[i] = argTypes[i].toWireType(destructors, arguments[i-1]); - } + requestDeferredRegistration(function() { + var classType = requireRegisteredType(rawClassType, 'class'); + var humanName = classType.name + '.' + 'operator_call'; + var argTypes = requireArgumentTypes(rawArgTypes, 'method ' + humanName); - var rv = argTypes[0].fromWireType(rawInvoker.apply(null, args)); - runDestructors(destructors); - return rv; - }; + classType.Handle.prototype.operator_call = function() { + if (!this.ptr) { + throw new BindingError('cannot call emscripten binding method ' + humanName + ' on deleted object'); + } + if (arguments.length !== argCount - 1) { + throw new BindingError('emscripten binding method ' + humanName + ' called with ' + arguments.length + ' arguments, expected ' + (argCount-1)); + } + + var destructors = []; + var args = new Array(argCount); + args[0] = this.ptr; + for (var i = 1; i < argCount; ++i) { + args[i] = argTypes[i].toWireType(destructors, arguments[i-1]); + } + + var rv = argTypes[0].fromWireType(rawInvoker.apply(null, args)); + runDestructors(destructors); + return rv; + }; + }); } function __embind_register_class_operator_array_get( @@ -1089,30 +1126,31 @@ function __embind_register_class_operator_array_get( indexType, rawInvoker ) { - var classType = requireRegisteredType(rawClassType, 'class'); - indexType = requireRegisteredType(indexType, 'array access index ' + classType.name); - elementType = requireRegisteredType(elementType, 'array access element' + classType.name); rawInvoker = FUNCTION_TABLE[rawInvoker]; - var humanName = classType.name + '.' + 'operator_array_get'; - - classType.Handle.prototype.array_get = function() { - if (!this.ptr) { - throw new BindingError('cannot call emscripten binding method ' + humanName + ' on deleted object'); - } - - if (arguments.length !== 1) { - throw new BindingError('emscripten binding method ' + humanName + ' called with ' + arguments.length + ' arguments, expected ' + 1); - } - - var destructors = []; - var args = new Array(2); - args[0] = this.ptr; - args[1] = indexType.toWireType(destructors, arguments[0]); - - var rv = elementType.fromWireType(rawInvoker.apply(null, args)); - runDestructors(destructors); - return rv; - }; + requestDeferredRegistration(function() { + var classType = requireRegisteredType(rawClassType, 'class'); + indexType = requireRegisteredType(indexType, 'array access index ' + classType.name); + elementType = requireRegisteredType(elementType, 'array access element' + classType.name); + var humanName = classType.name + '.' + 'operator_array_get'; + classType.Handle.prototype.array_get = function() { + if (!this.ptr) { + throw new BindingError('cannot call emscripten binding method ' + humanName + ' on deleted object'); + } + + if (arguments.length !== 1) { + throw new BindingError('emscripten binding method ' + humanName + ' called with ' + arguments.length + ' arguments, expected ' + 1); + } + + var destructors = []; + var args = new Array(2); + args[0] = this.ptr; + args[1] = indexType.toWireType(destructors, arguments[0]); + + var rv = elementType.fromWireType(rawInvoker.apply(null, args)); + runDestructors(destructors); + return rv; + }; + }); } function __embind_register_class_operator_array_set( @@ -1121,31 +1159,32 @@ function __embind_register_class_operator_array_set( rawIndexType, rawInvoker ) { - var classType = requireRegisteredType(rawClassType, 'class'); - var indexType = requireRegisteredType(rawIndexType, 'array access index ' + classType.name); - elementType = requireRegisteredType(elementType, 'array access element ' + classType.name); rawInvoker = FUNCTION_TABLE[rawInvoker]; - var humanName = classType.name + '.' + 'operator_array_get'; - - classType.Handle.prototype.array_set = function() { - if (!this.ptr) { - throw new BindingError('cannot call emscripten binding method ' + humanName + ' on deleted object'); - } - - if (arguments.length !== 2) { - throw new BindingError('emscripten binding method ' + humanName + ' called with ' + arguments.length + ' arguments, expected ' + 2); - } - - var destructors = []; - var args = new Array(2); - args[0] = this.ptr; - args[1] = indexType.toWireType(destructors, arguments[0]); - args[2] = elementType.toWireType(destructors, arguments[1]); - - var rv = elementType.fromWireType(rawInvoker.apply(null, args)); - runDestructors(destructors); - return rv; - }; + requestDeferredRegistration(function() { + var classType = requireRegisteredType(rawClassType, 'class'); + var indexType = requireRegisteredType(rawIndexType, 'array access index ' + classType.name); + elementType = requireRegisteredType(elementType, 'array access element ' + classType.name); + var humanName = classType.name + '.' + 'operator_array_get'; + classType.Handle.prototype.array_set = function() { + if (!this.ptr) { + throw new BindingError('cannot call emscripten binding method ' + humanName + ' on deleted object'); + } + + if (arguments.length !== 2) { + throw new BindingError('emscripten binding method ' + humanName + ' called with ' + arguments.length + ' arguments, expected ' + 2); + } + + var destructors = []; + var args = new Array(2); + args[0] = this.ptr; + args[1] = indexType.toWireType(destructors, arguments[0]); + args[2] = elementType.toWireType(destructors, arguments[1]); + + var rv = elementType.fromWireType(rawInvoker.apply(null, args)); + runDestructors(destructors); + return rv; + }; + }); } function __embind_register_class_field( @@ -1157,32 +1196,33 @@ function __embind_register_class_field( memberPointerSize, memberPointer ) { - var classType = requireRegisteredType(rawClassType, 'class'); fieldName = Pointer_stringify(fieldName); - var humanName = classType.name + '.' + fieldName; - var fieldType = requireRegisteredType(rawFieldType, '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 + requestDeferredRegistration(function() { + var classType = requireRegisteredType(rawClassType, 'class'); + var humanName = classType.name + '.' + fieldName; + var fieldType = requireRegisteredType(rawFieldType, 'field ' + humanName); + 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 + }); + classType.Handle.memberType[fieldName] = "field"; }); - classType.Handle.memberType[fieldName] = "field"; } function RegisteredEnum() { From 5701999237b30f3503b6ad2f20099e874561bdf9 Mon Sep 17 00:00:00 2001 From: Bill Welden Date: Tue, 22 Jan 2013 15:26:03 -0800 Subject: [PATCH 095/258] Fix a lint error. --- src/embind/embind.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 86e24a916..ae61f09f2 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -1082,7 +1082,7 @@ function __embind_register_class_classmethod( var humanName = classType.name + '.' + methodName; var argTypes = requireArgumentTypes(rawArgTypes, 'classmethod ' + humanName); classType.constructor[methodName] = makeInvoker(humanName, argCount, argTypes, rawInvoker, fn); - }) + }); } function __embind_register_class_operator_call( From 661e70cb38bbdd704821c50c4324b5675b41068c Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Tue, 22 Jan 2013 08:46:35 -0800 Subject: [PATCH 096/258] simplify emscripten::val set and get --- src/embind/emval.js | 22 ++++-------------- system/include/emscripten/val.h | 40 ++++++++------------------------- 2 files changed, 13 insertions(+), 49 deletions(-) diff --git a/src/embind/emval.js b/src/embind/emval.js index 1953428d9..84ed19568 100755 --- a/src/embind/emval.js +++ b/src/embind/emval.js @@ -74,17 +74,8 @@ function __emval_get_global(name) { return __emval_register(global[name]); } -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_get_property(handle, key) { + return __emval_register(_emval_handle_array[handle].value[_emval_handle_array[key].value]); } function __emval_eval_global_method(handle, objectName, methodName) { @@ -94,13 +85,8 @@ function __emval_eval_global_method(handle, objectName, methodName) { return __emval_register(result); } -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_set_property(handle, key, value) { + _emval_handle_array[handle].value[_emval_handle_array[key].value] = _emval_handle_array[value].value; } function __emval_as(handle, returnType) { diff --git a/system/include/emscripten/val.h b/system/include/emscripten/val.h index b0f55ec6e..4903751be 100644 --- a/system/include/emscripten/val.h +++ b/system/include/emscripten/val.h @@ -20,12 +20,9 @@ namespace emscripten { void _emval_take_value(TYPEID type/*, ...*/); EM_VAL _emval_get_global(const char* name); - 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); + EM_VAL _emval_get_property(EM_VAL object, EM_VAL key); EM_VAL _emval_eval_global_method(EM_VAL object, const char* objectName, const char* methodName); - 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_set_property(EM_VAL object, EM_VAL key, EM_VAL value); void _emval_as(EM_VAL value, TYPEID returnType); EM_VAL _emval_call( EM_VAL value, @@ -106,38 +103,19 @@ namespace emscripten { bool hasOwnProperty(const char* key) const { return val::global("Object").get("prototype").get("hasOwnProperty").call("call", *this, val(key)).as(); } - - 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)); + + template + val get(const T& key) const { + return val(internal::_emval_get_property(handle, val(key).handle)); } val eval_global_method(const char* objectName, const char* methodName) const { return val(internal::_emval_eval_global_method(handle, objectName, methodName)); } - 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 + void set(const T& key, val v) { + internal::_emval_set_property(handle, val(key).handle, v.handle); } template From 066540930e679a965fa002cb09b3e6cc3bcc2c97 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Tue, 22 Jan 2013 15:58:17 -0800 Subject: [PATCH 097/258] Switch from val::get to val[] --- system/include/emscripten/bind.h | 4 ++-- system/include/emscripten/val.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index 652fb1b46..ce9053f26 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -793,11 +793,11 @@ namespace emscripten { //////////////////////////////////////////////////////////////////////////////// template std::vector vecFromJSArray(val v) { - auto l = v.get("length").as(); + auto l = v["length"].as(); std::vector rv; for(unsigned i = 0; i < l; ++i) { - rv.push_back(v.get(i).as()); + rv.push_back(v[i].as()); } return rv; diff --git a/system/include/emscripten/val.h b/system/include/emscripten/val.h index 4903751be..87126d084 100644 --- a/system/include/emscripten/val.h +++ b/system/include/emscripten/val.h @@ -105,7 +105,7 @@ namespace emscripten { } template - val get(const T& key) const { + val operator[](const T& key) const { return val(internal::_emval_get_property(handle, val(key).handle)); } From 09bfd969bd413be3dbcdd5dbf89c17211efed85b Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Tue, 22 Jan 2013 16:15:18 -0800 Subject: [PATCH 098/258] after merge we need to fix hasOwnProperty to use [] --- system/include/emscripten/val.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/include/emscripten/val.h b/system/include/emscripten/val.h index 87126d084..d6b1ac04d 100644 --- a/system/include/emscripten/val.h +++ b/system/include/emscripten/val.h @@ -101,7 +101,7 @@ namespace emscripten { } bool hasOwnProperty(const char* key) const { - return val::global("Object").get("prototype").get("hasOwnProperty").call("call", *this, val(key)).as(); + return val::global("Object")["prototype"]["hasOwnProperty"].call("call", *this, val(key)).as(); } template From bacb23ed1a80740b34e3a265a29be1d86dd72ff0 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Tue, 22 Jan 2013 16:39:06 -0800 Subject: [PATCH 099/258] Kill eval_global_method: it's replaced with more primitives --- src/embind/emval.js | 7 ------- system/include/emscripten/val.h | 5 ----- 2 files changed, 12 deletions(-) diff --git a/src/embind/emval.js b/src/embind/emval.js index 84ed19568..56c307bf5 100755 --- a/src/embind/emval.js +++ b/src/embind/emval.js @@ -78,13 +78,6 @@ function __emval_get_property(handle, key) { return __emval_register(_emval_handle_array[handle].value[_emval_handle_array[key].value]); } -function __emval_eval_global_method(handle, objectName, methodName) { - var objectNameStr = Pointer_stringify(objectName); - var methodNameStr = Pointer_stringify(methodName); - var result = eval.call(null, objectNameStr)[methodNameStr](_emval_handle_array[handle].value); - return __emval_register(result); -} - function __emval_set_property(handle, key, value) { _emval_handle_array[handle].value[_emval_handle_array[key].value] = _emval_handle_array[value].value; } diff --git a/system/include/emscripten/val.h b/system/include/emscripten/val.h index d6b1ac04d..3d0a3e60b 100644 --- a/system/include/emscripten/val.h +++ b/system/include/emscripten/val.h @@ -21,7 +21,6 @@ namespace emscripten { EM_VAL _emval_get_global(const char* name); EM_VAL _emval_get_property(EM_VAL object, EM_VAL key); - EM_VAL _emval_eval_global_method(EM_VAL object, const char* objectName, const char* methodName); void _emval_set_property(EM_VAL object, EM_VAL key, EM_VAL value); void _emval_as(EM_VAL value, TYPEID returnType); EM_VAL _emval_call( @@ -109,10 +108,6 @@ namespace emscripten { return val(internal::_emval_get_property(handle, val(key).handle)); } - val eval_global_method(const char* objectName, const char* methodName) const { - return val(internal::_emval_eval_global_method(handle, objectName, methodName)); - } - template void set(const T& key, val v) { internal::_emval_set_property(handle, val(key).handle, v.handle); From 50e05c30bfcdf3b1c8aea0be0b631233e24f694c Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Tue, 22 Jan 2013 18:02:56 -0800 Subject: [PATCH 100/258] Introduce val::new_ for constructor invocation --- src/embind/emval.js | 4 ++++ system/include/emscripten/val.h | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/src/embind/emval.js b/src/embind/emval.js index 56c307bf5..39f23a615 100755 --- a/src/embind/emval.js +++ b/src/embind/emval.js @@ -67,6 +67,10 @@ function __emval_take_value(type, v) { return __emval_register(v); } +function __emval_new(handle) { + return __emval_register(new (_emval_handle_array[handle].value)) +} + var global = (function(){return Function;})()('return this')(); function __emval_get_global(name) { diff --git a/system/include/emscripten/val.h b/system/include/emscripten/val.h index 3d0a3e60b..22a7776a6 100644 --- a/system/include/emscripten/val.h +++ b/system/include/emscripten/val.h @@ -19,6 +19,8 @@ namespace emscripten { EM_VAL _emval_new_cstring(const char*); void _emval_take_value(TYPEID type/*, ...*/); + EM_VAL _emval_new(EM_VAL value); + EM_VAL _emval_get_global(const char* name); EM_VAL _emval_get_property(EM_VAL object, EM_VAL key); void _emval_set_property(EM_VAL object, EM_VAL key, EM_VAL value); @@ -102,6 +104,10 @@ namespace emscripten { bool hasOwnProperty(const char* key) const { return val::global("Object")["prototype"]["hasOwnProperty"].call("call", *this, val(key)).as(); } + + val new_() const { + return val(internal::_emval_new(handle)); + } template val operator[](const T& key) const { From bf8a0f22d50d3ee8e19d9ec72a5ff563d42c570a Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Tue, 22 Jan 2013 18:28:22 -0800 Subject: [PATCH 101/258] Depending on the order of construction and destruction of val objects, count_emval_handles could be incorrect. --- src/embind/emval.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/embind/emval.js b/src/embind/emval.js index 39f23a615..e6ba566aa 100755 --- a/src/embind/emval.js +++ b/src/embind/emval.js @@ -10,7 +10,13 @@ var _emval_free_list = []; /** @expose */ Module.count_emval_handles = function() { - return _emval_handle_array.length; + var count = 0; + for (var i = 0; i < _emval_handle_array.length; ++i) { + if (_emval_handle_array[i] !== undefined) { + ++count; + } + } + return count; }; // Private C++ API From 15fa455b01fc5c498994c08882e80cfaeffc1983 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Tue, 22 Jan 2013 18:29:38 -0800 Subject: [PATCH 102/258] Missing semicolon --- src/embind/emval.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/embind/emval.js b/src/embind/emval.js index e6ba566aa..bf31bdf3e 100755 --- a/src/embind/emval.js +++ b/src/embind/emval.js @@ -74,7 +74,7 @@ function __emval_take_value(type, v) { } function __emval_new(handle) { - return __emval_register(new (_emval_handle_array[handle].value)) + return __emval_register(new (_emval_handle_array[handle].value)); } var global = (function(){return Function;})()('return this')(); From ba7a7200ec2737c9890408e906ebe6bc2b9ca69c Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Tue, 22 Jan 2013 19:14:02 -0800 Subject: [PATCH 103/258] Document operators that are not exposed --- system/include/emscripten/val.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/system/include/emscripten/val.h b/system/include/emscripten/val.h index 22a7776a6..857a1bdf4 100644 --- a/system/include/emscripten/val.h +++ b/system/include/emscripten/val.h @@ -47,6 +47,22 @@ namespace emscripten { class val { public: + // missing operators: + // * delete + // * in + // * instanceof + // * typeof + // * ! ~ - + ++ -- + // * * / % + // * + - + // * << >> >>> + // * < <= > >= + // * == != === !== + // * & ^ | && || ?: + // + // exposing void, comma, and conditional is unnecessary + // same with: = += -= *= /= %= <<= >>= >>>= &= ^= |= + static val array() { return val(internal::_emval_new_array()); } From 8a12d4c1c4aa9a57fdc414b3304555de8c7158ac Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Wed, 23 Jan 2013 11:54:00 -0800 Subject: [PATCH 104/258] fix a rebase conflict --- src/embind/embind.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index ae61f09f2..d8a4a4402 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -721,9 +721,8 @@ RegisteredRawPointer.prototype.fromWireTypeAutoDowncast = function(ptr) { // todo: can come back -1 or -2!! Throw appropriate exception return handle; } else { - handle = new this.Handle(ptr); + return new this.Handle(ptr); } - return handle; }; function RegisteredClassInstance(constructor, Handle) { From e5fca2885bf4d8066b9569548b4ced1b853f0414 Mon Sep 17 00:00:00 2001 From: Bill Welden Date: Mon, 28 Jan 2013 09:12:08 -0800 Subject: [PATCH 105/258] register_smart_ptr changed to .shared_ptr (mod to class definition) -- shared_ptr (outside of class definition) is still allowed shared pointers passed by reference can be modified by the called code -- the change is now reflected at the Javascript calling level other minor clean-up and small syntactic changes --- src/embind/embind.js | 155 ++++++++++++++----------------- system/include/emscripten/bind.h | 62 +++++++------ system/lib/embind/bind.cpp | 20 +--- 3 files changed, 104 insertions(+), 133 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index d8a4a4402..7210dbc14 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -5,7 +5,6 @@ /*global Pointer_stringify, writeStringToMemory*/ /*global __emval_register, _emval_handle_array, __emval_decref*/ /*global ___getDynamicPointerType: false*/ -/*global ___dynamicPointerCast: false*/ /*global ___typeName:false*/ /*global ___staticPointerCast: false*/ @@ -308,6 +307,12 @@ function runDestructors(destructors) { } } +function refreshSmartPointee(handle) { + if (handle && handle.smartPointer) { + handle.ptr = handle.type.smartPointerType.getPointee(handle.smartPointer); + } +} + function makeInvoker(name, argCount, argTypes, invoker, fn) { if (!FUNCTION_TABLE[fn]) { throw new BindingError('function '+name+' is not defined'); @@ -329,6 +334,9 @@ function makeInvoker(name, argCount, argTypes, invoker, fn) { rv = argTypes[0].fromWireType(rv); } runDestructors(destructors); + for (i = 1; i < argCount; i++) { + refreshSmartPointee(arguments[i-1]); + } return rv; }; } @@ -537,21 +545,21 @@ RegisteredPointer.prototype.toWireType = function(destructors, handle) { if (!handle) { return null; } - if (handle.pointeeType.isPolymorphic) { - fromRawType = handle.pointeeType.getDynamicRawPointerType(handle.ptr); + if (handle.type.isPolymorphic) { + fromRawType = handle.type.getDynamicRawPointerType(handle.ptr); } else { - fromRawType = handle.pointeeType.rawType; + fromRawType = handle.type.rawType; } - if (fromRawType === this.pointeeType.rawType) { + if (fromRawType === this.type.rawType) { return this.isSmartPointer ? handle.smartPointer : handle.ptr; } - var ptr = staticPointerCast(handle.ptr, fromRawType, this.pointeeType.rawType); + var ptr = staticPointerCast(handle.ptr, fromRawType, this.type.rawType); if (this.isSmartPointer) { // todo: if ptr == handle.ptr, there's no need to allocate a new smartPtr! - var smartPtr = _malloc(16); - handle.pointeeType.smartPointerType.rawConstructor(smartPtr, ptr, handle.smartPointer); + var smartPtr = _malloc(16); // todo: can we get C++ to tell us the size of the pointer? + handle.type.smartPointerType.rawConstructor(smartPtr, ptr, handle.smartPointer); ptr = smartPtr; - destructors.push(handle.pointeeType.smartPointerType.rawDestructor); + destructors.push(handle.type.smartPointerType.rawDestructor); destructors.push(ptr); } return ptr; @@ -582,7 +590,7 @@ RegisteredPointer.prototype.fromWireType = function(ptr) { RegisteredPointer.prototype.getDynamicRawPointerType = function(ptr) { var type = null; if (this.isPolymorphic) { - if (this.rawGetPointee) { + if (this.rawGetPointee) { // todo: did you mean isSmartPtr? type = ___getDynamicPointerType(this.rawGetPointee(ptr)); } else { type = ___getDynamicPointerType(ptr); @@ -594,8 +602,8 @@ RegisteredPointer.prototype.getDynamicRawPointerType = function(ptr) { RegisteredPointer.prototype.getDynamicDowncastType = function(ptr) { var downcastType = null; var type = this.getDynamicRawPointerType(ptr); - if (type && type !== this.pointeeType.rawType) { - var derivation = Module.__getDerivationPath(type, this.pointeeType.rawType); + if (type && type !== this.type.rawType) { + var derivation = Module.__getDerivationPath(type, this.type.rawType); for (var i = 0; i < derivation.size(); i++) { downcastType = typeRegistry[derivation.at(i)]; if (downcastType) { @@ -615,7 +623,7 @@ RegisteredPointer.prototype.fromWireTypeAutoDowncast = function(ptr) { // ptr is } var toType = this.getDynamicDowncastType(ptr); if (toType) { - var fromType = this.pointeeType; + var fromType = this.type; if (this.isSmartPointer) { handle = toType.smartPointerType.fromWireType(ptr); } else { @@ -641,12 +649,16 @@ function __embind_register_smart_ptr( rawConstructor = FUNCTION_TABLE[rawConstructor]; rawDestructor = FUNCTION_TABLE[rawDestructor]; rawGetPointee = FUNCTION_TABLE[rawGetPointee]; - + + if (name == "") { + name = pointeeType.name + "Ptr"; + } + var Handle = createNamedFunction(name, function(ptr) { this.count = {value: 1}; this.smartPointer = ptr; // std::shared_ptr* this.ptr = rawGetPointee(ptr); // T* - this.pointeeType = pointeeType; + this.type = pointeeType; }); // TODO: test for SmartPtr.prototype.constructor property? @@ -680,7 +692,7 @@ function __embind_register_smart_ptr( this.ptr = undefined; }; var registeredPointer = new RegisteredPointer(Handle, pointeeType.isPolymorphic, true, rawGetPointee, rawConstructor, rawDestructor); - registeredPointer.pointeeType = pointeeType; + registeredPointer.type = pointeeType; pointeeType.smartPointerType = registerType(rawType, name, registeredPointer); } @@ -828,11 +840,13 @@ function __embind_register_class( h.count = {value: 1, ptr: ptr }; h.ptr = ptr; - h.pointeeType = type; // set below + h.type = type; // set below for(var prop in Handle.prototype) { - var dp = Object.getOwnPropertyDescriptor(Handle.prototype, prop); - Object.defineProperty(h, prop, dp); + if (Handle.prototype.hasOwnProperty(prop)) { + var dp = Object.getOwnPropertyDescriptor(Handle.prototype, prop); + Object.defineProperty(h, prop, dp); + } } return h; @@ -875,15 +889,15 @@ function __embind_register_class( // todo: clean this up! var registeredClass = new RegisteredPointer(Handle, isPolymorphic, false); var type = registerType(rawType, name, registeredClass); - registeredClass.pointeeType = type; + registeredClass.type = type; - var registeredClass = new RegisteredPointer(Handle, isPolymorphic, false); + registeredClass = new RegisteredPointer(Handle, isPolymorphic, false); registerType(rawPointerType, name + '*', registeredClass); - registeredClass.pointeeType = type; + registeredClass.type = type; // todo: implement const pointers (no modification Javascript side) - var registeredClass = new RegisteredPointer(Handle, isPolymorphic, false); + registeredClass = new RegisteredPointer(Handle, isPolymorphic, false); registerType(rawConstPointerType, name + ' const*', registeredClass); - registeredClass.pointeeType = type; + registeredClass.type = type; type.constructor = createNamedFunction(type.name, function() { var body = type.constructor.body; @@ -965,26 +979,32 @@ function __embind_register_class_method( rv = argTypes[0].fromWireType(rv); } runDestructors(destructors); + for (i = 1; i < argCount; i++) { + refreshSmartPointee(arguments[i-1]); + } return rv; }; classType.Handle.memberType[methodName] = "method"; }); } -// todo: cast methods should require binding of their target types -function __embind_register_raw_cast_method( +function __embind_register_cast_method( rawClassType, isPolymorphic, methodName, - rawReturnType, - rawInvoker + rawRawReturnType, + rawSharedReturnType, + rawRawCaster, + rawSharedCaster ) { requestDeferredRegistration(function() { var classType = requireRegisteredType(rawClassType, 'class'); methodName = Pointer_stringify(methodName); var humanName = classType.name + '.' + methodName; - var returnType = requireRegisteredType(rawReturnType, 'method ' + humanName + ' return value'); - rawInvoker = FUNCTION_TABLE[rawInvoker]; + var rawReturnType = requireRegisteredType(rawRawReturnType, 'method ' + humanName + ' return value'); + var sharedReturnType = requireRegisteredType(rawSharedReturnType, 'method ' + humanName + ' shared pointer return value'); + var rawCaster = FUNCTION_TABLE[rawRawCaster]; + var sharedCaster = FUNCTION_TABLE[rawSharedCaster]; classType.Handle.prototype[methodName] = function() { if (!this.ptr) { throw new BindingError('cannot call emscripten binding method ' + humanName + ' on deleted object'); @@ -995,11 +1015,11 @@ function __embind_register_raw_cast_method( if (isPolymorphic) { // todo: this is all only to validate the cast -- cache the result var runtimeType = ___getDynamicPointerType(this.ptr); - var derivation = Module.__getDerivationPath(rawReturnType, runtimeType); // downcast is valid + var derivation = Module.__getDerivationPath(rawRawReturnType, runtimeType); // downcast is valid var size = derivation.size(); derivation.delete(); if (size === 0) { - derivation = Module.__getDerivationPath(runtimeType, rawReturnType); // upcast is valid + derivation = Module.__getDerivationPath(runtimeType, rawRawReturnType); // upcast is valid size = derivation.size(); derivation.delete(); if (size === 0) { @@ -1007,64 +1027,29 @@ function __embind_register_raw_cast_method( } } } - var args = new Array(1); - args[0] = this.ptr; - var ptr = rawInvoker.apply(null, args); - var rv = returnType.fromWireType(ptr); - rv.count = this.count; - this.count.value ++; + var args; + var ptr; + var rv; + if (this.smartPointer) { + args = new Array(2); + ptr = _malloc(8); + args[0] = ptr; + args[1] = this.smartPointer; + sharedCaster.apply(null,args); // need a smart pointer raw invoker + rv = sharedReturnType.fromWireType(ptr); + } else { + args = new Array(1); + args[0] = this.ptr; + ptr = rawCaster.apply(null, args); + rv = rawReturnType.fromWireType(ptr); + rv.count = this.count; + this.count.value ++; + } return rv; }; }); } -// todo: cast methods should not be passed from the smart ptr to the contained object!! -function __embind_register_smart_cast_method( - rawPointerType, - rawReturnType, - returnPointeeType, - isPolymorphic, - methodName, - rawInvoker -) { - requestDeferredRegistration(function() { - var pointerType = requireRegisteredType(rawPointerType, 'smart pointer class'); - methodName = Pointer_stringify(methodName); - var humanName = pointerType.name + '.' + methodName; - var returnType = requireRegisteredType(rawReturnType, 'method ' + humanName + ' return value'); - rawInvoker = FUNCTION_TABLE[rawInvoker]; - pointerType.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 - 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) { - throw new CastError("Pointer conversion is not available"); - } - } - } - var args = new Array(2); - var ptr = _malloc(8); - args[0] = ptr; - args[1] = this.smartPointer; - rawInvoker.apply(null,args); - return returnType.fromWireType(ptr); - }; - }); -} - function __embind_register_class_classmethod( rawClassType, methodName, diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index ce9053f26..a3789cabc 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -124,20 +124,14 @@ namespace emscripten { size_t memberFunctionSize, void* memberFunction); - void _embind_register_raw_cast_method( + void _embind_register_cast_method( TYPEID classType, bool isPolymorphic, const char* methodName, TYPEID returnType, - GenericFunction invoker); - - void _embind_register_smart_cast_method( - TYPEID pointerType, - TYPEID returnType, - TYPEID returnPointeeType, - bool isPolymorphic, - const char* methodName, - GenericFunction invoker); + TYPEID sharedReturnType, + GenericFunction rawInvoker, + GenericFunction sharedInvoker); void _embind_register_class_field( TYPEID classType, @@ -274,7 +268,6 @@ namespace emscripten { extern "C" { int __getDynamicPointerType(int p); - int __dynamicPointerCast(int p, int to); } template @@ -283,14 +276,14 @@ namespace emscripten { }; template - struct performShared { + struct performSharedCast { static std::shared_ptr cast(std::shared_ptr from) { return std::dynamic_pointer_cast(from); }; }; template - struct performShared { + struct performSharedCast { static std::shared_ptr cast(std::shared_ptr from) { return std::shared_ptr(from, static_cast(from.get())); }; @@ -300,7 +293,7 @@ namespace emscripten { void function(const char* name, ReturnType (fn)(Args...), Policies...) { using namespace internal; - registerStandardTypes(); + registerStandardTypes(); // todo: remove all typename WithPolicies::template ArgTypeList args; _embind_register_function( @@ -625,21 +618,12 @@ namespace emscripten { reinterpret_cast(&get_pointee)); } + }; - template - register_smart_ptr& cast(const char* methodName) { - using namespace internal; - typedef typename ReturnType::element_type ReturnPointeeType; - typedef typename PointerType::element_type PointeeType; - _embind_register_smart_cast_method( - TypeID::get(), - TypeID::get(), - TypeID::get(), - std::is_polymorphic::value, - methodName, - reinterpret_cast(&performShared::value>::cast)); - return *this; - } + template + class shared_ptr: public register_smart_ptr> { + public: + shared_ptr(const char* name): register_smart_ptr>(name) {}; }; //////////////////////////////////////////////////////////////////////////////// @@ -777,13 +761,31 @@ namespace emscripten { template class_& cast(const char* methodName) { using namespace internal; + //typedef typename std::shared_ptr PointerType; + typedef typename std::shared_ptr SharedReturnType; - _embind_register_raw_cast_method( + _embind_register_cast_method( TypeID::get(), std::is_polymorphic::value, methodName, TypeID::get(), - reinterpret_cast(&performRawStaticCast)); + TypeID::get(), + reinterpret_cast(&performRawStaticCast), + reinterpret_cast(&performSharedCast::value>::cast)); + return *this; + } + + class_& shared_ptr(const char* ptrName = "") { + using namespace internal; + typedef typename std::shared_ptr PointerType; + + _embind_register_smart_ptr( + TypeID::get(), + TypeID::get(), + ptrName, + reinterpret_cast(&raw_smart_pointer_constructor), + reinterpret_cast(&raw_destructor), + reinterpret_cast(&get_pointee)); return *this; } }; diff --git a/system/lib/embind/bind.cpp b/system/lib/embind/bind.cpp index 5e82bb7eb..bc79876fe 100755 --- a/system/lib/embind/bind.cpp +++ b/system/lib/embind/bind.cpp @@ -101,7 +101,7 @@ namespace emscripten { // __getDerivationPath returns an array of type_info pointers describing the derivation chain starting with // the derived type and proceeding toward (and ending with) the base type. Types are only included if they // appear on all possible derivation paths. - std::vector __getDerivationPath(int dv, const int bs) { + std::vector __getDerivationPath(int dv, const int bs) { // todo: use emval array to pass return value?? std::vector> paths; const std::type_info* dv1 = (std::type_info*)dv; @@ -176,27 +176,11 @@ namespace emscripten { // __getDynamicPointerType returns (for polymorphic types only!) the type of the instance actually // pointed to. - int EMSCRIPTEN_KEEPALIVE __getDynamicPointerType(int p) { + int EMSCRIPTEN_KEEPALIVE __getDynamicPointerType(int p) { // use size_t void** vtable = *(void***)p; return (int)static_cast(vtable[-1]); } - // Calls to __dynamic_cast are generated by the compiler to implement dynamic_cast<>() -- its prototype is - // not available through any header file. It is called directly here because it allows run-time - // specification of the target pointer type (which can only be specified at compile time when using - // dynamic_cast<>(). - void* __dynamic_cast(void*, const std::type_info*, const std::type_info*, int); - - // __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 to) { - int ret = (int)__staticPointerCast((void *)p, __getDynamicPointerType(p), to); - if (ret < 0) { - return 0; - } - return ret; - } - const char* EMSCRIPTEN_KEEPALIVE __typeName(int p) { const std::type_info* ti = (const std::type_info*)p; size_t nameLen = std::min(strlen(ti->name()), (unsigned int)1024); From 7c93292f7dc7cd0e445c0bf7d76c1dc4d9f49453 Mon Sep 17 00:00:00 2001 From: Bill Welden Date: Mon, 28 Jan 2013 09:15:36 -0800 Subject: [PATCH 106/258] fix lint warning --- src/embind/embind.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 7210dbc14..55866231a 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -650,7 +650,7 @@ function __embind_register_smart_ptr( rawDestructor = FUNCTION_TABLE[rawDestructor]; rawGetPointee = FUNCTION_TABLE[rawGetPointee]; - if (name == "") { + if (name === "") { name = pointeeType.name + "Ptr"; } From ec5bc68515751aae5e90eb9a55af1908b2cb8329 Mon Sep 17 00:00:00 2001 From: Bill Welden Date: Mon, 28 Jan 2013 14:03:17 -0800 Subject: [PATCH 107/258] Revert "fix lint warning" This reverts commit 4a9e281a7d42595354c775ee1a79fdb9c3ec6354. --- src/embind/embind.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 55866231a..7210dbc14 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -650,7 +650,7 @@ function __embind_register_smart_ptr( rawDestructor = FUNCTION_TABLE[rawDestructor]; rawGetPointee = FUNCTION_TABLE[rawGetPointee]; - if (name === "") { + if (name == "") { name = pointeeType.name + "Ptr"; } From 40ad51a5c81881ae0ddb5c36f19e7d362107ccc6 Mon Sep 17 00:00:00 2001 From: Bill Welden Date: Mon, 28 Jan 2013 14:03:27 -0800 Subject: [PATCH 108/258] Revert "register_smart_ptr changed to .shared_ptr (mod to class definition) -- shared_ptr (outside of class definition) is still allowed" This reverts commit c38bb38e9d6393dfacb4afb0e9ba80ce42aee965. --- src/embind/embind.js | 155 +++++++++++++++++-------------- system/include/emscripten/bind.h | 62 ++++++------- system/lib/embind/bind.cpp | 20 +++- 3 files changed, 133 insertions(+), 104 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 7210dbc14..d8a4a4402 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -5,6 +5,7 @@ /*global Pointer_stringify, writeStringToMemory*/ /*global __emval_register, _emval_handle_array, __emval_decref*/ /*global ___getDynamicPointerType: false*/ +/*global ___dynamicPointerCast: false*/ /*global ___typeName:false*/ /*global ___staticPointerCast: false*/ @@ -307,12 +308,6 @@ function runDestructors(destructors) { } } -function refreshSmartPointee(handle) { - if (handle && handle.smartPointer) { - handle.ptr = handle.type.smartPointerType.getPointee(handle.smartPointer); - } -} - function makeInvoker(name, argCount, argTypes, invoker, fn) { if (!FUNCTION_TABLE[fn]) { throw new BindingError('function '+name+' is not defined'); @@ -334,9 +329,6 @@ function makeInvoker(name, argCount, argTypes, invoker, fn) { rv = argTypes[0].fromWireType(rv); } runDestructors(destructors); - for (i = 1; i < argCount; i++) { - refreshSmartPointee(arguments[i-1]); - } return rv; }; } @@ -545,21 +537,21 @@ RegisteredPointer.prototype.toWireType = function(destructors, handle) { if (!handle) { return null; } - if (handle.type.isPolymorphic) { - fromRawType = handle.type.getDynamicRawPointerType(handle.ptr); + if (handle.pointeeType.isPolymorphic) { + fromRawType = handle.pointeeType.getDynamicRawPointerType(handle.ptr); } else { - fromRawType = handle.type.rawType; + fromRawType = handle.pointeeType.rawType; } - if (fromRawType === this.type.rawType) { + if (fromRawType === this.pointeeType.rawType) { return this.isSmartPointer ? handle.smartPointer : handle.ptr; } - var ptr = staticPointerCast(handle.ptr, fromRawType, this.type.rawType); + var ptr = staticPointerCast(handle.ptr, fromRawType, this.pointeeType.rawType); if (this.isSmartPointer) { // todo: if ptr == handle.ptr, there's no need to allocate a new smartPtr! - var smartPtr = _malloc(16); // todo: can we get C++ to tell us the size of the pointer? - handle.type.smartPointerType.rawConstructor(smartPtr, ptr, handle.smartPointer); + var smartPtr = _malloc(16); + handle.pointeeType.smartPointerType.rawConstructor(smartPtr, ptr, handle.smartPointer); ptr = smartPtr; - destructors.push(handle.type.smartPointerType.rawDestructor); + destructors.push(handle.pointeeType.smartPointerType.rawDestructor); destructors.push(ptr); } return ptr; @@ -590,7 +582,7 @@ RegisteredPointer.prototype.fromWireType = function(ptr) { RegisteredPointer.prototype.getDynamicRawPointerType = function(ptr) { var type = null; if (this.isPolymorphic) { - if (this.rawGetPointee) { // todo: did you mean isSmartPtr? + if (this.rawGetPointee) { type = ___getDynamicPointerType(this.rawGetPointee(ptr)); } else { type = ___getDynamicPointerType(ptr); @@ -602,8 +594,8 @@ RegisteredPointer.prototype.getDynamicRawPointerType = function(ptr) { RegisteredPointer.prototype.getDynamicDowncastType = function(ptr) { var downcastType = null; var type = this.getDynamicRawPointerType(ptr); - if (type && type !== this.type.rawType) { - var derivation = Module.__getDerivationPath(type, this.type.rawType); + if (type && type !== this.pointeeType.rawType) { + var derivation = Module.__getDerivationPath(type, this.pointeeType.rawType); for (var i = 0; i < derivation.size(); i++) { downcastType = typeRegistry[derivation.at(i)]; if (downcastType) { @@ -623,7 +615,7 @@ RegisteredPointer.prototype.fromWireTypeAutoDowncast = function(ptr) { // ptr is } var toType = this.getDynamicDowncastType(ptr); if (toType) { - var fromType = this.type; + var fromType = this.pointeeType; if (this.isSmartPointer) { handle = toType.smartPointerType.fromWireType(ptr); } else { @@ -649,16 +641,12 @@ function __embind_register_smart_ptr( rawConstructor = FUNCTION_TABLE[rawConstructor]; rawDestructor = FUNCTION_TABLE[rawDestructor]; rawGetPointee = FUNCTION_TABLE[rawGetPointee]; - - if (name == "") { - name = pointeeType.name + "Ptr"; - } - + var Handle = createNamedFunction(name, function(ptr) { this.count = {value: 1}; this.smartPointer = ptr; // std::shared_ptr* this.ptr = rawGetPointee(ptr); // T* - this.type = pointeeType; + this.pointeeType = pointeeType; }); // TODO: test for SmartPtr.prototype.constructor property? @@ -692,7 +680,7 @@ function __embind_register_smart_ptr( this.ptr = undefined; }; var registeredPointer = new RegisteredPointer(Handle, pointeeType.isPolymorphic, true, rawGetPointee, rawConstructor, rawDestructor); - registeredPointer.type = pointeeType; + registeredPointer.pointeeType = pointeeType; pointeeType.smartPointerType = registerType(rawType, name, registeredPointer); } @@ -840,13 +828,11 @@ function __embind_register_class( h.count = {value: 1, ptr: ptr }; h.ptr = ptr; - h.type = type; // set below + h.pointeeType = type; // set below for(var prop in Handle.prototype) { - if (Handle.prototype.hasOwnProperty(prop)) { - var dp = Object.getOwnPropertyDescriptor(Handle.prototype, prop); - Object.defineProperty(h, prop, dp); - } + var dp = Object.getOwnPropertyDescriptor(Handle.prototype, prop); + Object.defineProperty(h, prop, dp); } return h; @@ -889,15 +875,15 @@ function __embind_register_class( // todo: clean this up! var registeredClass = new RegisteredPointer(Handle, isPolymorphic, false); var type = registerType(rawType, name, registeredClass); - registeredClass.type = type; + registeredClass.pointeeType = type; - registeredClass = new RegisteredPointer(Handle, isPolymorphic, false); + var registeredClass = new RegisteredPointer(Handle, isPolymorphic, false); registerType(rawPointerType, name + '*', registeredClass); - registeredClass.type = type; + registeredClass.pointeeType = type; // todo: implement const pointers (no modification Javascript side) - registeredClass = new RegisteredPointer(Handle, isPolymorphic, false); + var registeredClass = new RegisteredPointer(Handle, isPolymorphic, false); registerType(rawConstPointerType, name + ' const*', registeredClass); - registeredClass.type = type; + registeredClass.pointeeType = type; type.constructor = createNamedFunction(type.name, function() { var body = type.constructor.body; @@ -979,32 +965,26 @@ function __embind_register_class_method( rv = argTypes[0].fromWireType(rv); } runDestructors(destructors); - for (i = 1; i < argCount; i++) { - refreshSmartPointee(arguments[i-1]); - } return rv; }; classType.Handle.memberType[methodName] = "method"; }); } -function __embind_register_cast_method( +// todo: cast methods should require binding of their target types +function __embind_register_raw_cast_method( rawClassType, isPolymorphic, methodName, - rawRawReturnType, - rawSharedReturnType, - rawRawCaster, - rawSharedCaster + rawReturnType, + rawInvoker ) { requestDeferredRegistration(function() { var classType = requireRegisteredType(rawClassType, 'class'); methodName = Pointer_stringify(methodName); var humanName = classType.name + '.' + methodName; - var rawReturnType = requireRegisteredType(rawRawReturnType, 'method ' + humanName + ' return value'); - var sharedReturnType = requireRegisteredType(rawSharedReturnType, 'method ' + humanName + ' shared pointer return value'); - var rawCaster = FUNCTION_TABLE[rawRawCaster]; - var sharedCaster = FUNCTION_TABLE[rawSharedCaster]; + var returnType = requireRegisteredType(rawReturnType, 'method ' + humanName + ' return value'); + rawInvoker = FUNCTION_TABLE[rawInvoker]; classType.Handle.prototype[methodName] = function() { if (!this.ptr) { throw new BindingError('cannot call emscripten binding method ' + humanName + ' on deleted object'); @@ -1015,11 +995,11 @@ function __embind_register_cast_method( if (isPolymorphic) { // todo: this is all only to validate the cast -- cache the result var runtimeType = ___getDynamicPointerType(this.ptr); - var derivation = Module.__getDerivationPath(rawRawReturnType, runtimeType); // downcast is valid + var derivation = Module.__getDerivationPath(rawReturnType, runtimeType); // downcast is valid var size = derivation.size(); derivation.delete(); if (size === 0) { - derivation = Module.__getDerivationPath(runtimeType, rawRawReturnType); // upcast is valid + derivation = Module.__getDerivationPath(runtimeType, rawReturnType); // upcast is valid size = derivation.size(); derivation.delete(); if (size === 0) { @@ -1027,29 +1007,64 @@ function __embind_register_cast_method( } } } - var args; - var ptr; - var rv; - if (this.smartPointer) { - args = new Array(2); - ptr = _malloc(8); - args[0] = ptr; - args[1] = this.smartPointer; - sharedCaster.apply(null,args); // need a smart pointer raw invoker - rv = sharedReturnType.fromWireType(ptr); - } else { - args = new Array(1); - args[0] = this.ptr; - ptr = rawCaster.apply(null, args); - rv = rawReturnType.fromWireType(ptr); - rv.count = this.count; - this.count.value ++; - } + var args = new Array(1); + args[0] = this.ptr; + var ptr = rawInvoker.apply(null, args); + var rv = returnType.fromWireType(ptr); + rv.count = this.count; + this.count.value ++; return rv; }; }); } +// todo: cast methods should not be passed from the smart ptr to the contained object!! +function __embind_register_smart_cast_method( + rawPointerType, + rawReturnType, + returnPointeeType, + isPolymorphic, + methodName, + rawInvoker +) { + requestDeferredRegistration(function() { + var pointerType = requireRegisteredType(rawPointerType, 'smart pointer class'); + methodName = Pointer_stringify(methodName); + var humanName = pointerType.name + '.' + methodName; + var returnType = requireRegisteredType(rawReturnType, 'method ' + humanName + ' return value'); + rawInvoker = FUNCTION_TABLE[rawInvoker]; + pointerType.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 + 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) { + throw new CastError("Pointer conversion is not available"); + } + } + } + var args = new Array(2); + var ptr = _malloc(8); + args[0] = ptr; + args[1] = this.smartPointer; + rawInvoker.apply(null,args); + return returnType.fromWireType(ptr); + }; + }); +} + function __embind_register_class_classmethod( rawClassType, methodName, diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index a3789cabc..ce9053f26 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -124,14 +124,20 @@ namespace emscripten { size_t memberFunctionSize, void* memberFunction); - void _embind_register_cast_method( + void _embind_register_raw_cast_method( TYPEID classType, bool isPolymorphic, const char* methodName, TYPEID returnType, - TYPEID sharedReturnType, - GenericFunction rawInvoker, - GenericFunction sharedInvoker); + GenericFunction invoker); + + void _embind_register_smart_cast_method( + TYPEID pointerType, + TYPEID returnType, + TYPEID returnPointeeType, + bool isPolymorphic, + const char* methodName, + GenericFunction invoker); void _embind_register_class_field( TYPEID classType, @@ -268,6 +274,7 @@ namespace emscripten { extern "C" { int __getDynamicPointerType(int p); + int __dynamicPointerCast(int p, int to); } template @@ -276,14 +283,14 @@ namespace emscripten { }; template - struct performSharedCast { + struct performShared { static std::shared_ptr cast(std::shared_ptr from) { return std::dynamic_pointer_cast(from); }; }; template - struct performSharedCast { + struct performShared { static std::shared_ptr cast(std::shared_ptr from) { return std::shared_ptr(from, static_cast(from.get())); }; @@ -293,7 +300,7 @@ namespace emscripten { void function(const char* name, ReturnType (fn)(Args...), Policies...) { using namespace internal; - registerStandardTypes(); // todo: remove all + registerStandardTypes(); typename WithPolicies::template ArgTypeList args; _embind_register_function( @@ -618,12 +625,21 @@ namespace emscripten { reinterpret_cast(&get_pointee)); } - }; - template - class shared_ptr: public register_smart_ptr> { - public: - shared_ptr(const char* name): register_smart_ptr>(name) {}; + template + register_smart_ptr& cast(const char* methodName) { + using namespace internal; + typedef typename ReturnType::element_type ReturnPointeeType; + typedef typename PointerType::element_type PointeeType; + _embind_register_smart_cast_method( + TypeID::get(), + TypeID::get(), + TypeID::get(), + std::is_polymorphic::value, + methodName, + reinterpret_cast(&performShared::value>::cast)); + return *this; + } }; //////////////////////////////////////////////////////////////////////////////// @@ -761,31 +777,13 @@ namespace emscripten { template class_& cast(const char* methodName) { using namespace internal; - //typedef typename std::shared_ptr PointerType; - typedef typename std::shared_ptr SharedReturnType; - _embind_register_cast_method( + _embind_register_raw_cast_method( TypeID::get(), std::is_polymorphic::value, methodName, TypeID::get(), - TypeID::get(), - reinterpret_cast(&performRawStaticCast), - reinterpret_cast(&performSharedCast::value>::cast)); - return *this; - } - - class_& shared_ptr(const char* ptrName = "") { - using namespace internal; - typedef typename std::shared_ptr PointerType; - - _embind_register_smart_ptr( - TypeID::get(), - TypeID::get(), - ptrName, - reinterpret_cast(&raw_smart_pointer_constructor), - reinterpret_cast(&raw_destructor), - reinterpret_cast(&get_pointee)); + reinterpret_cast(&performRawStaticCast)); return *this; } }; diff --git a/system/lib/embind/bind.cpp b/system/lib/embind/bind.cpp index bc79876fe..5e82bb7eb 100755 --- a/system/lib/embind/bind.cpp +++ b/system/lib/embind/bind.cpp @@ -101,7 +101,7 @@ namespace emscripten { // __getDerivationPath returns an array of type_info pointers describing the derivation chain starting with // the derived type and proceeding toward (and ending with) the base type. Types are only included if they // appear on all possible derivation paths. - std::vector __getDerivationPath(int dv, const int bs) { // todo: use emval array to pass return value?? + std::vector __getDerivationPath(int dv, const int bs) { std::vector> paths; const std::type_info* dv1 = (std::type_info*)dv; @@ -176,11 +176,27 @@ namespace emscripten { // __getDynamicPointerType returns (for polymorphic types only!) the type of the instance actually // pointed to. - int EMSCRIPTEN_KEEPALIVE __getDynamicPointerType(int p) { // use size_t + int EMSCRIPTEN_KEEPALIVE __getDynamicPointerType(int p) { void** vtable = *(void***)p; return (int)static_cast(vtable[-1]); } + // Calls to __dynamic_cast are generated by the compiler to implement dynamic_cast<>() -- its prototype is + // not available through any header file. It is called directly here because it allows run-time + // specification of the target pointer type (which can only be specified at compile time when using + // dynamic_cast<>(). + void* __dynamic_cast(void*, const std::type_info*, const std::type_info*, int); + + // __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 to) { + int ret = (int)__staticPointerCast((void *)p, __getDynamicPointerType(p), to); + if (ret < 0) { + return 0; + } + return ret; + } + const char* EMSCRIPTEN_KEEPALIVE __typeName(int p) { const std::type_info* ti = (const std::type_info*)p; size_t nameLen = std::min(strlen(ti->name()), (unsigned int)1024); From c42eb7377eb547237d670238d7062c86e9b08673 Mon Sep 17 00:00:00 2001 From: Bill Welden Date: Mon, 28 Jan 2013 15:21:59 -0800 Subject: [PATCH 109/258] Removed explicit cast capability from bindings. --- src/embind/embind.js | 95 -------------------------------- system/include/emscripten/bind.h | 63 --------------------- 2 files changed, 158 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index d8a4a4402..75450735c 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -5,7 +5,6 @@ /*global Pointer_stringify, writeStringToMemory*/ /*global __emval_register, _emval_handle_array, __emval_decref*/ /*global ___getDynamicPointerType: false*/ -/*global ___dynamicPointerCast: false*/ /*global ___typeName:false*/ /*global ___staticPointerCast: false*/ @@ -971,100 +970,6 @@ function __embind_register_class_method( }); } -// todo: cast methods should require binding of their target types -function __embind_register_raw_cast_method( - rawClassType, - isPolymorphic, - methodName, - rawReturnType, - rawInvoker -) { - requestDeferredRegistration(function() { - var classType = requireRegisteredType(rawClassType, 'class'); - methodName = Pointer_stringify(methodName); - var humanName = classType.name + '.' + methodName; - var returnType = requireRegisteredType(rawReturnType, 'method ' + humanName + ' return value'); - rawInvoker = FUNCTION_TABLE[rawInvoker]; - classType.Handle.prototype[methodName] = function() { - if (!this.ptr) { - throw new BindingError('cannot call emscripten binding method ' + humanName + ' on deleted object'); - } - if (arguments.length !== 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(rawReturnType, runtimeType); // downcast is valid - var size = derivation.size(); - derivation.delete(); - if (size === 0) { - derivation = Module.__getDerivationPath(runtimeType, rawReturnType); // upcast is valid - size = derivation.size(); - derivation.delete(); - if (size === 0) { - throw new CastError("Pointer conversion is not available"); - } - } - } - var args = new Array(1); - args[0] = this.ptr; - var ptr = rawInvoker.apply(null, args); - var rv = returnType.fromWireType(ptr); - rv.count = this.count; - this.count.value ++; - return rv; - }; - }); -} - -// todo: cast methods should not be passed from the smart ptr to the contained object!! -function __embind_register_smart_cast_method( - rawPointerType, - rawReturnType, - returnPointeeType, - isPolymorphic, - methodName, - rawInvoker -) { - requestDeferredRegistration(function() { - var pointerType = requireRegisteredType(rawPointerType, 'smart pointer class'); - methodName = Pointer_stringify(methodName); - var humanName = pointerType.name + '.' + methodName; - var returnType = requireRegisteredType(rawReturnType, 'method ' + humanName + ' return value'); - rawInvoker = FUNCTION_TABLE[rawInvoker]; - pointerType.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 - 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) { - throw new CastError("Pointer conversion is not available"); - } - } - } - var args = new Array(2); - var ptr = _malloc(8); - args[0] = ptr; - args[1] = this.smartPointer; - rawInvoker.apply(null,args); - return returnType.fromWireType(ptr); - }; - }); -} - function __embind_register_class_classmethod( rawClassType, methodName, diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index ce9053f26..70a5b60c8 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -124,21 +124,6 @@ namespace emscripten { size_t memberFunctionSize, void* memberFunction); - void _embind_register_raw_cast_method( - TYPEID classType, - bool isPolymorphic, - const char* methodName, - TYPEID returnType, - GenericFunction invoker); - - void _embind_register_smart_cast_method( - TYPEID pointerType, - TYPEID returnType, - TYPEID returnPointeeType, - bool isPolymorphic, - const char* methodName, - GenericFunction invoker); - void _embind_register_class_field( TYPEID classType, const char* fieldName, @@ -274,28 +259,8 @@ namespace emscripten { extern "C" { int __getDynamicPointerType(int p); - int __dynamicPointerCast(int p, int to); } - template - ToType& performRawStaticCast(FromType& from) { - return *static_cast(&from); - }; - - template - struct performShared { - static std::shared_ptr cast(std::shared_ptr from) { - return std::dynamic_pointer_cast(from); - }; - }; - - template - struct performShared { - static std::shared_ptr cast(std::shared_ptr from) { - return std::shared_ptr(from, static_cast(from.get())); - }; - }; - template void function(const char* name, ReturnType (fn)(Args...), Policies...) { using namespace internal; @@ -625,21 +590,6 @@ namespace emscripten { reinterpret_cast(&get_pointee)); } - - template - register_smart_ptr& cast(const char* methodName) { - using namespace internal; - typedef typename ReturnType::element_type ReturnPointeeType; - typedef typename PointerType::element_type PointeeType; - _embind_register_smart_cast_method( - TypeID::get(), - TypeID::get(), - TypeID::get(), - std::is_polymorphic::value, - methodName, - reinterpret_cast(&performShared::value>::cast)); - return *this; - } }; //////////////////////////////////////////////////////////////////////////////// @@ -773,19 +723,6 @@ namespace emscripten { reinterpret_cast(&internal::ArrayAccessSetInvoker::invoke)); return *this; } - - template - class_& cast(const char* methodName) { - using namespace internal; - - _embind_register_raw_cast_method( - TypeID::get(), - std::is_polymorphic::value, - methodName, - TypeID::get(), - reinterpret_cast(&performRawStaticCast)); - return *this; - } }; //////////////////////////////////////////////////////////////////////////////// From 79a7db6c2f1fee9e5822adbcbf102bea7aeb78ab Mon Sep 17 00:00:00 2001 From: Bill Welden Date: Wed, 30 Jan 2013 14:58:27 -0800 Subject: [PATCH 110/258] - Classes now inherit elements from grandparent (etc.) as well as parent classes - Removed dead code --- src/embind/embind.js | 123 +------------------------------------------ 1 file changed, 2 insertions(+), 121 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 75450735c..f875aa9ad 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -73,9 +73,11 @@ function createInheritedFunctionOrProperty(name, type, nameInBaseClass, baseClas get: upcastingWrapper(baseClassDescriptor.get), set: upcastingWrapper(baseClassDescriptor.set) }); + type.Handle.memberType[name] = 'field'; } else if (baseClassPrototype.constructor.memberType[nameInBaseClass] === 'method') { var baseClassMethod = baseClassPrototype[nameInBaseClass]; type.Handle.prototype[name] = createNamedFunction(name, upcastingWrapper(baseClassMethod)); + type.Handle.memberType[name] = 'method'; } } @@ -683,127 +685,6 @@ function __embind_register_smart_ptr( pointeeType.smartPointerType = registerType(rawType, name, registeredPointer); } -function RegisteredRawPointer(isPolymorphic, classType, Handle) { - this.isPolymorphic = isPolymorphic; - this.pointeeType = classType; - this.Handle = Handle; -} - -RegisteredRawPointer.prototype.toWireType = function(destructors, o) { - return o.ptr; -}; - -RegisteredRawPointer.prototype.fromWireType = function(ptr) { - return new this.Handle(ptr); -}; - -RegisteredRawPointer.prototype.fromWireTypeAutoDowncast = function(ptr) { - if (this.isPolymorphic) { - var toRawType = ___getDynamicPointerType(ptr); - if (toRawType === null || toRawType === this.pointeeType.rawType) { - return new this.Handle(ptr); - } - var derivation = Module.__getDerivationPath(toRawType, this.pointeeType.rawType); - var candidateType = null; - for (var i = 0; i < derivation.size(); i++) { - candidateType = typeRegistry[derivation.at(i)]; - if (candidateType) { - break; - } - } - derivation.delete(); - if (candidateType === null) { - return new this.Handle(ptr); - } - var handle = candidateType.fromWireType(ptr); - handle.ptr = ___staticPointerCast(handle.ptr, this.pointeeType.rawType, candidateType.rawType); - // todo: can come back -1 or -2!! Throw appropriate exception - return handle; - } else { - return new this.Handle(ptr); - } -}; - -function RegisteredClassInstance(constructor, Handle) { - this.constructor = constructor; - this.Handle = Handle; -} - -function __embind_register_vector( - vectorType, - elementType, - name, - constructor, - destructor, - length, - getter, - setter -) { - name = Pointer_stringify(name); - elementType = requireRegisteredType(elementType, 'vector ' + name); - - constructor = FUNCTION_TABLE[constructor]; - destructor = FUNCTION_TABLE[destructor]; - length = FUNCTION_TABLE[length]; - getter = FUNCTION_TABLE[getter]; - setter = FUNCTION_TABLE[setter]; - - registerType(vectorType, name, { - name: name, - fromWireType: function(ptr) { - var arr = []; - Object.defineProperty(arr, 'delete', { - writable: false, - enumerable: false, - configurable: false, - value: function() { - var needsToBeDeleted = elementType.hasOwnProperty('Handle'); - for (var i = 0; i < arr.length; i++) { - if (needsToBeDeleted) { - arr[i].delete(); - } - } - } - }); - - var n = length(ptr); - for (var i = 0; i < n; i++) { - var v = elementType.fromWireType(getter(ptr, i)); - arr.push(v); - } - - destructor(ptr); - return arr; - }, - toWireType: function(destructors, o) { - var vec = constructor(); - for (var val in o) { - setter(vec, elementType.toWireType(destructors, o[val])); - } - runDestructors(destructors); - - destructors.push(destructor); - destructors.push(vec); - return vec; - } - }); -} - -RegisteredClassInstance.prototype.toWireType = function(destructors, o) { - return o.ptr; -}; - -RegisteredClassInstance.prototype.fromWireType = function(ptr) { - return new this.Handle(ptr); -}; - -function RegisteredRawConstPointer() { -} - -RegisteredRawConstPointer.prototype.toWireType = function(destructors, o) { - return o.ptr; -}; - // TODO: null pointers are always zero (not a Handle) in Javascript function __embind_register_class( rawType, From 5b3d12c90558f0472c374349c5a4cdce474832a4 Mon Sep 17 00:00:00 2001 From: Bill Welden Date: Thu, 31 Jan 2013 09:42:34 -0800 Subject: [PATCH 111/258] Can now reference base classes through multiple levels of inheritance if one or more intermediate classes are not bound. Warning: we know nothing about the members of the unbound classes, so you might end up inheriting the bound base class's member when a C++ routine would inherit the identically named member from the unbound class. --- src/embind/embind.js | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index f875aa9ad..21a5dc46e 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -81,24 +81,35 @@ function createInheritedFunctionOrProperty(name, type, nameInBaseClass, baseClas } } +function collectRegisteredBaseClasses(rawType) { + var rawBaseTypes = Module.__getBaseClasses(rawType); + var baseTypes = []; + for (var i = 0; i < rawBaseTypes.size(); i++) { + var baseType = typeRegistry[rawBaseTypes.at(i)]; + if (baseType) { + baseTypes.push(baseType); + } else { + baseTypes = baseTypes.concat(collectRegisteredBaseClasses(rawBaseTypes.at(i))); + } + } + return baseTypes; +} + function resolveType(type) { if (!type.resolved) { var baseClassType, name, baseProto; var inheritedNames = {}; - var rawBaseClassTypes = Module.__getBaseClasses(type.rawType); - for (var i = 0; i < rawBaseClassTypes.size(); i++) { - var rawBaseClassType = rawBaseClassTypes.at(i); - baseClassType = typeRegistry[rawBaseClassType]; - if (baseClassType) { - resolveType(baseClassType); - baseProto = baseClassType.Handle.prototype; - for (name in baseProto) { - if (baseProto.hasOwnProperty(name) && baseClassType.Handle.memberType[name]) { - if (!(name in inheritedNames)) { - inheritedNames[name] = []; - } - inheritedNames[name].push(baseClassType); + var baseTypes = collectRegisteredBaseClasses(type.rawType); + for (var i = 0; i < baseTypes.length; i++) { + var baseType = baseTypes[i]; + resolveType(baseType); + baseProto = baseType.Handle.prototype; + for (name in baseProto) { + if (baseProto.hasOwnProperty(name) && baseType.Handle.memberType[name]) { + if (!(name in inheritedNames)) { + inheritedNames[name] = []; } + inheritedNames[name].push(baseType); } } } From 3b6ea45565ae17f32d77933b505a6ccfe7a8967f Mon Sep 17 00:00:00 2001 From: Andy Friesen Date: Thu, 31 Jan 2013 10:10:49 -0800 Subject: [PATCH 112/258] emscripten::internal::optional is now copyable. Fix JSInterface::JSInterface copy constructor to take a const& --- system/include/emscripten/bind.h | 31 +++++++++++++++++++++++-------- tests/embind/embind_test.cpp | 21 +++++++++++++++++++++ tests/embind/embind_test.js | 5 +++++ 3 files changed, 49 insertions(+), 8 deletions(-) diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index 70a5b60c8..99b29e9cb 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -817,13 +817,22 @@ namespace emscripten { } } - optional(const optional&) = delete; + optional(const optional& rhs) + : initialized(false) + { + *this = rhs; + } T& operator*() { assert(initialized); return *get(); } + const T& operator*() const { + assert(initialized); + return *get(); + } + explicit operator bool() const { return initialized; } @@ -837,12 +846,14 @@ namespace emscripten { return *this; } - optional& operator=(optional& o) { + optional& operator=(const optional& o) { if (initialized) { get()->~T(); } - new(get()) T(*o); - initialized = true; + if (o.initialized) { + new(get()) T(*o); + } + initialized = o.initialized; } private: @@ -850,6 +861,10 @@ namespace emscripten { return reinterpret_cast(&data); } + T const* get() const { + return reinterpret_cast(&data); + } + bool initialized; typename std::aligned_storage::type data; }; @@ -865,9 +880,9 @@ namespace emscripten { initialize(handle); } - JSInterface(JSInterface& obj) { - jsobj = obj.jsobj; - } + JSInterface(const JSInterface& obj) + : jsobj(obj.jsobj) + {} template ReturnType call(const char* name, Args... args) { @@ -875,7 +890,7 @@ namespace emscripten { return Caller::call(*jsobj, name, args...); } - static std::shared_ptr cloneToSharedPtr(JSInterface& i) { + static std::shared_ptr cloneToSharedPtr(const JSInterface& i) { return std::make_shared(i); } diff --git a/tests/embind/embind_test.cpp b/tests/embind/embind_test.cpp index dc052d1af..485cdfcd8 100644 --- a/tests/embind/embind_test.cpp +++ b/tests/embind/embind_test.cpp @@ -233,6 +233,24 @@ void emval_test_call_function(val v, int i, float f, TupleVector tv, StructVecto v(i, f, tv, sv); } +void optional_test_copy() { + using emscripten::internal::optional; + + optional foo = 22; + optional bar(foo); + + return bool(bar); +} + +void optional_test_copy2() { + using emscripten::internal::optional; + + optional foo; + optional bar(foo); + + return bool(bar); +} + EMSCRIPTEN_BINDINGS(([]() { function("mallinfo", &emval_test_mallinfo); @@ -337,4 +355,7 @@ EMSCRIPTEN_BINDINGS(([]() { function("emval_test_call_method3", &emval_test_call_method3); function("emval_test_call_function", &emval_test_call_function); + + function('optional_test_copy', &optional_test_copy); + function('optional_test_copy2', &optional_test_copy2); })); diff --git a/tests/embind/embind_test.js b/tests/embind/embind_test.js index 8c61553bc..9ba6a48f5 100644 --- a/tests/embind/embind_test.js +++ b/tests/embind/embind_test.js @@ -390,4 +390,9 @@ module({ assert.deepEqual(['called'], calls); }, }); + + test('emscripten::internal::optional', function () { + assert.true(cm.optional_test_copy()); + assert.false(cm.optional_test_copy2()); + }); }); From 62b9f6a00f0eee036e7dfb4326a813c31d3ba41e Mon Sep 17 00:00:00 2001 From: Todd Lee Date: Thu, 31 Jan 2013 12:24:56 -0800 Subject: [PATCH 113/258] header dependency fix --- system/include/emscripten/wire.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/system/include/emscripten/wire.h b/system/include/emscripten/wire.h index 1173034f7..dc612e11d 100755 --- a/system/include/emscripten/wire.h +++ b/system/include/emscripten/wire.h @@ -8,6 +8,8 @@ // We'll call the on-the-wire type WireType. #include +#include +#include namespace emscripten { namespace internal { From 4fa56f796784b44a2b70aa69c5c9de012634ecd4 Mon Sep 17 00:00:00 2001 From: Todd Lee Date: Thu, 31 Jan 2013 18:31:56 -0800 Subject: [PATCH 114/258] move vecFromJSArray from bind.h to val.h to make it unnecessary to include bind.h --- system/include/emscripten/bind.h | 12 ------------ system/include/emscripten/val.h | 13 +++++++++++++ 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index 99b29e9cb..7a87107f1 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -728,18 +728,6 @@ namespace emscripten { //////////////////////////////////////////////////////////////////////////////// // VECTORS //////////////////////////////////////////////////////////////////////////////// - template - std::vector vecFromJSArray(val v) { - auto l = v["length"].as(); - - std::vector rv; - for(unsigned i = 0; i < l; ++i) { - rv.push_back(v[i].as()); - } - - return rv; - }; - template class_> register_vector(const char* name) { using namespace std; diff --git a/system/include/emscripten/val.h b/system/include/emscripten/val.h index 857a1bdf4..3952e008d 100644 --- a/system/include/emscripten/val.h +++ b/system/include/emscripten/val.h @@ -2,6 +2,7 @@ #include // uintptr_t #include +#include namespace emscripten { namespace internal { @@ -237,4 +238,16 @@ namespace emscripten { } }; } + + template + std::vector vecFromJSArray(val v) { + auto l = v["length"].as(); + + std::vector rv; + for(unsigned i = 0; i < l; ++i) { + rv.push_back(v[i].as()); + } + + return rv; + }; } From 70aebcde9e97a9befdb7b912d0a3c1f3b5b9a003 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Fri, 1 Feb 2013 17:15:19 -0800 Subject: [PATCH 115/258] EMSCRIPTEN_KEEPALIVE has been removed --- system/lib/embind/bind.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/system/lib/embind/bind.cpp b/system/lib/embind/bind.cpp index 5e82bb7eb..cbb4b1833 100755 --- a/system/lib/embind/bind.cpp +++ b/system/lib/embind/bind.cpp @@ -138,7 +138,7 @@ namespace emscripten { return derivationPath; } - void* EMSCRIPTEN_KEEPALIVE __staticPointerCast(void* p, int from, int to) { + void* __staticPointerCast(void* p, int from, int to) { std::vector> paths; int direction = 1; @@ -176,7 +176,7 @@ namespace emscripten { // __getDynamicPointerType returns (for polymorphic types only!) the type of the instance actually // pointed to. - int EMSCRIPTEN_KEEPALIVE __getDynamicPointerType(int p) { + int __getDynamicPointerType(int p) { void** vtable = *(void***)p; return (int)static_cast(vtable[-1]); } @@ -189,7 +189,7 @@ 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 to) { + int __dynamicPointerCast(int p, int to) { int ret = (int)__staticPointerCast((void *)p, __getDynamicPointerType(p), to); if (ret < 0) { return 0; @@ -197,7 +197,7 @@ namespace emscripten { return ret; } - const char* EMSCRIPTEN_KEEPALIVE __typeName(int p) { + const char* __typeName(int p) { const std::type_info* ti = (const std::type_info*)p; size_t nameLen = std::min(strlen(ti->name()), (unsigned int)1024); char* name = (char *)malloc(nameLen+1); @@ -221,7 +221,7 @@ namespace emscripten { return bases; } - int EMSCRIPTEN_KEEPALIVE __peek32(int p) { + int __peek32(int p) { return *(int *)p; } From 848c186df3a8f1eff6e7b1af0b08df25aefae0e3 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Fri, 1 Feb 2013 17:18:25 -0800 Subject: [PATCH 116/258] Fix cxa_demangle compile errors --- system/lib/libcxxabi/src/cxa_demangle.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/system/lib/libcxxabi/src/cxa_demangle.cpp b/system/lib/libcxxabi/src/cxa_demangle.cpp index c1e126035..804ea7ce5 100644 --- a/system/lib/libcxxabi/src/cxa_demangle.cpp +++ b/system/lib/libcxxabi/src/cxa_demangle.cpp @@ -5695,7 +5695,7 @@ public: if (__right_ != 0) r += __right_->size(); else if (__size_ != 0) - r += static_cast(snprintf(0, 0, "%ld", __size_)); + r += static_cast(snprintf(0, 0, "%lu", static_cast(__size_))); const_cast(__cached_size_) = static_cast(r); } return static_cast(__cached_size_); @@ -5710,7 +5710,7 @@ public: buf = __right_->get_demangled_name(buf); else if (__size_ != 0) { - int rs = sprintf(buf, "%ld", __size_); + int rs = sprintf(buf, "%lu", static_cast(__size_)); buf += rs; } *buf++ = ']'; @@ -5735,7 +5735,7 @@ public: if (__right_ != 0) r += __right_->size(); else if (__size_ != 0) - r += static_cast(snprintf(0, 0, "%ld", __size_)); + r += static_cast(snprintf(0, 0, "%lu", static_cast(__size_))); return r; } @@ -5747,7 +5747,7 @@ public: buf = __right_->get_demangled_name(buf); else if (__size_ != 0) { - int off = sprintf(buf, "%ld", __size_); + int off = sprintf(buf, "%lu", static_cast(__size_)); buf += off; } char* t = buf; From aef932e3a9278087e96b15c1d02e10e28ce58f47 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Fri, 1 Feb 2013 18:52:37 -0800 Subject: [PATCH 117/258] Bring back EMSCRIPTEN_KEEPALIVE --- system/include/emscripten/emscripten.h | 2 +- system/lib/embind/bind.cpp | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/system/include/emscripten/emscripten.h b/system/include/emscripten/emscripten.h index 61634b0e4..a4474670d 100644 --- a/system/include/emscripten/emscripten.h +++ b/system/include/emscripten/emscripten.h @@ -24,7 +24,7 @@ extern "C" { * with closure, asm.js, etc. For example * -s EXPORTED_FUNCTIONS=["_main", "myfunc"] */ -/* #define EMSCRIPTEN_KEEPALIVE __attribute__((used)) */ +#define EMSCRIPTEN_KEEPALIVE __attribute__((used)) /* * Interface to the underlying JS engine. This function will diff --git a/system/lib/embind/bind.cpp b/system/lib/embind/bind.cpp index cbb4b1833..5e82bb7eb 100755 --- a/system/lib/embind/bind.cpp +++ b/system/lib/embind/bind.cpp @@ -138,7 +138,7 @@ namespace emscripten { return derivationPath; } - void* __staticPointerCast(void* p, int from, int to) { + void* EMSCRIPTEN_KEEPALIVE __staticPointerCast(void* p, int from, int to) { std::vector> paths; int direction = 1; @@ -176,7 +176,7 @@ namespace emscripten { // __getDynamicPointerType returns (for polymorphic types only!) the type of the instance actually // pointed to. - int __getDynamicPointerType(int p) { + int EMSCRIPTEN_KEEPALIVE __getDynamicPointerType(int p) { void** vtable = *(void***)p; return (int)static_cast(vtable[-1]); } @@ -189,7 +189,7 @@ namespace emscripten { // __dynamicPointerCast performs a C++ dynamic_cast<>() operation, but allowing run-time specification of // the from and to pointer types. - int __dynamicPointerCast(int p, int to) { + int EMSCRIPTEN_KEEPALIVE __dynamicPointerCast(int p, int to) { int ret = (int)__staticPointerCast((void *)p, __getDynamicPointerType(p), to); if (ret < 0) { return 0; @@ -197,7 +197,7 @@ namespace emscripten { return ret; } - const char* __typeName(int p) { + const char* EMSCRIPTEN_KEEPALIVE __typeName(int p) { const std::type_info* ti = (const std::type_info*)p; size_t nameLen = std::min(strlen(ti->name()), (unsigned int)1024); char* name = (char *)malloc(nameLen+1); @@ -221,7 +221,7 @@ namespace emscripten { return bases; } - int __peek32(int p) { + int EMSCRIPTEN_KEEPALIVE __peek32(int p) { return *(int *)p; } From 771ee9e921504e77fe1750173e38c35857a3f016 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Mon, 4 Feb 2013 22:49:29 -0800 Subject: [PATCH 118/258] Fix a bunch of compiler warnings --- system/include/emscripten/bind.h | 2 + system/lib/embind/bind.cpp | 98 ++++++++++++++++---------------- 2 files changed, 51 insertions(+), 49 deletions(-) diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index 7a87107f1..28fe1f3a8 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -710,6 +710,7 @@ namespace emscripten { TypeID::get(), TypeID::get(), reinterpret_cast(&internal::ArrayAccessGetInvoker::invoke)); + return *this; } template @@ -842,6 +843,7 @@ namespace emscripten { new(get()) T(*o); } initialized = o.initialized; + return *this; } private: diff --git a/system/lib/embind/bind.cpp b/system/lib/embind/bind.cpp index 5e82bb7eb..39ee02bec 100755 --- a/system/lib/embind/bind.cpp +++ b/system/lib/embind/bind.cpp @@ -93,51 +93,60 @@ namespace emscripten { } } + // __getDerivationPath returns an array of type_info pointers describing the derivation chain starting with + // the derived type and proceeding toward (and ending with) the base type. Types are only included if they + // appear on all possible derivation paths. + std::vector __getDerivationPath(int dv, const int bs) { + std::vector> paths; + + 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(dv1); + const __cxxabiv1::__class_type_info* bs2 = dynamic_cast(bs1); + + if (dv2 && bs2) { + __cxxabiv1::__getDerivationPaths(dv2, bs2, std::vector(), paths); + } + + std::vector derivationPath; + if (paths.size() > 0) { + for (int j = 0; j < paths[0].size(); j++) { + bool disqualified = false; + for (int i = 1; i < paths.size(); i++) { + bool found = false; + for (int j2 = 0; j2 < paths[i].size(); j2++) { + if (paths[i][j2] == paths[0][j]) { + found = true; + break; + } + } + if (!found) { + disqualified = true; + break; + } + } + if (!disqualified) { + derivationPath.emplace_back((int)paths[0][j]); + } + } + } + return derivationPath; + } + + std::vector __getBaseClasses(int tp) { + std::vector baseTypes = __internalGetBaseClasses((const __cxxabiv1::__class_type_info*)tp); + std::vector bases; + for (int j = 0; j < baseTypes.size(); j++) { + bases.emplace_back((int)baseTypes[j]); + } + return bases; + } + extern "C" { // These three routines constitute an extension to the compiler's support for dynamic type conversion. // They are used by embind.js to implement automatic downcasting of return values which are pointers to // polymorphic objects. - // __getDerivationPath returns an array of type_info pointers describing the derivation chain starting with - // the derived type and proceeding toward (and ending with) the base type. Types are only included if they - // appear on all possible derivation paths. - std::vector __getDerivationPath(int dv, const int bs) { - std::vector> paths; - - 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(dv1); - const __cxxabiv1::__class_type_info* bs2 = dynamic_cast(bs1); - - if (dv2 && bs2) { - __cxxabiv1::__getDerivationPaths(dv2, bs2, std::vector(), paths); - } - - std::vector derivationPath; - if (paths.size() > 0) { - for (int j = 0; j < paths[0].size(); j++) { - bool disqualified = false; - for (int i = 1; i < paths.size(); i++) { - bool found = false; - for (int j2 = 0; j2 < paths[i].size(); j2++) { - if (paths[i][j2] == paths[0][j]) { - found = true; - break; - } - } - if (!found) { - disqualified = true; - break; - } - } - if (!disqualified) { - derivationPath.emplace_back((int)paths[0][j]); - } - } - } - return derivationPath; - } - void* EMSCRIPTEN_KEEPALIVE __staticPointerCast(void* p, int from, int to) { std::vector> paths; int direction = 1; @@ -212,15 +221,6 @@ namespace emscripten { return name; } - std::vector __getBaseClasses(int tp) { - std::vector baseTypes = __internalGetBaseClasses((const __cxxabiv1::__class_type_info*)tp); - std::vector bases; - for (int j = 0; j < baseTypes.size(); j++) { - bases.emplace_back((int)baseTypes[j]); - } - return bases; - } - int EMSCRIPTEN_KEEPALIVE __peek32(int p) { return *(int *)p; } From 51dfc764bd72ef54b00617e6dcedf017f52a53d0 Mon Sep 17 00:00:00 2001 From: Bill Welden Date: Tue, 5 Feb 2013 13:25:32 -0800 Subject: [PATCH 119/258] Fix a couple of merge issues bringing down the lastest emscripten. --- system/lib/embind/bind.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/system/lib/embind/bind.cpp b/system/lib/embind/bind.cpp index 39ee02bec..705cc4db3 100755 --- a/system/lib/embind/bind.cpp +++ b/system/lib/embind/bind.cpp @@ -241,5 +241,3 @@ namespace emscripten { } } - - From dd618d24fc2500ec49fe536936ea404e92cc7150 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Fri, 15 Feb 2013 16:49:36 -0800 Subject: [PATCH 120/258] implement an api for allowing JS subclasses of C++ interfaces --- system/include/emscripten/bind.h | 64 +++++++++++++++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index 28fe1f3a8..a807be4cb 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -277,6 +277,11 @@ namespace emscripten { } namespace internal { + template + ClassType* operator_new(Args... args) { + return new ClassType(args...); + } + template ClassType* raw_constructor( typename internal::BindingType::WireType... args @@ -595,7 +600,49 @@ namespace emscripten { //////////////////////////////////////////////////////////////////////////////// // CLASSES //////////////////////////////////////////////////////////////////////////////// - // TODO: support class definitions without constructors. + + // abstract classes + template + class wrapper : public T { + public: + wrapper(const val& wrapped) + : wrapped(wrapped) + {} + + template + ReturnType call(const char* name, Args... args) const { + return Caller::call(wrapped, name, args...); + } + + private: + // this class only exists because you can't partially specialize function templates + template + struct Caller { + static ReturnType call(const val& v, const char* name, Args... args) { + return v.call(name, args...).template as(); + } + }; + + template + struct Caller { + static void call(const val& v, const char* name, Args... args) { + v.call_void(name, args...); + } + }; + + /* + void assertInitialized() { + if (!jsobj) { + internal::_embind_fatal_error( + "Cannot invoke call on uninitialized Javascript interface wrapper.", "JSInterface"); + } + }*/ + val wrapped; + }; + +#define EMSCRIPTEN_WRAPPER(T) \ + T(const ::emscripten::val& v): wrapper(v) {} + // TODO: support external class constructors template class class_ { @@ -626,6 +673,21 @@ namespace emscripten { return *this; } + template + class_& allow_subclass() { + using namespace internal; + + // TODO: unique or anonymous name + class_("WrapperType") + .template constructor() + ; + + return classmethod( + "implement", + &operator_new, + allow_raw_pointer()); + } + template class_& method(const char* methodName, ReturnType (ClassType::*memberFunction)(Args...), Policies...) { using namespace internal; From 494461157beba8f685349b2d35f73bb0a14070c5 Mon Sep 17 00:00:00 2001 From: Bill Welden Date: Mon, 18 Feb 2013 14:36:01 -0800 Subject: [PATCH 121/258] NSTAR-1099 Eliminate naming conflict in embind handles --- src/embind/embind.js | 116 +++++++++++++++++++++---------------------- 1 file changed, 56 insertions(+), 60 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 21a5dc46e..b0eef51f4 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -51,13 +51,13 @@ function performDeferredRegistrations(){ function createInheritedFunctionOrProperty(name, type, nameInBaseClass, baseClassType) { function upcastingWrapper(method) { return function() { - var baseClassPtr = ___staticPointerCast(this.ptr, type.rawType, baseClassType.rawType); - if (baseClassPtr === this.ptr) { + var baseClassPtr = ___staticPointerCast(this.$$.ptr, type.rawType, baseClassType.rawType); + if (baseClassPtr === this.$$.ptr) { return method.apply(this, arguments); } else { var handle = this.clone(); try { - handle.ptr = baseClassPtr; + handle.$$.ptr = baseClassPtr; return method.apply(handle, arguments); } finally { handle.delete(); @@ -549,21 +549,21 @@ RegisteredPointer.prototype.toWireType = function(destructors, handle) { if (!handle) { return null; } - if (handle.pointeeType.isPolymorphic) { - fromRawType = handle.pointeeType.getDynamicRawPointerType(handle.ptr); + if (handle.$$.pointeeType.isPolymorphic) { + fromRawType = handle.$$.pointeeType.getDynamicRawPointerType(handle.$$.ptr); } else { - fromRawType = handle.pointeeType.rawType; + fromRawType = handle.$$.pointeeType.rawType; } if (fromRawType === this.pointeeType.rawType) { - return this.isSmartPointer ? handle.smartPointer : handle.ptr; + return this.isSmartPointer ? handle.$$.smartPtr : handle.$$.ptr; } - var ptr = staticPointerCast(handle.ptr, fromRawType, this.pointeeType.rawType); + var ptr = staticPointerCast(handle.$$.ptr, fromRawType, this.pointeeType.rawType); if (this.isSmartPointer) { - // todo: if ptr == handle.ptr, there's no need to allocate a new smartPtr! + // todo: if ptr == handle.$$.ptr, there's no need to allocate a new smartPtr! var smartPtr = _malloc(16); - handle.pointeeType.smartPointerType.rawConstructor(smartPtr, ptr, handle.smartPointer); + handle.$$.pointeeType.smartPointerType.rawConstructor(smartPtr, ptr, handle.$$.smartPtr); ptr = smartPtr; - destructors.push(handle.pointeeType.smartPointerType.rawDestructor); + destructors.push(handle.$$.pointeeType.smartPointerType.rawDestructor); destructors.push(ptr); } return ptr; @@ -633,7 +633,7 @@ RegisteredPointer.prototype.fromWireTypeAutoDowncast = function(ptr) { // ptr is } else { handle = toType.fromWireType(ptr); } - handle.ptr = staticPointerCast(handle.ptr, fromType.rawType, toType.rawType); + handle.$$.ptr = staticPointerCast(handle.$$.ptr, fromType.rawType, toType.rawType); } else { handle = this.fromWireType(ptr); } @@ -655,10 +655,11 @@ function __embind_register_smart_ptr( rawGetPointee = FUNCTION_TABLE[rawGetPointee]; var Handle = createNamedFunction(name, function(ptr) { - this.count = {value: 1}; - this.smartPointer = ptr; // std::shared_ptr* - this.ptr = rawGetPointee(ptr); // T* - this.pointeeType = pointeeType; + this.$$ = {}; + this.$$.count = {value: 1}; + this.$$.smartPtr = ptr; // std::shared_ptr* + this.$$.ptr = rawGetPointee(ptr); // T* + this.$$.pointeeType = pointeeType; }); // TODO: test for SmartPtr.prototype.constructor property? @@ -666,30 +667,31 @@ function __embind_register_smart_ptr( Handle.prototype = Object.create(pointeeType.Handle.prototype); Handle.prototype.clone = function() { - if (!this.ptr) { + if (!this.$$.ptr) { throw new BindingError(pointeeType.name + ' instance already deleted'); } var clone = Object.create(Handle.prototype); - clone.count = this.count; - clone.smartPointer = this.smartPointer; - clone.ptr = this.ptr; + clone.$$ = {}; + clone.$$.count = this.$$.count; + clone.$$.smartPtr = this.$$.smartPtr; + clone.$$.ptr = this.$$.ptr; - clone.count.value += 1; + clone.$$.count.value += 1; return clone; }; Handle.prototype['delete'] = function() { - if (!this.ptr) { + if (!this.$$.ptr) { throw new BindingError(pointeeType.name + ' instance already deleted'); } - this.count.value -= 1; - if (0 === this.count.value) { - rawDestructor(this.smartPointer); + this.$$.count.value -= 1; + if (0 === this.$$.count.value) { + rawDestructor(this.$$.smartPtr); } - this.smartPointer = undefined; - this.ptr = undefined; + this.$$.smartPtr = undefined; + this.$$.ptr = undefined; }; var registeredPointer = new RegisteredPointer(Handle, pointeeType.isPolymorphic, true, rawGetPointee, rawConstructor, rawDestructor); registeredPointer.pointeeType = pointeeType; @@ -716,10 +718,10 @@ function __embind_register_class( throw new BindingError(name + ' does not define call operator'); } }; - - h.count = {value: 1, ptr: ptr }; - h.ptr = ptr; - h.pointeeType = type; // set below + h.$$ = {}; + h.$$.count = {value: 1, ptr: ptr }; + h.$$.ptr = ptr; + h.$$.pointeeType = type; // set below for(var prop in Handle.prototype) { var dp = Object.getOwnPropertyDescriptor(Handle.prototype, prop); @@ -730,36 +732,31 @@ function __embind_register_class( }); Handle.prototype.clone = function() { - if (!this.ptr) { + if (!this.$$.ptr) { throw new BindingError(type.name + ' instance already deleted'); } var clone = Object.create(Handle.prototype); - clone.count = this.count; - clone.ptr = this.ptr; + clone.$$ = {}; + clone.$$.count = this.$$.count; + clone.$$.ptr = this.$$.ptr; - clone.count.value += 1; + clone.$$.count.value += 1; return clone; }; - Handle.prototype.move = function() { - var rv = this.clone(); - this.delete(); - 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 + // todo: then replace this.$$.count.ptr below with this.$$.ptr and make sure it fails Handle.prototype['delete'] = function() { - if (!this.ptr) { + if (!this.$$.ptr) { throw new BindingError(type.name + ' instance already deleted'); // todo: but 'type' hasn't been resolved!?! } - this.count.value -= 1; - if (0 === this.count.value) { - rawDestructor(this.count.ptr); + this.$$.count.value -= 1; + if (0 === this.$$.count.value) { + rawDestructor(this.$$.count.ptr); } - this.ptr = undefined; + this.$$.ptr = undefined; }; Handle.memberType = {}; @@ -834,8 +831,7 @@ function __embind_register_class_method( var humanName = classType.name + '.' + methodName; var argTypes = requireArgumentTypes(rawArgTypes, 'method ' + humanName); classType.Handle.prototype[methodName] = function() { - - if (!this.ptr) { + if (!this.$$.ptr) { throw new BindingError('cannot call emscripten binding method ' + humanName + ' on deleted object'); } if (arguments.length !== argCount - 1) { @@ -844,7 +840,7 @@ function __embind_register_class_method( var destructors = []; var args = new Array(argCount + 1); - args[0] = this.ptr; + args[0] = this.$$.ptr; args[1] = memberFunction; for (var i = 1; i < argCount; ++i) { args[i + 1] = argTypes[i].toWireType(destructors, arguments[i-1]); @@ -895,7 +891,7 @@ function __embind_register_class_operator_call( var argTypes = requireArgumentTypes(rawArgTypes, 'method ' + humanName); classType.Handle.prototype.operator_call = function() { - if (!this.ptr) { + if (!this.$$.ptr) { throw new BindingError('cannot call emscripten binding method ' + humanName + ' on deleted object'); } if (arguments.length !== argCount - 1) { @@ -904,7 +900,7 @@ function __embind_register_class_operator_call( var destructors = []; var args = new Array(argCount); - args[0] = this.ptr; + args[0] = this.$$.ptr; for (var i = 1; i < argCount; ++i) { args[i] = argTypes[i].toWireType(destructors, arguments[i-1]); } @@ -929,7 +925,7 @@ function __embind_register_class_operator_array_get( elementType = requireRegisteredType(elementType, 'array access element' + classType.name); var humanName = classType.name + '.' + 'operator_array_get'; classType.Handle.prototype.array_get = function() { - if (!this.ptr) { + if (!this.$$.ptr) { throw new BindingError('cannot call emscripten binding method ' + humanName + ' on deleted object'); } @@ -939,7 +935,7 @@ function __embind_register_class_operator_array_get( var destructors = []; var args = new Array(2); - args[0] = this.ptr; + args[0] = this.$$.ptr; args[1] = indexType.toWireType(destructors, arguments[0]); var rv = elementType.fromWireType(rawInvoker.apply(null, args)); @@ -962,7 +958,7 @@ function __embind_register_class_operator_array_set( elementType = requireRegisteredType(elementType, 'array access element ' + classType.name); var humanName = classType.name + '.' + 'operator_array_get'; classType.Handle.prototype.array_set = function() { - if (!this.ptr) { + if (!this.$$.ptr) { throw new BindingError('cannot call emscripten binding method ' + humanName + ' on deleted object'); } @@ -972,7 +968,7 @@ function __embind_register_class_operator_array_set( var destructors = []; var args = new Array(2); - args[0] = this.ptr; + args[0] = this.$$.ptr; args[1] = indexType.toWireType(destructors, arguments[0]); args[2] = elementType.toWireType(destructors, arguments[1]); @@ -1002,17 +998,17 @@ function __embind_register_class_field( var fieldType = requireRegisteredType(rawFieldType, 'field ' + humanName); Object.defineProperty(classType.Handle.prototype, fieldName, { get: function() { - if (!this.ptr) { + if (!this.$$.ptr) { throw new BindingError('cannot access emscripten binding field ' + humanName + ' on deleted object'); } - return fieldType.fromWireType(getter(this.ptr, memberPointer)); + return fieldType.fromWireType(getter(this.$$.ptr, memberPointer)); }, set: function(v) { - if (!this.ptr) { + 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)); + setter(this.$$.ptr, memberPointer, fieldType.toWireType(destructors, v)); runDestructors(destructors); }, enumerable: true From 88e98068137d1b176415fa7e7419ad42e93a6fa1 Mon Sep 17 00:00:00 2001 From: Bill Welden Date: Tue, 19 Feb 2013 13:20:41 -0800 Subject: [PATCH 122/258] Change register_smart_ptr to smart_ptr. --- system/include/emscripten/bind.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index a807be4cb..fd949234f 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -579,9 +579,9 @@ namespace emscripten { //////////////////////////////////////////////////////////////////////////////// template - class register_smart_ptr { + class smart_ptr { public: - register_smart_ptr(const char* name) { + smart_ptr(const char* name) { using namespace internal; typedef typename PointerType::element_type PointeeType; From 7d1614a3ee806c5635d4c4d351611fba895fe02e Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Fri, 22 Feb 2013 13:33:28 -0800 Subject: [PATCH 123/258] simplify embind a bit and checkpoint progress towards external constructors --- src/embind/embind.js | 10 +++---- system/include/emscripten/bind.h | 50 +++++++++++++++++--------------- 2 files changed, 32 insertions(+), 28 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index b0eef51f4..b823e281c 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -332,7 +332,7 @@ function makeInvoker(name, argCount, argTypes, invoker, fn) { var args = new Array(argCount); args[0] = fn; for (var i = 1; i < argCount; ++i) { - args[i] = argTypes[i].toWireType(destructors, arguments[i-1]); + args[i] = argTypes[i].toWireType(destructors, arguments[i - 1]); } var rv = invoker.apply(null, args); if (argTypes[0].fromWireTypeAutoDowncast) { @@ -800,9 +800,9 @@ function __embind_register_class_constructor( throw new BindingError('emscripten binding ' + humanName + ' called with ' + arguments.length + ' arguments, expected ' + (argCount-1)); } var destructors = []; - var args = new Array(argCount-1); + var args = new Array(argCount - 1); for (var i = 1; i < argCount; ++i) { - args[i-1] = argTypes[i].toWireType(destructors, arguments[i-1]); + args[i - 1] = argTypes[i].toWireType(destructors, arguments[i - 1]); } var ptr = rawConstructor.apply(null, args); @@ -843,7 +843,7 @@ function __embind_register_class_method( args[0] = this.$$.ptr; args[1] = memberFunction; for (var i = 1; i < argCount; ++i) { - args[i + 1] = argTypes[i].toWireType(destructors, arguments[i-1]); + args[i + 1] = argTypes[i].toWireType(destructors, arguments[i - 1]); } var rv = rawInvoker.apply(null, args); if (argTypes[0].fromWireTypeAutoDowncast) { @@ -902,7 +902,7 @@ function __embind_register_class_operator_call( var args = new Array(argCount); args[0] = this.$$.ptr; for (var i = 1; i < argCount; ++i) { - args[i] = argTypes[i].toWireType(destructors, arguments[i-1]); + args[i] = argTypes[i].toWireType(destructors, arguments[i - 1]); } var rv = argTypes[0].fromWireType(rawInvoker.apply(null, args)); diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index fd949234f..f4fe37afe 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -579,22 +579,18 @@ namespace emscripten { //////////////////////////////////////////////////////////////////////////////// template - class smart_ptr { - public: - smart_ptr(const char* name) { - using namespace internal; - typedef typename PointerType::element_type PointeeType; - - registerStandardTypes(); - _embind_register_smart_ptr( - TypeID::get(), - TypeID::get(), - name, - reinterpret_cast(&raw_smart_pointer_constructor), - reinterpret_cast(&raw_destructor), - reinterpret_cast(&get_pointee)); - - } + void smart_ptr(const char* name) { + using namespace internal; + typedef typename PointerType::element_type PointeeType; + + registerStandardTypes(); + _embind_register_smart_ptr( + TypeID::get(), + TypeID::get(), + name, + reinterpret_cast(&raw_smart_pointer_constructor), + reinterpret_cast(&raw_destructor), + reinterpret_cast(&get_pointee)); }; //////////////////////////////////////////////////////////////////////////////// @@ -630,13 +626,6 @@ namespace emscripten { } }; - /* - void assertInitialized() { - if (!jsobj) { - internal::_embind_fatal_error( - "Cannot invoke call on uninitialized Javascript interface wrapper.", "JSInterface"); - } - }*/ val wrapped; }; @@ -673,6 +662,21 @@ namespace emscripten { return *this; } + template + class_& constructor(SmartPtr (*factory)(Args...)) { + using namespace internal; + + smart_ptr("SmartPtr"); + + typename WithPolicies<>::template ArgTypeList args; + _embind_register_class_smart_ptr_constructor( + TypeID::get(), + args.count, + args.types, + reinterpret_cast(&raw_smart_ptr_constructor + return *this; + } + template class_& allow_subclass() { using namespace internal; From 68845c447da1f53441e0399279c6cb99535d3076 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Fri, 22 Feb 2013 13:58:30 -0800 Subject: [PATCH 124/258] fix a syntax error --- system/include/emscripten/bind.h | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index f4fe37afe..1ef462bc4 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -291,9 +291,6 @@ namespace emscripten { ); } -// template -// void nullDeallocator(PointerType* p) {} - template typename std::shared_ptr raw_smart_pointer_constructor(PointerType *ptr, std::shared_ptr basePtr, void (PointerType*)) { return std::shared_ptr(basePtr, ptr); @@ -668,12 +665,14 @@ namespace emscripten { smart_ptr("SmartPtr"); - typename WithPolicies<>::template ArgTypeList args; + typename WithPolicies<>::template ArgTypeList args; + /* _embind_register_class_smart_ptr_constructor( TypeID::get(), args.count, args.types, reinterpret_cast(&raw_smart_ptr_constructor + */ return *this; } From 04d596f0408ccd7b591cf323bf2602b545155dcc Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Mon, 25 Feb 2013 16:04:14 -0800 Subject: [PATCH 125/258] Replace operator call with a method invoking said operator call, so isinstance works again. When Function.create exists we can have the benefit of both worlds. --- src/embind/embind.js | 57 +++----------------------------- system/include/emscripten/bind.h | 18 ++-------- 2 files changed, 6 insertions(+), 69 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index b823e281c..21eca35e9 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -711,24 +711,10 @@ function __embind_register_class( rawDestructor = FUNCTION_TABLE[rawDestructor]; var Handle = createNamedFunction(name, function(ptr) { - var h = function() { - if(h.operator_call !== undefined) { - return h.operator_call.apply(h, arguments); - } else { - throw new BindingError(name + ' does not define call operator'); - } - }; - h.$$ = {}; - h.$$.count = {value: 1, ptr: ptr }; - h.$$.ptr = ptr; - h.$$.pointeeType = type; // set below - - for(var prop in Handle.prototype) { - var dp = Object.getOwnPropertyDescriptor(Handle.prototype, prop); - Object.defineProperty(h, prop, dp); - } - - return h; + this.$$ = {}; + this.$$.count = {value: 1, ptr: ptr }; + this.$$.ptr = ptr; + this.$$.pointeeType = type; // set below }); Handle.prototype.clone = function() { @@ -877,41 +863,6 @@ function __embind_register_class_classmethod( }); } -function __embind_register_class_operator_call( - rawClassType, - argCount, - rawArgTypesAddr, - rawInvoker -) { - var rawArgTypes = heap32VectorToArray(argCount, rawArgTypesAddr); - rawInvoker = FUNCTION_TABLE[rawInvoker]; - requestDeferredRegistration(function() { - var classType = requireRegisteredType(rawClassType, 'class'); - var humanName = classType.name + '.' + 'operator_call'; - var argTypes = requireArgumentTypes(rawArgTypes, 'method ' + humanName); - - classType.Handle.prototype.operator_call = function() { - if (!this.$$.ptr) { - throw new BindingError('cannot call emscripten binding method ' + humanName + ' on deleted object'); - } - if (arguments.length !== argCount - 1) { - throw new BindingError('emscripten binding method ' + humanName + ' called with ' + arguments.length + ' arguments, expected ' + (argCount-1)); - } - - var destructors = []; - var args = new Array(argCount); - args[0] = this.$$.ptr; - for (var i = 1; i < argCount; ++i) { - args[i] = argTypes[i].toWireType(destructors, arguments[i - 1]); - } - - var rv = argTypes[0].fromWireType(rawInvoker.apply(null, args)); - runDestructors(destructors); - return rv; - }; - }); -} - function __embind_register_class_operator_array_get( rawClassType, elementType, diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index 1ef462bc4..5806d57f0 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -141,12 +141,6 @@ namespace emscripten { GenericFunction invoker, GenericFunction method); - void _embind_register_class_operator_call( - TYPEID classType, - unsigned argCount, - TYPEID argTypes[], - GenericFunction invoker); - void _embind_register_class_operator_array_get( TYPEID classType, TYPEID elementType, @@ -754,16 +748,8 @@ namespace emscripten { } template - class_& calloperator(Policies...) { - using namespace internal; - - typename WithPolicies::template ArgTypeList args; - _embind_register_class_operator_call( - TypeID::get(), - args.count, - args.types, - reinterpret_cast(&internal::FunctorInvoker::invoke)); - return *this; + class_& calloperator(const char* methodName, Policies... policies) { + return method(methodName, &ClassType::operator(), policies...); } template From 176000557d9d25d6eb14511413e23321ea94e0aa Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Mon, 25 Feb 2013 18:04:45 -0800 Subject: [PATCH 126/258] allow non-member functions bound as methods. --- system/include/emscripten/bind.h | 42 ++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index 5806d57f0..c83512c7b 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -348,6 +348,32 @@ namespace emscripten { } }; + template + struct FunctionInvoker { + typedef ReturnType (FunctionPointer)(ClassType& ct, Args...); + static typename internal::BindingType::WireType invoke( + ClassType* ptr, + FunctionPointer** function, + typename internal::BindingType::WireType... args + ) { + return internal::BindingType::toWireType( + (*function)(*ptr, internal::BindingType::toWireType(args)...) + ); + } + }; + + template + struct FunctionInvoker { + typedef void (FunctionPointer)(ClassType& ct, Args...); + static void invoke( + ClassType* ptr, + FunctionPointer** function, + typename internal::BindingType::WireType... args + ) { + (*function)(*ptr, internal::BindingType::toWireType(args)...); + } + }; + template struct MethodInvoker { typedef ReturnType (ClassType::*MemberPointer)(Args...); @@ -717,6 +743,22 @@ namespace emscripten { return *this; } + template + class_& method(const char* methodName, ReturnType (*function)(ClassType& ptr, Args...), Policies...) { + using namespace internal; + + typename WithPolicies::template ArgTypeList args; + _embind_register_class_method( + TypeID::get(), + methodName, + args.count, + args.types, + reinterpret_cast(&FunctionInvoker::invoke), + sizeof(function), + &function); + return *this; + } + template class_& field(const char* fieldName, FieldType ClassType::*field) { using namespace internal; From 07cd277b4df9308db695d8a96ab570b4205b9622 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Mon, 25 Feb 2013 18:29:41 -0800 Subject: [PATCH 127/258] Use standard method machinery for array access. --- src/embind/embind.js | 67 ----------------------------- system/include/emscripten/bind.h | 72 +++++++++----------------------- 2 files changed, 19 insertions(+), 120 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 21eca35e9..c7848d9c0 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -863,73 +863,6 @@ function __embind_register_class_classmethod( }); } -function __embind_register_class_operator_array_get( - rawClassType, - elementType, - indexType, - rawInvoker -) { - rawInvoker = FUNCTION_TABLE[rawInvoker]; - requestDeferredRegistration(function() { - var classType = requireRegisteredType(rawClassType, 'class'); - indexType = requireRegisteredType(indexType, 'array access index ' + classType.name); - elementType = requireRegisteredType(elementType, 'array access element' + classType.name); - var humanName = classType.name + '.' + 'operator_array_get'; - classType.Handle.prototype.array_get = function() { - if (!this.$$.ptr) { - throw new BindingError('cannot call emscripten binding method ' + humanName + ' on deleted object'); - } - - if (arguments.length !== 1) { - throw new BindingError('emscripten binding method ' + humanName + ' called with ' + arguments.length + ' arguments, expected ' + 1); - } - - var destructors = []; - var args = new Array(2); - args[0] = this.$$.ptr; - args[1] = indexType.toWireType(destructors, arguments[0]); - - var rv = elementType.fromWireType(rawInvoker.apply(null, args)); - runDestructors(destructors); - return rv; - }; - }); -} - -function __embind_register_class_operator_array_set( - rawClassType, - elementType, - rawIndexType, - rawInvoker -) { - rawInvoker = FUNCTION_TABLE[rawInvoker]; - requestDeferredRegistration(function() { - var classType = requireRegisteredType(rawClassType, 'class'); - var indexType = requireRegisteredType(rawIndexType, 'array access index ' + classType.name); - elementType = requireRegisteredType(elementType, 'array access element ' + classType.name); - var humanName = classType.name + '.' + 'operator_array_get'; - classType.Handle.prototype.array_set = function() { - if (!this.$$.ptr) { - throw new BindingError('cannot call emscripten binding method ' + humanName + ' on deleted object'); - } - - if (arguments.length !== 2) { - throw new BindingError('emscripten binding method ' + humanName + ' called with ' + arguments.length + ' arguments, expected ' + 2); - } - - var destructors = []; - var args = new Array(2); - args[0] = this.$$.ptr; - args[1] = indexType.toWireType(destructors, arguments[0]); - args[2] = elementType.toWireType(destructors, arguments[1]); - - var rv = elementType.fromWireType(rawInvoker.apply(null, args)); - runDestructors(destructors); - return rv; - }; - }); -} - function __embind_register_class_field( rawClassType, fieldName, diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index c83512c7b..d99dd7638 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -141,18 +141,6 @@ namespace emscripten { GenericFunction invoker, GenericFunction method); - void _embind_register_class_operator_array_get( - TYPEID classType, - TYPEID elementType, - TYPEID indexType, - GenericFunction invoker); - - void _embind_register_class_operator_array_set( - TYPEID classType, - TYPEID elementType, - TYPEID indexType, - GenericFunction invoker); - void _embind_register_enum( TYPEID enumType, const char* name); @@ -325,29 +313,6 @@ namespace emscripten { } }; - template - struct ArrayAccessGetInvoker { - static typename internal::BindingType::WireType invoke( - ClassType* ptr, - typename internal::BindingType::WireType index - ) { - return internal::BindingType::toWireType( - (*ptr)[internal::BindingType::fromWireType(index)] - ); - } - }; - - template - struct ArrayAccessSetInvoker { - static void invoke( - ClassType* ptr, - typename internal::BindingType::WireType index, - typename internal::BindingType::WireType item - ) { - (*ptr)[internal::BindingType::fromWireType(index)] = internal::BindingType::fromWireType(item); - } - }; - template struct FunctionInvoker { typedef ReturnType (FunctionPointer)(ClassType& ct, Args...); @@ -357,7 +322,7 @@ namespace emscripten { typename internal::BindingType::WireType... args ) { return internal::BindingType::toWireType( - (*function)(*ptr, internal::BindingType::toWireType(args)...) + (*function)(*ptr, internal::BindingType::fromWireType(args)...) ); } }; @@ -370,7 +335,7 @@ namespace emscripten { FunctionPointer** function, typename internal::BindingType::WireType... args ) { - (*function)(*ptr, internal::BindingType::toWireType(args)...); + (*function)(*ptr, internal::BindingType::fromWireType(args)...); } }; @@ -472,6 +437,17 @@ namespace emscripten { setter(ptr, FieldBinding::fromWireType(value)); } }; + + template + struct ArrayAccess { + static ElementType get(ClassType& ptr, IndexType index) { + return ptr[index]; + } + + static void set(ClassType& ptr, IndexType index, const ElementType& value) { + ptr[index] = value; + } + }; } //////////////////////////////////////////////////////////////////////////////// @@ -796,26 +772,16 @@ namespace emscripten { template class_& arrayoperatorget() { - using namespace internal; - - _embind_register_class_operator_array_get( - TypeID::get(), - TypeID::get(), - TypeID::get(), - reinterpret_cast(&internal::ArrayAccessGetInvoker::invoke)); - return *this; + return method( + "array_get", + internal::ArrayAccess::get); } template class_& arrayoperatorset() { - using namespace internal; - - _embind_register_class_operator_array_set( - TypeID::get(), - TypeID::get(), - TypeID::get(), - reinterpret_cast(&internal::ArrayAccessSetInvoker::invoke)); - return *this; + return method( + "array_set", + internal::ArrayAccess::set); } }; From 78548810ba0ec6386fc4c7ab1342fe55321dad40 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Mon, 25 Feb 2013 18:58:11 -0800 Subject: [PATCH 128/258] Allow const non-member functions --- system/include/emscripten/bind.h | 43 +++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index d99dd7638..33e1cc43a 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -440,7 +440,7 @@ namespace emscripten { template struct ArrayAccess { - static ElementType get(ClassType& ptr, IndexType index) { + static ElementType get(const ClassType& ptr, IndexType index) { return ptr[index]; } @@ -448,6 +448,18 @@ namespace emscripten { ptr[index] = value; } }; + + template + struct MapAccess { + static ValueType get(const std::map& m, const KeyType& k) { + auto i = m.find(k); + if (i == m.end()) { + return ValueType(); + } else { + return i->second; + } + } + }; } //////////////////////////////////////////////////////////////////////////////// @@ -735,6 +747,22 @@ namespace emscripten { return *this; } + template + class_& method(const char* methodName, ReturnType (*function)(const ClassType& ptr, Args...), Policies...) { + using namespace internal; + + typename WithPolicies::template ArgTypeList args; + _embind_register_class_method( + TypeID::get(), + methodName, + args.count, + args.types, + reinterpret_cast(&FunctionInvoker::invoke), + sizeof(function), + &function); + return *this; + } + template class_& field(const char* fieldName, FieldType ClassType::*field) { using namespace internal; @@ -795,7 +823,7 @@ namespace emscripten { void (VecType::*push_back)(const T&) = &VecType::push_back; const T& (VecType::*at)(size_t) const = &VecType::at; - auto c = class_>(name) + return class_>(name) .template constructor<>() .method("push_back", push_back) .method("at", at) @@ -803,8 +831,6 @@ namespace emscripten { .template arrayoperatorget() .template arrayoperatorset() ; - - return c; } //////////////////////////////////////////////////////////////////////////////// @@ -815,13 +841,12 @@ namespace emscripten { using namespace std; typedef map MapType; - auto c = class_(name) + return class_(name) .method("size", &MapType::size) - .template arrayoperatorget() + // make this map_get? + .method("array_get", internal::MapAccess::get) .template arrayoperatorset() - ; - - return c; + ; } From f7b5f283e123ef2cf892c9e5d8a97cbd9e68349c Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Mon, 25 Feb 2013 23:50:32 -0800 Subject: [PATCH 129/258] Use length-prefix strings instead of null-terminated strings to support passing strings with embedded nul characters. --- src/embind/embind.js | 15 +++++++++++---- system/include/emscripten/wire.h | 14 ++++++++++---- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index c7848d9c0..c164b3263 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -270,17 +270,24 @@ function RegisteredString(stringType, name) { } RegisteredString.prototype.toWireType = function(destructors, value) { - var ptr = _malloc(value.length + 1); - writeStringToMemory(value, ptr); + // assumes 4-byte alignment + var length = value.length; + var ptr = _malloc(4 + length); + HEAP32[ptr >> 2] = length; + writeStringToMemory(value, ptr + 4); destructors.push(_free); destructors.push(ptr); return ptr; }; RegisteredString.prototype.fromWireType = function(value) { - var rv = Pointer_stringify(value); + var length = HEAP32[value >> 2]; + var a = new Array(length); + for (var i = 0; i < length; ++i) { + a[i] = String.fromCharCode(HEAP8[value + 4 + i]); + } _free(value); - return rv; + return a.join(''); }; function __embind_register_cstring(rawType, name) { diff --git a/system/include/emscripten/wire.h b/system/include/emscripten/wire.h index dc612e11d..10484a61e 100755 --- a/system/include/emscripten/wire.h +++ b/system/include/emscripten/wire.h @@ -180,12 +180,18 @@ namespace emscripten { template<> struct BindingType { - typedef char* WireType; + typedef struct { + size_t length; + char data[1]; // trailing data + }* WireType; static WireType toWireType(const std::string& v) { - return strdup(v.c_str()); + WireType wt = (WireType)malloc(sizeof(size_t) + v.length()); + wt->length = v.length(); + memcpy(wt->data, v.data(), v.length()); + return wt; } - static std::string fromWireType(char* v) { - return std::string(v); + static std::string fromWireType(WireType v) { + return std::string(v->data, v->length); } static void destroy(WireType v) { free(v); From 5d119a9acbfd75413bb81fd87d858c9d4d1d6f0e Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Mon, 25 Feb 2013 23:55:26 -0800 Subject: [PATCH 130/258] jshint fix --- src/embind/embind.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index c164b3263..29fb80bf0 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -1,7 +1,7 @@ /*global Module*/ /*global Module*/ /*global _malloc, _free, _memcpy*/ -/*global FUNCTION_TABLE, HEAP32*/ +/*global FUNCTION_TABLE, HEAP32, HEAP8*/ /*global Pointer_stringify, writeStringToMemory*/ /*global __emval_register, _emval_handle_array, __emval_decref*/ /*global ___getDynamicPointerType: false*/ From 6a2a01fe0ec1a297c5a45e21a6107e592986e762 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Tue, 26 Feb 2013 00:57:45 -0800 Subject: [PATCH 131/258] We were misusing writeStringToMemory which could cause memory corruption. --- src/embind/embind.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 29fb80bf0..91812697d 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -2,7 +2,7 @@ /*global Module*/ /*global _malloc, _free, _memcpy*/ /*global FUNCTION_TABLE, HEAP32, HEAP8*/ -/*global Pointer_stringify, writeStringToMemory*/ +/*global Pointer_stringify*/ /*global __emval_register, _emval_handle_array, __emval_decref*/ /*global ___getDynamicPointerType: false*/ /*global ___typeName:false*/ @@ -274,7 +274,9 @@ RegisteredString.prototype.toWireType = function(destructors, value) { var length = value.length; var ptr = _malloc(4 + length); HEAP32[ptr >> 2] = length; - writeStringToMemory(value, ptr + 4); + for (var i = 0; i < length; ++i) { + HEAPU8[ptr + 4 + i] = value.charCodeAt(i); + } destructors.push(_free); destructors.push(ptr); return ptr; From c776c8831f313adb61c1d0e749dc86fbddb9c632 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Tue, 26 Feb 2013 01:00:03 -0800 Subject: [PATCH 132/258] fix jshint --- src/embind/embind.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 91812697d..71da1108d 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -1,7 +1,7 @@ /*global Module*/ /*global Module*/ /*global _malloc, _free, _memcpy*/ -/*global FUNCTION_TABLE, HEAP32, HEAP8*/ +/*global FUNCTION_TABLE, HEAP32, HEAPU8*/ /*global Pointer_stringify*/ /*global __emval_register, _emval_handle_array, __emval_decref*/ /*global ___getDynamicPointerType: false*/ @@ -286,7 +286,7 @@ RegisteredString.prototype.fromWireType = function(value) { var length = HEAP32[value >> 2]; var a = new Array(length); for (var i = 0; i < length; ++i) { - a[i] = String.fromCharCode(HEAP8[value + 4 + i]); + a[i] = String.fromCharCode(HEAPU8[value + 4 + i]); } _free(value); return a.join(''); From d53d8001aaa3666eb6de40fec25dbec338a75c81 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Tue, 26 Feb 2013 01:20:43 -0800 Subject: [PATCH 133/258] Disallow direct embind constructor calls. --- src/embind/embind.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/embind/embind.js b/src/embind/embind.js index 71da1108d..742dbb942 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -769,6 +769,9 @@ function __embind_register_class( registeredClass.pointeeType = type; type.constructor = createNamedFunction(type.name, function() { + if (Object.getPrototypeOf(this) !== Handle.prototype) { + throw new BindingError("Use 'new' to construct " + name); + } var body = type.constructor.body; return body.apply(this, arguments); }); From 136c2c4457c87d5dbe9477b28b84362dd5a48b64 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Tue, 26 Feb 2013 11:41:19 -0800 Subject: [PATCH 134/258] Give a sensible error message if attempting to construct a class with no accessible constructor --- src/embind/embind.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/embind/embind.js b/src/embind/embind.js index 742dbb942..5a49d1778 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -773,6 +773,9 @@ function __embind_register_class( throw new BindingError("Use 'new' to construct " + name); } var body = type.constructor.body; + if (undefined === body) { + throw new BindingError(name + " has no accessible constructor"); + } return body.apply(this, arguments); }); type.constructor.prototype = type.Handle.prototype; From 8e471a05c1d2e00a3d367cff36cb3788b9681491 Mon Sep 17 00:00:00 2001 From: Bill Welden Date: Tue, 26 Feb 2013 08:53:03 -0800 Subject: [PATCH 135/258] Fixed a problem where auto downcasting from a smart pointer to a derived class for which no smart pointer was registered would cause a crash. Now a smart pointer is only downcast to the most derived class which is bound and for which a smart pointer is registered. --- src/embind/embind.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 5a49d1778..277515a5c 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -619,7 +619,7 @@ RegisteredPointer.prototype.getDynamicDowncastType = function(ptr) { var derivation = Module.__getDerivationPath(type, this.pointeeType.rawType); for (var i = 0; i < derivation.size(); i++) { downcastType = typeRegistry[derivation.at(i)]; - if (downcastType) { + if (downcastType && (!this.isSmartPointer || downcastType.smartPointerType)) { break; } } From 4185573aca1eeb4d088a227acbb995253e8f5b8c Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Wed, 27 Feb 2013 00:01:10 -0800 Subject: [PATCH 136/258] Support external raw pointer constructors. --- src/embind/embind.js | 12 ++++++++---- system/include/emscripten/bind.h | 19 ++++++++++++++----- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 277515a5c..5a0699bad 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -788,10 +788,13 @@ function __embind_register_class_constructor( rawClassType, argCount, rawArgTypesAddr, + invoker, rawConstructor ) { var rawArgTypes = heap32VectorToArray(argCount, rawArgTypesAddr); - rawConstructor = FUNCTION_TABLE[rawConstructor]; + invoker = FUNCTION_TABLE[invoker]; + rawConstructor = rawConstructor; + requestDeferredRegistration(function() { var classType = requireRegisteredType(rawClassType, 'class'); var humanName = 'constructor ' + classType.name; @@ -801,12 +804,13 @@ function __embind_register_class_constructor( throw new BindingError('emscripten binding ' + humanName + ' called with ' + arguments.length + ' arguments, expected ' + (argCount-1)); } var destructors = []; - var args = new Array(argCount - 1); + var args = new Array(argCount); + args[0] = rawConstructor; for (var i = 1; i < argCount; ++i) { - args[i - 1] = argTypes[i].toWireType(destructors, arguments[i - 1]); + args[i] = argTypes[i].toWireType(destructors, arguments[i - 1]); } - var ptr = rawConstructor.apply(null, args); + var ptr = invoker.apply(null, args); runDestructors(destructors); return classType.Handle.call(this, ptr); diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index 33e1cc43a..257c73344 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -113,6 +113,7 @@ namespace emscripten { TYPEID classType, unsigned argCount, TYPEID argTypes[], + GenericFunction invoker, GenericFunction constructor); void _embind_register_class_method( @@ -211,7 +212,7 @@ namespace emscripten { template struct Invoker { static typename internal::BindingType::WireType invoke( - ReturnType (fn)(Args...), + ReturnType (*fn)(Args...), typename internal::BindingType::WireType... args ) { return internal::BindingType::toWireType( @@ -225,7 +226,7 @@ namespace emscripten { template struct Invoker { static void invoke( - void (fn)(Args...), + void (*fn)(Args...), typename internal::BindingType::WireType... args ) { return fn( @@ -655,15 +656,23 @@ namespace emscripten { } template - class_& constructor(Policies...) { + class_& constructor(Policies... policies) { + return constructor( + &internal::operator_new, + policies...); + } + + template + class_& constructor(ClassType* (*factory)(Args...), Policies...) { using namespace internal; - typename WithPolicies::template ArgTypeList args; + typename WithPolicies::template ArgTypeList args; _embind_register_class_constructor( TypeID::get(), args.count, args.types, - reinterpret_cast(&raw_constructor)); + reinterpret_cast(&Invoker::invoke), + reinterpret_cast(factory)); return *this; } From d0ab8be8b2ef3bb0016ae7d5f50518407b515d2d Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Wed, 27 Feb 2013 17:05:03 -0800 Subject: [PATCH 137/258] Support autodowncasting when passing pointers into val --- src/embind/emval.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/embind/emval.js b/src/embind/emval.js index bf31bdf3e..b1c9ed049 100755 --- a/src/embind/emval.js +++ b/src/embind/emval.js @@ -69,7 +69,7 @@ function __emval_new_cstring(v) { function __emval_take_value(type, v) { type = requireRegisteredType(type, '_emval_take_value'); - v = type.fromWireType(v); + v = type.fromWireTypeAutoDowncast ? type.fromWireTypeAutoDowncast(v) : type.fromWireType(v); return __emval_register(v); } From 55ebc1f6697261ef6e6e4920a28d41db783dba8c Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Tue, 26 Feb 2013 17:52:47 -0800 Subject: [PATCH 138/258] Rework how vectors and maps are bound: add bounds checking and return undefined if out of bounds. --- src/embind/embind.js | 6 +- system/include/emscripten/bind.h | 140 ++++++++++++++----------------- 2 files changed, 67 insertions(+), 79 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 5a0699bad..38720db36 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -85,11 +85,11 @@ function collectRegisteredBaseClasses(rawType) { var rawBaseTypes = Module.__getBaseClasses(rawType); var baseTypes = []; for (var i = 0; i < rawBaseTypes.size(); i++) { - var baseType = typeRegistry[rawBaseTypes.at(i)]; + var baseType = typeRegistry[rawBaseTypes.get(i)]; if (baseType) { baseTypes.push(baseType); } else { - baseTypes = baseTypes.concat(collectRegisteredBaseClasses(rawBaseTypes.at(i))); + baseTypes = baseTypes.concat(collectRegisteredBaseClasses(rawBaseTypes.get(i))); } } return baseTypes; @@ -618,7 +618,7 @@ RegisteredPointer.prototype.getDynamicDowncastType = function(ptr) { if (type && type !== this.pointeeType.rawType) { var derivation = Module.__getDerivationPath(type, this.pointeeType.rawType); for (var i = 0; i < derivation.size(); i++) { - downcastType = typeRegistry[derivation.at(i)]; + downcastType = typeRegistry[derivation.get(i)]; if (downcastType && (!this.isSmartPointer || downcastType.smartPointerType)) { break; } diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index 257c73344..3ecd858b8 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -290,36 +290,12 @@ namespace emscripten { return ptr.get(); } - template - struct FunctorInvoker { - static typename internal::BindingType::WireType invoke( - const FunctorType& ptr, - typename internal::BindingType::WireType... args - ) { - return internal::BindingType::toWireType( - ptr( - internal::BindingType::fromWireType(args)... - ) - ); - } - }; - - template - struct FunctorInvoker { - static void invoke( - const FunctorType& ptr, - typename internal::BindingType::WireType... args - ) { - ptr(internal::BindingType::fromWireType(args)...); - } - }; - template struct FunctionInvoker { - typedef ReturnType (FunctionPointer)(ClassType& ct, Args...); + typedef ReturnType (*FunctionPointer)(ClassType& ct, Args...); static typename internal::BindingType::WireType invoke( ClassType* ptr, - FunctionPointer** function, + FunctionPointer* function, typename internal::BindingType::WireType... args ) { return internal::BindingType::toWireType( @@ -330,10 +306,10 @@ namespace emscripten { template struct FunctionInvoker { - typedef void (FunctionPointer)(ClassType& ct, Args...); + typedef void (*FunctionPointer)(ClassType& ct, Args...); static void invoke( ClassType* ptr, - FunctionPointer** function, + FunctionPointer* function, typename internal::BindingType::WireType... args ) { (*function)(*ptr, internal::BindingType::fromWireType(args)...); @@ -439,28 +415,6 @@ namespace emscripten { } }; - template - struct ArrayAccess { - static ElementType get(const ClassType& ptr, IndexType index) { - return ptr[index]; - } - - static void set(ClassType& ptr, IndexType index, const ElementType& value) { - ptr[index] = value; - } - }; - - template - struct MapAccess { - static ValueType get(const std::map& m, const KeyType& k) { - auto i = m.find(k); - if (i == m.end()) { - return ValueType(); - } else { - return i->second; - } - } - }; } //////////////////////////////////////////////////////////////////////////////// @@ -806,55 +760,89 @@ namespace emscripten { class_& calloperator(const char* methodName, Policies... policies) { return method(methodName, &ClassType::operator(), policies...); } - - template - class_& arrayoperatorget() { - return method( - "array_get", - internal::ArrayAccess::get); - } - - template - class_& arrayoperatorset() { - return method( - "array_set", - internal::ArrayAccess::set); - } }; //////////////////////////////////////////////////////////////////////////////// // VECTORS //////////////////////////////////////////////////////////////////////////////// + + namespace internal { + template + struct VectorAccess { + static val get( + const VectorType& v, + typename VectorType::size_type index + ) { + if (index < v.size()) { + return val(v[index]); + } else { + return val::undefined(); + } + } + + static bool set( + VectorType& v, + typename VectorType::size_type index, + const typename VectorType::value_type& value + ) { + v[index] = value; + return true; + } + }; + } + template class_> register_vector(const char* name) { - using namespace std; - typedef vector VecType; + typedef std::vector VecType; void (VecType::*push_back)(const T&) = &VecType::push_back; - const T& (VecType::*at)(size_t) const = &VecType::at; return class_>(name) .template constructor<>() .method("push_back", push_back) - .method("at", at) - .method("size", &vector::size) - .template arrayoperatorget() - .template arrayoperatorset() + .method("size", &VecType::size) + .method("get", &internal::VectorAccess::get) + .method("set", &internal::VectorAccess::set) ; } //////////////////////////////////////////////////////////////////////////////// // MAPS //////////////////////////////////////////////////////////////////////////////// + + namespace internal { + template + struct MapAccess { + static val get( + const MapType& m, + const typename MapType::key_type& k + ) { + auto i = m.find(k); + if (i == m.end()) { + return val::undefined(); + } else { + return val(i->second); + } + } + + static void set( + MapType& m, + const typename MapType::key_type& k, + const typename MapType::mapped_type& v + ) { + m[k] = v; + } + }; + } + template class_> register_map(const char* name) { - using namespace std; - typedef map MapType; + typedef std::map MapType; return class_(name) + .template constructor<>() .method("size", &MapType::size) - // make this map_get? - .method("array_get", internal::MapAccess::get) - .template arrayoperatorset() + .method("get", internal::MapAccess::get) + .method("set", internal::MapAccess::set) ; } From 088e1af2f1311e9b6f9d5b73128794449d1b9adb Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Thu, 28 Feb 2013 00:29:10 -0800 Subject: [PATCH 139/258] allow specialization of get_element_type if using a smart pointer with a different mechanism for looking up the pointee type --- system/include/emscripten/bind.h | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index 3ecd858b8..298df4a45 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -538,10 +538,16 @@ namespace emscripten { // SMART POINTERS //////////////////////////////////////////////////////////////////////////////// + // specialize if you have a different pointer type + template + struct get_element_type { + typedef typename PointerType::element_type type; + }; + template void smart_ptr(const char* name) { using namespace internal; - typedef typename PointerType::element_type PointeeType; + typedef typename get_element_type::type PointeeType; registerStandardTypes(); _embind_register_smart_ptr( @@ -647,6 +653,23 @@ namespace emscripten { return *this; } + /* + template + class_& constructor(SmartPtr (*factory)(Args...)) { + using namespace internal; + + smart_ptr("SmartPtr"); + + typename WithPolicies<>::template ArgTypeList args; + _embind_register_class_smart_ptr_constructor( + TypeID::get(), + args.count, + args.types, + reinterpret_cast(&raw_smart_ptr_constructor + return *this; + } + */ + template class_& allow_subclass() { using namespace internal; From 0912a345d87ebaab6e6811135a065aade48bf39a Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Thu, 28 Feb 2013 00:29:47 -0800 Subject: [PATCH 140/258] comment --- src/embind/emval.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/embind/emval.js b/src/embind/emval.js index b1c9ed049..37a4970ec 100755 --- a/src/embind/emval.js +++ b/src/embind/emval.js @@ -77,6 +77,7 @@ function __emval_new(handle) { return __emval_register(new (_emval_handle_array[handle].value)); } +// appease jshint (technically this code uses eval) var global = (function(){return Function;})()('return this')(); function __emval_get_global(name) { From 92db1c8c8dd903f71da8a0c191b8c109322e2f48 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Thu, 28 Feb 2013 00:41:04 -0800 Subject: [PATCH 141/258] Use custom JavaScript exception types in embind and throw a BindingError when trying to pass a non-object to a function that expects an object reference. --- src/embind/embind.js | 85 +++++++++++++++++++++++++++++++------------- 1 file changed, 61 insertions(+), 24 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 38720db36..08ed7ec10 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -1,5 +1,4 @@ /*global Module*/ -/*global Module*/ /*global _malloc, _free, _memcpy*/ /*global FUNCTION_TABLE, HEAP32, HEAPU8*/ /*global Pointer_stringify*/ @@ -8,13 +7,47 @@ /*global ___typeName:false*/ /*global ___staticPointerCast: false*/ +var BindingError = Module.BindingError = extendError(Error, 'BindingError'); +var CastError = Module.CastError = extendError(Error, 'CastError'); + +function throwBindingError(value) { + throw new BindingError(value); +} + function exposePublicSymbol(name, value) { if (Module.hasOwnProperty(name)) { - throw new BindingError("Cannot register public name '" + name + "' twice"); + throwBindingError("Cannot register public name '" + name + "' twice"); } Module[name] = value; } +// from https://github.com/imvu/imvujs/blob/master/src/error.js +function extendError(baseErrorType, errorName) { + var errorClass = createNamedFunction(errorName, function(message) { + this.name = errorName; + this.message = message; + + var stack = (new Error(message)).stack; + if (stack !== undefined) { + this.stack = this.toString() + '\n' + + stack.replace(/^Error(:[^\n]*)?\n/, ''); + } + }); + errorClass.prototype = Object.create(baseErrorType.prototype); + errorClass.prototype.constructor = errorClass; + errorClass.prototype.toString = function() { + if (this.message === undefined) { + return this.name; + } else { + return this.name + ': ' + this.message; + } + }; + + return errorClass; +} + + +// from https://github.com/imvu/imvujs/blob/master/src/function.js function createNamedFunction(name, body) { /*jshint evil:true*/ return new Function( @@ -141,10 +174,10 @@ function resolveBindings() { function registerType(rawType, name, registeredInstance) { if (!rawType) { - throw new BindingError('type "' + name + '" must have a positive integer typeid pointer'); + throwBindingError('type "' + name + '" must have a positive integer typeid pointer'); } if (typeRegistry.hasOwnProperty(rawType)) { - throw new BindingError("Cannot register type '" + name + "' twice"); + throwBindingError("Cannot register type '" + name + "' twice"); } registeredInstance.rawType = rawType; registeredInstance.name = name; @@ -155,7 +188,7 @@ function registerType(rawType, name, registeredInstance) { function requireRegisteredType(rawType, humanName) { var impl = typeRegistry[rawType]; if (undefined === impl) { - throw new BindingError(humanName + " has unknown type " + typeName(rawType)); + throwBindingError(humanName + " has unknown type " + typeName(rawType)); } return impl; } @@ -315,12 +348,6 @@ function __embind_register_emval(rawType, name) { registerType(rawType, name, new RegisteredEmval()); } -var BindingError = Error; -var CastError = Error; -/** @expose */ -Module.BindingError = BindingError; -Module.CastError = CastError; - function runDestructors(destructors) { while (destructors.length) { var ptr = destructors.pop(); @@ -331,11 +358,11 @@ function runDestructors(destructors) { function makeInvoker(name, argCount, argTypes, invoker, fn) { if (!FUNCTION_TABLE[fn]) { - throw new BindingError('function '+name+' is not defined'); + throwBindingError('function '+name+' is not defined'); } return function() { if (arguments.length !== argCount - 1) { - throw new BindingError('function ' + name + ' called with ' + arguments.length + ' arguments, expected ' + (argCount - 1)); + throwBindingError('function ' + name + ' called with ' + arguments.length + ' arguments, expected ' + (argCount - 1)); } var destructors = []; var args = new Array(argCount); @@ -555,8 +582,11 @@ function RegisteredPointer(Handle, isPolymorphic, isSmartPointer, rawGetPointee, RegisteredPointer.prototype.toWireType = function(destructors, handle) { var fromRawType; - if (!handle) { - return null; + if (handle === null) { + return 0; // todo: maybe this should return a zero-initialized smart pointer object + } + if (!(handle instanceof ClassHandle)) { + throwBindingError('Expected pointer or null, got ' + IMVU.repr(handle)); } if (handle.$$.pointeeType.isPolymorphic) { fromRawType = handle.$$.pointeeType.getDynamicRawPointerType(handle.$$.ptr); @@ -569,6 +599,7 @@ RegisteredPointer.prototype.toWireType = function(destructors, handle) { var ptr = staticPointerCast(handle.$$.ptr, fromRawType, this.pointeeType.rawType); if (this.isSmartPointer) { // todo: if ptr == handle.$$.ptr, there's no need to allocate a new smartPtr! + // todo: _malloc(16) is not big enough var smartPtr = _malloc(16); handle.$$.pointeeType.smartPointerType.rawConstructor(smartPtr, ptr, handle.$$.smartPtr); ptr = smartPtr; @@ -677,7 +708,7 @@ function __embind_register_smart_ptr( Handle.prototype.clone = function() { if (!this.$$.ptr) { - throw new BindingError(pointeeType.name + ' instance already deleted'); + throwBindingError(pointeeType.name + ' instance already deleted'); } var clone = Object.create(Handle.prototype); @@ -692,7 +723,7 @@ function __embind_register_smart_ptr( Handle.prototype['delete'] = function() { if (!this.$$.ptr) { - throw new BindingError(pointeeType.name + ' instance already deleted'); + throwBindingError(pointeeType.name + ' instance already deleted'); } this.$$.count.value -= 1; @@ -707,6 +738,9 @@ function __embind_register_smart_ptr( pointeeType.smartPointerType = registerType(rawType, name, registeredPointer); } +function ClassHandle() { +} + // TODO: null pointers are always zero (not a Handle) in Javascript function __embind_register_class( rawType, @@ -726,9 +760,12 @@ function __embind_register_class( this.$$.pointeeType = type; // set below }); + Handle.prototype = Object.create(ClassHandle.prototype, { + constructor: { value: Handle }, + }); Handle.prototype.clone = function() { if (!this.$$.ptr) { - throw new BindingError(type.name + ' instance already deleted'); + throwBindingError(type.name + ' instance already deleted'); } var clone = Object.create(Handle.prototype); @@ -744,7 +781,7 @@ function __embind_register_class( // 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(type.name + ' instance already deleted'); // todo: but 'type' hasn't been resolved!?! + throwBindingError(type.name + ' instance already deleted'); // todo: but 'type' hasn't been resolved!?! } this.$$.count.value -= 1; @@ -801,7 +838,7 @@ function __embind_register_class_constructor( var argTypes = requireArgumentTypes(rawArgTypes, humanName); classType.constructor.body = function() { if (arguments.length !== argCount - 1) { - throw new BindingError('emscripten binding ' + humanName + ' called with ' + arguments.length + ' arguments, expected ' + (argCount-1)); + throwBindingError('emscripten binding ' + humanName + ' called with ' + arguments.length + ' arguments, expected ' + (argCount-1)); } var destructors = []; var args = new Array(argCount); @@ -837,10 +874,10 @@ function __embind_register_class_method( var argTypes = requireArgumentTypes(rawArgTypes, 'method ' + humanName); classType.Handle.prototype[methodName] = function() { if (!this.$$.ptr) { - throw new BindingError('cannot call emscripten binding method ' + humanName + ' on deleted object'); + throwBindingError('cannot call emscripten binding method ' + humanName + ' on deleted object'); } if (arguments.length !== argCount - 1) { - throw new BindingError('emscripten binding method ' + humanName + ' called with ' + arguments.length + ' arguments, expected ' + (argCount-1)); + throwBindingError('emscripten binding method ' + humanName + ' called with ' + arguments.length + ' arguments, expected ' + (argCount-1)); } var destructors = []; @@ -902,13 +939,13 @@ function __embind_register_class_field( Object.defineProperty(classType.Handle.prototype, fieldName, { get: function() { if (!this.$$.ptr) { - throw new BindingError('cannot access emscripten binding field ' + humanName + ' on deleted object'); + throwBindingError('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'); + throwBindingError('cannot modify emscripten binding field ' + humanName + ' on deleted object'); } var destructors = []; setter(this.$$.ptr, memberPointer, fieldType.toWireType(destructors, v)); From 76705c9008a9f075f2c3447079a6650326488376 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Thu, 28 Feb 2013 00:58:29 -0800 Subject: [PATCH 142/258] Illegal to pass raw pointer object from JS into smart pointer parameter --- src/embind/embind.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/embind/embind.js b/src/embind/embind.js index 08ed7ec10..e850cc3f3 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -588,6 +588,9 @@ RegisteredPointer.prototype.toWireType = function(destructors, handle) { if (!(handle instanceof ClassHandle)) { throwBindingError('Expected pointer or null, got ' + IMVU.repr(handle)); } + if (this.isSmartPointer && undefined === handle.$$.smartPtr) { + throwBindingError('Passing raw pointer to smart pointer is illegal'); + } if (handle.$$.pointeeType.isPolymorphic) { fromRawType = handle.$$.pointeeType.getDynamicRawPointerType(handle.$$.ptr); } else { From 612076a7cd5e24924f878873b01c321e0ccee444 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Thu, 28 Feb 2013 13:00:14 -0800 Subject: [PATCH 143/258] Kill a bunch of C-style casts --- system/include/emscripten/bind.h | 34 +++++++++++--------------------- system/lib/embind/bind.cpp | 25 +++++++++++------------ 2 files changed, 24 insertions(+), 35 deletions(-) diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index 298df4a45..bf9706d47 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -116,6 +116,13 @@ namespace emscripten { GenericFunction invoker, GenericFunction constructor); + void _embind_register_class_smart_ptr_constructor( + TYPEID classType, + unsigned argCount, + TYPEID argTypes[], + GenericFunction invoker, + GenericFunction constructor); + void _embind_register_class_method( TYPEID classType, const char* methodName, @@ -241,11 +248,11 @@ namespace emscripten { //////////////////////////////////////////////////////////////////////////////// extern "C" { - int __getDynamicPointerType(int p); + void* __getDynamicPointerType(void* p); } template - void function(const char* name, ReturnType (fn)(Args...), Policies...) { + void function(const char* name, ReturnType (*fn)(Args...), Policies...) { using namespace internal; registerStandardTypes(); @@ -640,24 +647,7 @@ namespace emscripten { class_& constructor(SmartPtr (*factory)(Args...)) { using namespace internal; - smart_ptr("SmartPtr"); - - typename WithPolicies<>::template ArgTypeList args; - /* - _embind_register_class_smart_ptr_constructor( - TypeID::get(), - args.count, - args.types, - reinterpret_cast(&raw_smart_ptr_constructor - */ - return *this; - } - - /* - template - class_& constructor(SmartPtr (*factory)(Args...)) { - using namespace internal; - + // todo: generate unique name smart_ptr("SmartPtr"); typename WithPolicies<>::template ArgTypeList args; @@ -665,10 +655,10 @@ namespace emscripten { TypeID::get(), args.count, args.types, - reinterpret_cast(&raw_smart_ptr_constructor + reinterpret_cast(&Invoker::invoke), + reinterpret_cast(factory)); return *this; } - */ template class_& allow_subclass() { diff --git a/system/lib/embind/bind.cpp b/system/lib/embind/bind.cpp index 705cc4db3..081db2cdd 100755 --- a/system/lib/embind/bind.cpp +++ b/system/lib/embind/bind.cpp @@ -147,7 +147,7 @@ namespace emscripten { // They are used by embind.js to implement automatic downcasting of return values which are pointers to // polymorphic objects. - void* EMSCRIPTEN_KEEPALIVE __staticPointerCast(void* p, int from, int to) { + void* EMSCRIPTEN_KEEPALIVE __staticPointerCast(void* p, const void* from, void* to) { std::vector> paths; int direction = 1; @@ -185,30 +185,29 @@ namespace emscripten { // __getDynamicPointerType returns (for polymorphic types only!) the type of the instance actually // pointed to. - int EMSCRIPTEN_KEEPALIVE __getDynamicPointerType(int p) { - void** vtable = *(void***)p; - return (int)static_cast(vtable[-1]); + const void* EMSCRIPTEN_KEEPALIVE __getDynamicPointerType(void* p) { + void** vtable = *reinterpret_cast(p); + return vtable[-1]; } // Calls to __dynamic_cast are generated by the compiler to implement dynamic_cast<>() -- its prototype is // not available through any header file. It is called directly here because it allows run-time // specification of the target pointer type (which can only be specified at compile time when using // dynamic_cast<>(). - void* __dynamic_cast(void*, const std::type_info*, const std::type_info*, int); + void* __dynamic_cast(void*, const std::type_info*, const std::type_info*, void*); // __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 to) { - int ret = (int)__staticPointerCast((void *)p, __getDynamicPointerType(p), to); + void* EMSCRIPTEN_KEEPALIVE __dynamicPointerCast(void* p, void* to) { + void* ret = __staticPointerCast(p, __getDynamicPointerType(p), to); if (ret < 0) { return 0; } return ret; } - const char* EMSCRIPTEN_KEEPALIVE __typeName(int p) { - const std::type_info* ti = (const std::type_info*)p; - size_t nameLen = std::min(strlen(ti->name()), (unsigned int)1024); + const char* EMSCRIPTEN_KEEPALIVE __typeName(const std::type_info* ti) { + size_t nameLen = std::min(strlen(ti->name()), 1024U); char* name = (char *)malloc(nameLen+1); int stat; @@ -221,8 +220,8 @@ namespace emscripten { return name; } - int EMSCRIPTEN_KEEPALIVE __peek32(int p) { - return *(int *)p; + size_t EMSCRIPTEN_KEEPALIVE __peek32(size_t p) { + return *reinterpret_cast(p); } EMSCRIPTEN_BINDINGS(([]() { @@ -231,7 +230,7 @@ namespace emscripten { // developers, but perhaps the double underscore will scare them away from calling it. function("__getDerivationPath", &__getDerivationPath); function("__getBaseClasses", &__getBaseClasses); - function("__peek32", &__peek32); + function("__peek32", &__peek32, allow_raw_pointers()); })); } From f22f5867872ffc07ba3e3a75472009d4fe4e0b42 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Thu, 28 Feb 2013 13:25:31 -0800 Subject: [PATCH 144/258] Apparently the WireType Marshaller is no longer necessary?? --- src/embind/embind.js | 34 +++++++++++++++++++++++++++++++- system/include/emscripten/bind.h | 2 +- system/include/emscripten/wire.h | 25 ++--------------------- 3 files changed, 36 insertions(+), 25 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index e850cc3f3..589f7ba99 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -833,7 +833,6 @@ function __embind_register_class_constructor( ) { var rawArgTypes = heap32VectorToArray(argCount, rawArgTypesAddr); invoker = FUNCTION_TABLE[invoker]; - rawConstructor = rawConstructor; requestDeferredRegistration(function() { var classType = requireRegisteredType(rawClassType, 'class'); @@ -858,6 +857,39 @@ function __embind_register_class_constructor( }); } +function __embind_register_class_smart_ptr_constructor( + rawClassType, + argCount, + rawArgTypesAddr, + invoker, + rawConstructor +) { + var rawArgTypes = heap32VectorToArray(argCount, rawArgTypesAddr); + invoker = FUNCTION_TABLE[invoker]; + + requestDeferredRegistration(function() { + var classType = requireRegisteredType(rawClassType, 'class'); + var humanName = 'constructor ' + classType.name; + var argTypes = requireArgumentTypes(rawArgTypes, humanName); + classType.constructor.body = function() { + if (arguments.length !== argCount - 1) { + throwBindingError(humanName + ' + called with ' + arguments.length + ' arguments, expected ' + (argCount-1)); + } + var destructors = []; + var args = new Array(argCount); + args[0] = rawConstructor; + for (var i = 1; i < argCount; ++i) { + args[i] = argTypes[i].toWireType(destructors, arguments[i - 1]); + } + + var ptr = invoker.apply(null, args); + runDestructors(destructors); + + return argTypes[0].fromWireType(ptr); + }; + }); +} + function __embind_register_class_method( rawClassType, methodName, diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index bf9706d47..baa579c6f 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -650,7 +650,7 @@ namespace emscripten { // todo: generate unique name smart_ptr("SmartPtr"); - typename WithPolicies<>::template ArgTypeList args; + typename WithPolicies<>::template ArgTypeList args; _embind_register_class_smart_ptr_constructor( TypeID::get(), args.count, diff --git a/system/include/emscripten/wire.h b/system/include/emscripten/wire.h index 10484a61e..1be23e781 100755 --- a/system/include/emscripten/wire.h +++ b/system/include/emscripten/wire.h @@ -261,33 +261,12 @@ namespace emscripten { typedef typename std::remove_reference::type ActualT; typedef ActualT* WireType; - struct Marshaller { - explicit Marshaller(WireType wt) - : wireType(wt) - {} - - Marshaller(Marshaller&& wt) - : wireType(wt.wireType) - { - wt.wireType = 0; - } - - operator ActualT&() const { - return *wireType; - } - - private: - Marshaller() = delete; - Marshaller(const Marshaller&) = delete; - ActualT* wireType; - }; - static WireType toWireType(T v) { return new T(v); } - static Marshaller fromWireType(WireType p) { - return Marshaller(p); + static ActualT& fromWireType(WireType p) { + return *p; } static void destroy(WireType p) { From f23cc669e328eec1c088b2a39637ccef7dfb5e84 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Thu, 28 Feb 2013 13:45:46 -0800 Subject: [PATCH 145/258] Add support for rvalue reference parameters on factories. I don't really understand this code. --- src/embind/embind.js | 4 ++-- system/include/emscripten/wire.h | 11 +++++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 589f7ba99..b5be46024 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -840,7 +840,7 @@ function __embind_register_class_constructor( var argTypes = requireArgumentTypes(rawArgTypes, humanName); classType.constructor.body = function() { if (arguments.length !== argCount - 1) { - throwBindingError('emscripten binding ' + humanName + ' called with ' + arguments.length + ' arguments, expected ' + (argCount-1)); + throwBindingError(humanName + ' called with ' + arguments.length + ' arguments, expected ' + (argCount-1)); } var destructors = []; var args = new Array(argCount); @@ -873,7 +873,7 @@ function __embind_register_class_smart_ptr_constructor( var argTypes = requireArgumentTypes(rawArgTypes, humanName); classType.constructor.body = function() { if (arguments.length !== argCount - 1) { - throwBindingError(humanName + ' + called with ' + arguments.length + ' arguments, expected ' + (argCount-1)); + throwBindingError(humanName + ' called with ' + arguments.length + ' arguments, expected ' + (argCount-1)); } var destructors = []; var args = new Array(argCount); diff --git a/system/include/emscripten/wire.h b/system/include/emscripten/wire.h index 1be23e781..d4efc3acf 100755 --- a/system/include/emscripten/wire.h +++ b/system/include/emscripten/wire.h @@ -209,6 +209,17 @@ namespace emscripten { } }; + template + struct BindingType { + typedef typename BindingType::WireType WireType; + static WireType toWireType(const T& v) { + return BindingType::toWireType(v); + } + static T fromWireType(WireType wt) { + return BindingType::fromWireType(wt); + } + }; + template struct BindingType { typedef T* WireType; From 3adc0bd5ed011987ae2c396027e3dcc5ebdea92e Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Thu, 28 Feb 2013 16:33:00 -0800 Subject: [PATCH 146/258] Turns out we can simplify and reuse a lot of code :o --- src/embind/embind.js | 33 -------------------------------- system/include/emscripten/bind.h | 19 ++++++------------ 2 files changed, 6 insertions(+), 46 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index b5be46024..d4d428ad9 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -834,39 +834,6 @@ function __embind_register_class_constructor( var rawArgTypes = heap32VectorToArray(argCount, rawArgTypesAddr); invoker = FUNCTION_TABLE[invoker]; - requestDeferredRegistration(function() { - var classType = requireRegisteredType(rawClassType, 'class'); - var humanName = 'constructor ' + classType.name; - var argTypes = requireArgumentTypes(rawArgTypes, humanName); - classType.constructor.body = function() { - if (arguments.length !== argCount - 1) { - throwBindingError(humanName + ' called with ' + arguments.length + ' arguments, expected ' + (argCount-1)); - } - var destructors = []; - var args = new Array(argCount); - args[0] = rawConstructor; - for (var i = 1; i < argCount; ++i) { - args[i] = argTypes[i].toWireType(destructors, arguments[i - 1]); - } - - var ptr = invoker.apply(null, args); - runDestructors(destructors); - - return classType.Handle.call(this, ptr); - }; - }); -} - -function __embind_register_class_smart_ptr_constructor( - rawClassType, - argCount, - rawArgTypesAddr, - invoker, - rawConstructor -) { - var rawArgTypes = heap32VectorToArray(argCount, rawArgTypesAddr); - invoker = FUNCTION_TABLE[invoker]; - requestDeferredRegistration(function() { var classType = requireRegisteredType(rawClassType, 'class'); var humanName = 'constructor ' + classType.name; diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index baa579c6f..15bbfc865 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -116,13 +116,6 @@ namespace emscripten { GenericFunction invoker, GenericFunction constructor); - void _embind_register_class_smart_ptr_constructor( - TYPEID classType, - unsigned argCount, - TYPEID argTypes[], - GenericFunction invoker, - GenericFunction constructor); - void _embind_register_class_method( TYPEID classType, const char* methodName, @@ -633,7 +626,7 @@ namespace emscripten { class_& constructor(ClassType* (*factory)(Args...), Policies...) { using namespace internal; - typename WithPolicies::template ArgTypeList args; + typename WithPolicies::template ArgTypeList, Args...> args; _embind_register_class_constructor( TypeID::get(), args.count, @@ -643,15 +636,15 @@ namespace emscripten { return *this; } - template - class_& constructor(SmartPtr (*factory)(Args...)) { + template + class_& constructor(SmartPtr (*factory)(Args...), Policies...) { using namespace internal; - // todo: generate unique name + // TODO: generate unique name smart_ptr("SmartPtr"); - typename WithPolicies<>::template ArgTypeList args; - _embind_register_class_smart_ptr_constructor( + typename WithPolicies::template ArgTypeList args; + _embind_register_class_constructor( TypeID::get(), args.count, args.types, From 067dbfd94662027d220cd93085d67ca0bb3cf800 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Thu, 28 Feb 2013 16:57:56 -0800 Subject: [PATCH 147/258] Mike Ey and I decided the smart pointer constructor form is different enough to deserve its own name. --- system/include/emscripten/bind.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index 15bbfc865..f6b640d11 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -637,7 +637,7 @@ namespace emscripten { } template - class_& constructor(SmartPtr (*factory)(Args...), Policies...) { + class_& smart_ptr_constructor(SmartPtr (*factory)(Args...), Policies...) { using namespace internal; // TODO: generate unique name From 9008c4da5344b55d524a811567e611c901c5c16f Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Thu, 28 Feb 2013 17:07:48 -0800 Subject: [PATCH 148/258] smart ptr 5472 object is non-enumerable --- src/embind/embind.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index d4d428ad9..7000ddc48 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -698,11 +698,14 @@ function __embind_register_smart_ptr( rawGetPointee = FUNCTION_TABLE[rawGetPointee]; var Handle = createNamedFunction(name, function(ptr) { - this.$$ = {}; - this.$$.count = {value: 1}; - this.$$.smartPtr = ptr; // std::shared_ptr* - this.$$.ptr = rawGetPointee(ptr); // T* - this.$$.pointeeType = pointeeType; + Object.defineProperty(this, '$$', { + value: { + count: {value: 1}, + smartPtr: ptr, + ptr: rawGetPointee(ptr), + pointeeType: pointeeType, + }, + }); }); // TODO: test for SmartPtr.prototype.constructor property? From d824fcb7ba3f28987761a80a9645ca7c630eb97b Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Thu, 28 Feb 2013 17:15:20 -0800 Subject: [PATCH 149/258] $$ is never enumerable --- src/embind/embind.js | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 7000ddc48..aab20b69f 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -718,10 +718,13 @@ function __embind_register_smart_ptr( } var clone = Object.create(Handle.prototype); - clone.$$ = {}; - clone.$$.count = this.$$.count; - clone.$$.smartPtr = this.$$.smartPtr; - clone.$$.ptr = this.$$.ptr; + Object.defineProperty(clone, '$$', { + value: { + count: this.$$.count, + smartPtr: this.$$.smartPtr, + ptr: this.$$.ptr, + }, + }); clone.$$.count.value += 1; return clone; @@ -760,10 +763,13 @@ function __embind_register_class( rawDestructor = FUNCTION_TABLE[rawDestructor]; var Handle = createNamedFunction(name, function(ptr) { - this.$$ = {}; - this.$$.count = {value: 1, ptr: ptr }; - this.$$.ptr = ptr; - this.$$.pointeeType = type; // set below + Object.defineProperty(this, '$$', { + value: { + count: {value: 1, ptr: ptr}, + ptr: ptr, + pointeeType: type, + } + }); }); Handle.prototype = Object.create(ClassHandle.prototype, { @@ -775,9 +781,12 @@ function __embind_register_class( } var clone = Object.create(Handle.prototype); - clone.$$ = {}; - clone.$$.count = this.$$.count; - clone.$$.ptr = this.$$.ptr; + Object.defineProperty(clone, '$$', { + value: { + count: this.$$.count, + ptr: this.$$.ptr, + }, + }); clone.$$.count.value += 1; return clone; From 1a352601847328eb9f60b5c7068b8f18ab28ddc6 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Fri, 1 Mar 2013 12:52:54 -0800 Subject: [PATCH 150/258] Preliminary support for custom smart pointers. --- system/include/emscripten/bind.h | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index f6b640d11..e8f21be42 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -284,12 +284,6 @@ namespace emscripten { delete ptr; } - template - typename PointerType::element_type* get_pointee(const PointerType& ptr) { - // TODO: replace with general pointer traits implementation - return ptr.get(); - } - template struct FunctionInvoker { typedef ReturnType (*FunctionPointer)(ClassType& ct, Args...); @@ -540,14 +534,24 @@ namespace emscripten { // specialize if you have a different pointer type template - struct get_element_type { - typedef typename PointerType::element_type type; + struct smart_ptr_trait { + typedef typename PointerType::element_type element_type; + static element_type* get(const PointerType& ptr) { + return ptr.get(); + } }; + namespace internal { + template + typename smart_ptr_trait::element_type* get_pointee(const PointerType& ptr) { + return smart_ptr_trait::get(ptr); + } + } + template void smart_ptr(const char* name) { using namespace internal; - typedef typename get_element_type::type PointeeType; + typedef typename smart_ptr_trait::element_type PointeeType; registerStandardTypes(); _embind_register_smart_ptr( From 50c5092c4a37565558e6ffab7602e778b6774e2a Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Fri, 1 Mar 2013 16:21:30 -0800 Subject: [PATCH 151/258] Kill the malloc(16) by writing a test with a 1 MB smart pointer type :) --- src/embind/embind.js | 6 +----- system/include/emscripten/bind.h | 7 +++++-- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index aab20b69f..8da583ba1 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -601,11 +601,7 @@ RegisteredPointer.prototype.toWireType = function(destructors, handle) { } var ptr = staticPointerCast(handle.$$.ptr, fromRawType, this.pointeeType.rawType); if (this.isSmartPointer) { - // todo: if ptr == handle.$$.ptr, there's no need to allocate a new smartPtr! - // todo: _malloc(16) is not big enough - var smartPtr = _malloc(16); - handle.$$.pointeeType.smartPointerType.rawConstructor(smartPtr, ptr, handle.$$.smartPtr); - ptr = smartPtr; + ptr = handle.$$.pointeeType.smartPointerType.rawConstructor(ptr, handle.$$.smartPtr); destructors.push(handle.$$.pointeeType.smartPointerType.rawDestructor); destructors.push(ptr); } diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index e8f21be42..9120a0954 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -275,8 +275,11 @@ namespace emscripten { } template - typename std::shared_ptr raw_smart_pointer_constructor(PointerType *ptr, std::shared_ptr basePtr, void (PointerType*)) { - return std::shared_ptr(basePtr, ptr); + typename std::shared_ptr* raw_smart_pointer_constructor( + PointerType *ptr, + std::shared_ptr* basePtr + ) { + return new std::shared_ptr(*basePtr, ptr); } template From ececfcc02e2020c2c965cb0d6edc0fc321def581 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Fri, 1 Mar 2013 17:31:44 -0800 Subject: [PATCH 152/258] Add support for marshalling custom smart pointer types in and out of parameters. --- src/embind/embind.js | 8 +++++++- system/include/emscripten/bind.h | 23 ++++++++++++++--------- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 8da583ba1..3ecea3e0a 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -601,7 +601,13 @@ RegisteredPointer.prototype.toWireType = function(destructors, handle) { } var ptr = staticPointerCast(handle.$$.ptr, fromRawType, this.pointeeType.rawType); if (this.isSmartPointer) { - ptr = handle.$$.pointeeType.smartPointerType.rawConstructor(ptr, handle.$$.smartPtr); + // If this is for smart ptr type conversion, I think it + // assumes that smart_ptr has an identical binary layout to + // smart_ptr. I wonder if that's untrue for any common + // smart pointer. - chad + ptr = handle.$$.pointeeType.smartPointerType.rawConstructor( + ptr, + handle.$$.smartPtr); destructors.push(handle.$$.pointeeType.smartPointerType.rawDestructor); destructors.push(ptr); } diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index 9120a0954..ad11395b8 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -274,14 +274,6 @@ namespace emscripten { ); } - template - typename std::shared_ptr* raw_smart_pointer_constructor( - PointerType *ptr, - std::shared_ptr* basePtr - ) { - return new std::shared_ptr(*basePtr, ptr); - } - template void raw_destructor(ClassType* ptr) { delete ptr; @@ -539,12 +531,25 @@ namespace emscripten { template struct smart_ptr_trait { typedef typename PointerType::element_type element_type; + static element_type* get(const PointerType& ptr) { return ptr.get(); } + + static PointerType share(const PointerType& r, element_type* ptr) { + return PointerType(r, ptr); + } }; namespace internal { + template + SmartPointerType* raw_smart_pointer_constructor( + typename smart_ptr_trait::element_type* ptr, + SmartPointerType* basePtr + ) { + return new SmartPointerType(smart_ptr_trait::share(*basePtr, ptr)); + } + template typename smart_ptr_trait::element_type* get_pointee(const PointerType& ptr) { return smart_ptr_trait::get(ptr); @@ -561,7 +566,7 @@ namespace emscripten { TypeID::get(), TypeID::get(), name, - reinterpret_cast(&raw_smart_pointer_constructor), + reinterpret_cast(&raw_smart_pointer_constructor), reinterpret_cast(&raw_destructor), reinterpret_cast(&get_pointee)); }; From 97373dc8c29bdc205ffc7f0674edaa9eff80fb0c Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Tue, 5 Mar 2013 14:02:29 -0800 Subject: [PATCH 153/258] kinda start thinking about making isinstance work with base classes --- src/embind/embind.js | 13 +++++++------ system/include/emscripten/bind.h | 4 +++- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 3ecea3e0a..b33ff2238 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -42,7 +42,7 @@ function extendError(baseErrorType, errorName) { return this.name + ': ' + this.message; } }; - + return errorClass; } @@ -698,7 +698,7 @@ function __embind_register_smart_ptr( rawConstructor = FUNCTION_TABLE[rawConstructor]; rawDestructor = FUNCTION_TABLE[rawDestructor]; rawGetPointee = FUNCTION_TABLE[rawGetPointee]; - + var Handle = createNamedFunction(name, function(ptr) { Object.defineProperty(this, '$$', { value: { @@ -713,7 +713,7 @@ function __embind_register_smart_ptr( // TODO: test for SmartPtr.prototype.constructor property? // We likely want it distinct from pointeeType.prototype.constructor Handle.prototype = Object.create(pointeeType.Handle.prototype); - + Handle.prototype.clone = function() { if (!this.$$.ptr) { throwBindingError(pointeeType.name + ' instance already deleted'); @@ -727,16 +727,16 @@ function __embind_register_smart_ptr( ptr: this.$$.ptr, }, }); - + clone.$$.count.value += 1; return clone; }; - + Handle.prototype['delete'] = function() { if (!this.$$.ptr) { throwBindingError(pointeeType.name + ' instance already deleted'); } - + this.$$.count.value -= 1; if (0 === this.$$.count.value) { rawDestructor(this.$$.smartPtr); @@ -757,6 +757,7 @@ function __embind_register_class( rawType, rawPointerType, rawConstPointerType, + baseClassType, isPolymorphic, name, rawDestructor diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index ad11395b8..cbc5cb8e9 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -105,6 +105,7 @@ namespace emscripten { TYPEID classType, TYPEID pointerType, TYPEID constPointerType, + TYPEID baseClassType, bool isPolymorphic, const char* className, GenericFunction destructor); @@ -610,7 +611,7 @@ namespace emscripten { #define EMSCRIPTEN_WRAPPER(T) \ T(const ::emscripten::val& v): wrapper(v) {} - // TODO: support external class constructors + // TODO: support base class template class class_ { public: @@ -622,6 +623,7 @@ namespace emscripten { TypeID::get(), TypeID>::get(), TypeID>::get(), + 0, std::is_polymorphic::value, name, reinterpret_cast(&raw_destructor)); From c2e001c109f50ecc4f49f977e15fb2440f93389d Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Tue, 5 Mar 2013 14:35:29 -0800 Subject: [PATCH 154/258] tiny refactoring --- src/embind/embind.js | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index b33ff2238..39f136f0b 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -182,7 +182,6 @@ function registerType(rawType, name, registeredInstance) { registeredInstance.rawType = rawType; registeredInstance.name = name; typeRegistry[rawType] = registeredInstance; - return registeredInstance; } function requireRegisteredType(rawType, humanName) { @@ -746,12 +745,18 @@ function __embind_register_smart_ptr( }; var registeredPointer = new RegisteredPointer(Handle, pointeeType.isPolymorphic, true, rawGetPointee, rawConstructor, rawDestructor); registeredPointer.pointeeType = pointeeType; - pointeeType.smartPointerType = registerType(rawType, name, registeredPointer); + registerType(rawType, name, registeredPointer); + pointeeType.smartPointerType = registeredPointer; } function ClassHandle() { } +function RegisteredClass(name, isPolymorphic, baseClass) { + this.name = name; + this.isPolymorphic = isPolymorphic; +} + // TODO: null pointers are always zero (not a Handle) in Javascript function __embind_register_class( rawType, @@ -765,6 +770,8 @@ function __embind_register_class( name = Pointer_stringify(name); rawDestructor = FUNCTION_TABLE[rawDestructor]; + var registeredClass = new RegisteredClass(name, isPolymorphic); + var Handle = createNamedFunction(name, function(ptr) { Object.defineProperty(this, '$$', { value: { @@ -812,8 +819,9 @@ function __embind_register_class( // todo: clean this up! var registeredClass = new RegisteredPointer(Handle, isPolymorphic, false); - var type = registerType(rawType, name, registeredClass); - registeredClass.pointeeType = type; + var type = registeredClass; + registerType(rawType, name, registeredClass); + registeredClass.pointeeType = registeredClass; var registeredClass = new RegisteredPointer(Handle, isPolymorphic, false); registerType(rawPointerType, name + '*', registeredClass); From ea2b0db6e84393ad2c9406f9c58346d7bd6b5881 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Tue, 5 Mar 2013 15:15:12 -0800 Subject: [PATCH 155/258] Kill a bunch of unused classes and reduce the number of lines of code in embind.js --- src/embind/embind.js | 359 ++++++++++++++++++++----------------------- 1 file changed, 168 insertions(+), 191 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 39f136f0b..5fd2fd1a5 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -180,7 +180,6 @@ function registerType(rawType, name, registeredInstance) { throwBindingError("Cannot register type '" + name + "' twice"); } registeredInstance.rawType = rawType; - registeredInstance.name = name; typeRegistry[rawType] = registeredInstance; } @@ -227,124 +226,104 @@ function staticPointerCast(from, fromType, toType) { return to; } -function RegisteredVoid() { -} - -RegisteredVoid.prototype.fromWireType = function() { - return undefined; -}; - function __embind_register_void(rawType, name) { name = Pointer_stringify(name); - registerType(rawType, name, new RegisteredVoid()); + registerType(rawType, name, { + name: name, + fromWireType: function() { + return undefined; + }, + }); } -function RegisteredBool(trueValue, falseValue) { - this.trueValue = trueValue; - this.falseValue = falseValue; -} - -RegisteredBool.prototype.toWireType = function(destructors, o) { - return o ? this.trueValue : this.falseValue; -}; - -RegisteredBool.prototype.fromWireType = function(wt) { - // ambiguous emscripten ABI: sometimes return values are - // true or false, and sometimes integers (0 or 1) - return !!wt; -}; - function __embind_register_bool(rawType, name, trueValue, falseValue) { name = Pointer_stringify(name); - registerType(rawType, name, new RegisteredBool(trueValue, falseValue)); + registerType(rawType, name, { + name: name, + fromWireType: function(wt) { + // ambiguous emscripten ABI: sometimes return values are + // true or false, and sometimes integers (0 or 1) + return !!wt; + }, + toWireType: function(destructors, o) { + return o ? trueValue : falseValue; + }, + }); } -function RegisteredInteger() { -} - -RegisteredInteger.prototype.toWireType = function(destructors, value) { - if (typeof value !== "number") { - throw new TypeError('Cannot convert "' + _embind_repr(value) + '" to ' + this.name); - } - return value | 0; -}; - -RegisteredInteger.prototype.fromWireType = function(value) { - return value; -}; - function __embind_register_integer(rawType, name) { name = Pointer_stringify(name); - registerType(rawType, name, new RegisteredInteger()); + registerType(rawType, name, { + name: name, + fromWireType: function(value) { + return value; + }, + toWireType: function(destructors, value) { + if (typeof value !== "number") { + throw new TypeError('Cannot convert "' + _embind_repr(value) + '" to ' + this.name); + } + return value | 0; + }, + }); } -function RegisteredFloat() { -} - -RegisteredFloat.prototype.toWireType = function(destructors, value) { - if (typeof value !== "number") { - throw new TypeError('Cannot convert "' + _embind_repr(value) + '" to ' +this.name); - } - return value; -}; - -RegisteredFloat.prototype.fromWireType = function(value) { - return value; -}; - function __embind_register_float(rawType, name) { name = Pointer_stringify(name); - registerType(rawType, name, new RegisteredFloat()); + registerType(rawType, name, { + name: name, + fromWireType: function(value) { + return value; + }, + toWireType: function(destructors, value) { + if (typeof value !== "number") { + throw new TypeError('Cannot convert "' + _embind_repr(value) + '" to ' +this.name); + } + return value; + }, + }); } -function RegisteredString(stringType, name) { - -} - -RegisteredString.prototype.toWireType = function(destructors, value) { - // assumes 4-byte alignment - var length = value.length; - var ptr = _malloc(4 + length); - HEAP32[ptr >> 2] = length; - for (var i = 0; i < length; ++i) { - HEAPU8[ptr + 4 + i] = value.charCodeAt(i); - } - destructors.push(_free); - destructors.push(ptr); - return ptr; -}; - -RegisteredString.prototype.fromWireType = function(value) { - var length = HEAP32[value >> 2]; - var a = new Array(length); - for (var i = 0; i < length; ++i) { - a[i] = String.fromCharCode(HEAPU8[value + 4 + i]); - } - _free(value); - return a.join(''); -}; - function __embind_register_cstring(rawType, name) { name = Pointer_stringify(name); - registerType(rawType, name, new RegisteredString()); + registerType(rawType, name, { + name: name, + fromWireType: function(value) { + var length = HEAP32[value >> 2]; + var a = new Array(length); + for (var i = 0; i < length; ++i) { + a[i] = String.fromCharCode(HEAPU8[value + 4 + i]); + } + _free(value); + return a.join(''); + }, + toWireType: function(destructors, value) { + // assumes 4-byte alignment + var length = value.length; + var ptr = _malloc(4 + length); + HEAP32[ptr >> 2] = length; + for (var i = 0; i < length; ++i) { + HEAPU8[ptr + 4 + i] = value.charCodeAt(i); + } + destructors.push(_free); + destructors.push(ptr); + return ptr; + }, + }); } -function RegisteredEmval() { -} - -RegisteredEmval.prototype.toWireType = function(destructors, value) { - return __emval_register(value); -}; - -RegisteredEmval.prototype.fromWireType = function(handle) { - var rv = _emval_handle_array[handle].value; - __emval_decref(handle); - return rv; -}; - function __embind_register_emval(rawType, name) { name = Pointer_stringify(name); - registerType(rawType, name, new RegisteredEmval()); + registerType(rawType, name, { + name: name, + fromWireType: function(handle) { + var rv = _emval_handle_array[handle].value; + __emval_decref(handle); + return rv; + }, + toWireType: function(destructors, value) { + return __emval_register(value); + }, + }); } function runDestructors(destructors) { @@ -390,41 +369,38 @@ function __embind_register_function(name, argCount, rawArgTypesAddr, rawInvoker, }); } -function RegisteredTuple(rawConstructor, rawDestructor) { - this.rawConstructor = rawConstructor; - this.rawDestructor = rawDestructor; - this.elements = []; -} - -RegisteredTuple.prototype.toWireType = function(destructors, o) { - var len = this.elements.length; - if (len !== o.length) { - throw new TypeError("Incorrect number of tuple elements"); - } - var ptr = this.rawConstructor(); - for (var i = 0; i < len; ++i) { - this.elements[i].write(ptr, o[i]); - } - destructors.push(this.rawDestructor); - destructors.push(ptr); - return ptr; -}; - -RegisteredTuple.prototype.fromWireType = function(ptr) { - var len = this.elements.length; - var rv = new Array(len); - for (var i = 0; i < len; ++i) { - rv[i] = this.elements[i].read(ptr); - } - this.rawDestructor(ptr); - return rv; -}; - function __embind_register_tuple(rawType, name, rawConstructor, rawDestructor) { name = Pointer_stringify(name); rawConstructor = FUNCTION_TABLE[rawConstructor]; rawDestructor = FUNCTION_TABLE[rawDestructor]; - registerType(rawType, name, new RegisteredTuple(rawConstructor, rawDestructor)); + registerType(rawType, name, { + name: name, + rawConstructor: rawConstructor, + rawDestructor: rawDestructor, + elements: [], + fromWireType: function(ptr) { + var len = this.elements.length; + var rv = new Array(len); + for (var i = 0; i < len; ++i) { + rv[i] = this.elements[i].read(ptr); + } + this.rawDestructor(ptr); + return rv; + }, + toWireType: function(destructors, o) { + var len = this.elements.length; + if (len !== o.length) { + throw new TypeError("Incorrect number of tuple elements"); + } + var ptr = this.rawConstructor(); + for (var i = 0; i < len; ++i) { + this.elements[i].write(ptr, o[i]); + } + destructors.push(rawDestructor); + destructors.push(ptr); + return ptr; + }, + }); } function copyMemberPointer(memberPointer, memberPointerSize) { @@ -447,6 +423,7 @@ function __embind_register_tuple_element( getter = FUNCTION_TABLE[getter]; setter = FUNCTION_TABLE[setter]; memberPointer = copyMemberPointer(memberPointer, memberPointerSize); + // TODO: this could register elements out of order requestDeferredRegistration(function() { var tupleType = requireRegisteredType(rawTupleType, 'tuple'); var type = requireRegisteredType(rawType, "element " + tupleType.name + "[" + tupleType.elements.length + "]"); @@ -496,38 +473,6 @@ function __embind_register_tuple_element_accessor( }); } -function RegisteredStruct(rawConstructor, rawDestructor) { - this.rawConstructor = rawConstructor; - this.rawDestructor = rawDestructor; - this.fields = {}; -} - -RegisteredStruct.prototype.toWireType = function(destructors, o) { - var fields = this.fields; - for (var fieldName in fields) { - if (!(fieldName in o)) { - throw new TypeError('Missing field'); - } - } - var ptr = this.rawConstructor(); - for (fieldName in fields) { - fields[fieldName].write(ptr, o[fieldName]); - } - destructors.push(this.rawDestructor); - destructors.push(ptr); - return ptr; -}; - -RegisteredStruct.prototype.fromWireType = function(ptr) { - var fields = this.fields; - var rv = {}; - for (var i in fields) { - rv[i] = fields[i].read(ptr); - } - this.rawDestructor(ptr); - return rv; -}; - function __embind_register_struct( rawType, name, @@ -538,7 +483,36 @@ function __embind_register_struct( rawConstructor = FUNCTION_TABLE[rawConstructor]; rawDestructor = FUNCTION_TABLE[rawDestructor]; - registerType(rawType, name, new RegisteredStruct(rawConstructor, rawDestructor)); + registerType(rawType, name, { + name: name, + rawConstructor: rawConstructor, + rawDestructor: rawDestructor, + fields: {}, + fromWireType: function(ptr) { + var fields = this.fields; + var rv = {}; + for (var i in fields) { + rv[i] = fields[i].read(ptr); + } + this.rawDestructor(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 = this.rawConstructor(); + for (fieldName in fields) { + fields[fieldName].write(ptr, o[fieldName]); + } + destructors.push(rawDestructor); + destructors.push(ptr); + return ptr; + }, + }); } function __embind_register_struct_field( @@ -554,6 +528,7 @@ function __embind_register_struct_field( rawGetter = FUNCTION_TABLE[rawGetter]; rawSetter = FUNCTION_TABLE[rawSetter]; memberPointer = copyMemberPointer(memberPointer, memberPointerSize); + // TODO: this could register elements out of order requestDeferredRegistration(function() { var structType = requireRegisteredType(rawStructType, 'struct'); var fieldType = requireRegisteredType(rawFieldType, 'field "' + structType.name + '.' + fieldName + '"'); @@ -745,6 +720,7 @@ function __embind_register_smart_ptr( }; var registeredPointer = new RegisteredPointer(Handle, pointeeType.isPolymorphic, true, rawGetPointee, rawConstructor, rawDestructor); registeredPointer.pointeeType = pointeeType; + registeredPointer.name = name; registerType(rawType, name, registeredPointer); pointeeType.smartPointerType = registeredPointer; } @@ -819,15 +795,18 @@ function __embind_register_class( // todo: clean this up! var registeredClass = new RegisteredPointer(Handle, isPolymorphic, false); + registeredClass.name = name; var type = registeredClass; registerType(rawType, name, registeredClass); registeredClass.pointeeType = registeredClass; var registeredClass = new RegisteredPointer(Handle, isPolymorphic, false); + registeredClass.name = name; registerType(rawPointerType, name + '*', registeredClass); registeredClass.pointeeType = type; // todo: implement const pointers (no modification Javascript side) var registeredClass = new RegisteredPointer(Handle, isPolymorphic, false); + registeredClass.name = name; registerType(rawConstPointerType, name + ' const*', registeredClass); registeredClass.pointeeType = type; @@ -982,27 +961,27 @@ function __embind_register_class_field( }); } -function RegisteredEnum() { - this.constructor = function() {}; - this.constructor.values = {}; -} - -RegisteredEnum.prototype.toWireType = function(destructors, c) { - return c.value; -}; - -RegisteredEnum.prototype.fromWireType = function(c) { - return this.constructor.values[c]; -}; - function __embind_register_enum( rawType, name ) { name = Pointer_stringify(name); - var newEnum = new RegisteredEnum(); - registerType(rawType, name, newEnum); - exposePublicSymbol(name, newEnum.constructor); + + function constructor() { + } + constructor.values = {}; + + registerType(rawType, name, { + name: name, + constructor: constructor, + fromWireType: function(c) { + return this.constructor.values[c]; + }, + toWireType: function(destructors, c) { + return c.value; + }, + }); + exposePublicSymbol(name, constructor); } function __embind_register_enum_value( @@ -1023,19 +1002,6 @@ function __embind_register_enum_value( Enum[name] = Value; } -function RegisteredInterface(rawConstructor, rawDestructor) { - this.rawConstructor = rawConstructor; - this.rawDestructor = rawDestructor; -} - -RegisteredInterface.prototype.toWireType = function(destructors, o) { - var handle = __emval_register(o); - var ptr = this.rawConstructor(handle); - destructors.push(this.rawDestructor); - destructors.push(ptr); - return ptr; -}; - function __embind_register_interface( rawType, name, @@ -1046,6 +1012,17 @@ function __embind_register_interface( rawConstructor = FUNCTION_TABLE[rawConstructor]; rawDestructor = FUNCTION_TABLE[rawDestructor]; - registerType(rawType, name, new RegisteredInterface(rawConstructor, rawDestructor)); + registerType(rawType, name, { + name: name, + rawConstructor: rawConstructor, + rawDestructor: rawDestructor, + toWireType: function(destructors, o) { + var handle = __emval_register(o); + var ptr = this.rawConstructor(handle); + destructors.push(this.rawDestructor); + destructors.push(ptr); + return ptr; + }, + }); } From f013fe14077ad109db4d679d2406453814622d07 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Tue, 5 Mar 2013 18:39:02 -0800 Subject: [PATCH 156/258] registerType gets its name from the registered instance now --- src/embind/embind.js | 67 +++++++++++++++++++++++++------------------- 1 file changed, 38 insertions(+), 29 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 5fd2fd1a5..629e42ff4 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -172,7 +172,8 @@ function resolveBindings() { } } -function registerType(rawType, name, registeredInstance) { +function registerType(rawType, registeredInstance) { + var name = registeredInstance.name; if (!rawType) { throwBindingError('type "' + name + '" must have a positive integer typeid pointer'); } @@ -228,7 +229,7 @@ function staticPointerCast(from, fromType, toType) { function __embind_register_void(rawType, name) { name = Pointer_stringify(name); - registerType(rawType, name, { + registerType(rawType, { name: name, fromWireType: function() { return undefined; @@ -238,7 +239,7 @@ function __embind_register_void(rawType, name) { function __embind_register_bool(rawType, name, trueValue, falseValue) { name = Pointer_stringify(name); - registerType(rawType, name, { + registerType(rawType, { name: name, fromWireType: function(wt) { // ambiguous emscripten ABI: sometimes return values are @@ -253,7 +254,7 @@ function __embind_register_bool(rawType, name, trueValue, falseValue) { function __embind_register_integer(rawType, name) { name = Pointer_stringify(name); - registerType(rawType, name, { + registerType(rawType, { name: name, fromWireType: function(value) { return value; @@ -269,7 +270,7 @@ function __embind_register_integer(rawType, name) { function __embind_register_float(rawType, name) { name = Pointer_stringify(name); - registerType(rawType, name, { + registerType(rawType, { name: name, fromWireType: function(value) { return value; @@ -285,7 +286,7 @@ function __embind_register_float(rawType, name) { function __embind_register_cstring(rawType, name) { name = Pointer_stringify(name); - registerType(rawType, name, { + registerType(rawType, { name: name, fromWireType: function(value) { var length = HEAP32[value >> 2]; @@ -313,7 +314,7 @@ function __embind_register_cstring(rawType, name) { function __embind_register_emval(rawType, name) { name = Pointer_stringify(name); - registerType(rawType, name, { + registerType(rawType, { name: name, fromWireType: function(handle) { var rv = _emval_handle_array[handle].value; @@ -373,7 +374,7 @@ function __embind_register_tuple(rawType, name, rawConstructor, rawDestructor) { name = Pointer_stringify(name); rawConstructor = FUNCTION_TABLE[rawConstructor]; rawDestructor = FUNCTION_TABLE[rawDestructor]; - registerType(rawType, name, { + registerType(rawType, { name: name, rawConstructor: rawConstructor, rawDestructor: rawDestructor, @@ -483,7 +484,7 @@ function __embind_register_struct( rawConstructor = FUNCTION_TABLE[rawConstructor]; rawDestructor = FUNCTION_TABLE[rawDestructor]; - registerType(rawType, name, { + registerType(rawType, { name: name, rawConstructor: rawConstructor, rawDestructor: rawDestructor, @@ -545,15 +546,20 @@ function __embind_register_struct_field( }); } -function RegisteredPointer(Handle, isPolymorphic, isSmartPointer, rawGetPointee, rawConstructor, rawDestructor) { - this.Handle = Handle; - this.isPolymorphic = isPolymorphic; +function RegisteredPointer(name, registeredClass, Handle, isSmartPointer, rawGetPointee, rawConstructor, rawDestructor) { + this.name = name; + this.registeredClass = registeredClass; + this.Handle = Handle; // <-- I think I can kill this this.isSmartPointer = isSmartPointer; this.rawGetPointee = rawGetPointee; this.rawConstructor = rawConstructor; this.rawDestructor = rawDestructor; } +RegisteredPointer.prototype.isPolymorphic = function() { + return this.registeredClass.isPolymorphic; +}; + RegisteredPointer.prototype.toWireType = function(destructors, handle) { var fromRawType; if (handle === null) { @@ -565,7 +571,7 @@ RegisteredPointer.prototype.toWireType = function(destructors, handle) { if (this.isSmartPointer && undefined === handle.$$.smartPtr) { throwBindingError('Passing raw pointer to smart pointer is illegal'); } - if (handle.$$.pointeeType.isPolymorphic) { + if (handle.$$.pointeeType.isPolymorphic()) { fromRawType = handle.$$.pointeeType.getDynamicRawPointerType(handle.$$.ptr); } else { fromRawType = handle.$$.pointeeType.rawType; @@ -586,7 +592,7 @@ RegisteredPointer.prototype.toWireType = function(destructors, handle) { destructors.push(ptr); } return ptr; - }; +}; RegisteredPointer.prototype.getPointee = function(ptr) { if (this.rawGetPointee) { @@ -612,7 +618,7 @@ RegisteredPointer.prototype.fromWireType = function(ptr) { // todo: could this return the actual type if not polymorphic? RegisteredPointer.prototype.getDynamicRawPointerType = function(ptr) { var type = null; - if (this.isPolymorphic) { + if (this.isPolymorphic()) { if (this.rawGetPointee) { type = ___getDynamicPointerType(this.rawGetPointee(ptr)); } else { @@ -718,10 +724,16 @@ function __embind_register_smart_ptr( this.$$.smartPtr = undefined; this.$$.ptr = undefined; }; - var registeredPointer = new RegisteredPointer(Handle, pointeeType.isPolymorphic, true, rawGetPointee, rawConstructor, rawDestructor); + var registeredPointer = new RegisteredPointer( + name, + pointeeType.registeredClass, + Handle, + true, + rawGetPointee, + rawConstructor, + rawDestructor); registeredPointer.pointeeType = pointeeType; - registeredPointer.name = name; - registerType(rawType, name, registeredPointer); + registerType(rawType, registeredPointer); pointeeType.smartPointerType = registeredPointer; } @@ -794,20 +806,17 @@ function __embind_register_class( Handle.memberType = {}; // todo: clean this up! - var registeredClass = new RegisteredPointer(Handle, isPolymorphic, false); - registeredClass.name = name; + var registeredClass = new RegisteredPointer(name, registeredClass, Handle, false); var type = registeredClass; - registerType(rawType, name, registeredClass); + registerType(rawType, registeredClass); registeredClass.pointeeType = registeredClass; - var registeredClass = new RegisteredPointer(Handle, isPolymorphic, false); - registeredClass.name = name; - registerType(rawPointerType, name + '*', registeredClass); + var registeredClass = new RegisteredPointer(name + '*', registeredClass, Handle, false); + registerType(rawPointerType, registeredClass); registeredClass.pointeeType = type; // todo: implement const pointers (no modification Javascript side) - var registeredClass = new RegisteredPointer(Handle, isPolymorphic, false); - registeredClass.name = name; - registerType(rawConstPointerType, name + ' const*', registeredClass); + var registeredClass = new RegisteredPointer(name + ' const*', registeredClass, Handle, false); + registerType(rawConstPointerType, registeredClass); registeredClass.pointeeType = type; type.constructor = createNamedFunction(type.name, function() { @@ -971,7 +980,7 @@ function __embind_register_enum( } constructor.values = {}; - registerType(rawType, name, { + registerType(rawType, { name: name, constructor: constructor, fromWireType: function(c) { @@ -1012,7 +1021,7 @@ function __embind_register_interface( rawConstructor = FUNCTION_TABLE[rawConstructor]; rawDestructor = FUNCTION_TABLE[rawDestructor]; - registerType(rawType, name, { + registerType(rawType, { name: name, rawConstructor: rawConstructor, rawDestructor: rawDestructor, From 32e54a09d2ecaf8543a5010b2f00a5ae677118b3 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Tue, 5 Mar 2013 23:56:25 -0800 Subject: [PATCH 157/258] More simplifications --- src/embind/embind.js | 52 +++++++++++++++++++++++++++----------------- 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 629e42ff4..393319d30 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -193,7 +193,10 @@ function requireRegisteredType(rawType, humanName) { } function typeName(rawType) { - return Pointer_stringify(___typeName(rawType)); + var bt = ___typeName(rawType); + var rv = Pointer_stringify(bt); + _free(bt); + return rv; } function heap32VectorToArray(count, firstElement) { @@ -222,7 +225,7 @@ function staticPointerCast(from, fromType, toType) { } var to = ___staticPointerCast(from, fromType, toType); if (to <= 0) { - throw new CastError("Pointer conversion is not available"); + throw new CastError("Pointer conversion from " + typeName(fromType) + " to " + typeName(toType) + " is not available"); } return to; } @@ -546,9 +549,10 @@ function __embind_register_struct_field( }); } -function RegisteredPointer(name, registeredClass, Handle, isSmartPointer, rawGetPointee, rawConstructor, rawDestructor) { +function RegisteredPointer(name, registeredClass, pointeeType, Handle, isSmartPointer, rawGetPointee, rawConstructor, rawDestructor) { this.name = name; this.registeredClass = registeredClass; + this.pointeeType = pointeeType; this.Handle = Handle; // <-- I think I can kill this this.isSmartPointer = isSmartPointer; this.rawGetPointee = rawGetPointee; @@ -571,10 +575,11 @@ RegisteredPointer.prototype.toWireType = function(destructors, handle) { if (this.isSmartPointer && undefined === handle.$$.smartPtr) { throwBindingError('Passing raw pointer to smart pointer is illegal'); } - if (handle.$$.pointeeType.isPolymorphic()) { - fromRawType = handle.$$.pointeeType.getDynamicRawPointerType(handle.$$.ptr); + var pointeeType = handle.$$.pointeeType; + if (pointeeType.isPolymorphic()) { + fromRawType = pointeeType.getDynamicRawPointerType(handle.$$.ptr); } else { - fromRawType = handle.$$.pointeeType.rawType; + fromRawType = pointeeType.rawType; } if (fromRawType === this.pointeeType.rawType) { return this.isSmartPointer ? handle.$$.smartPtr : handle.$$.ptr; @@ -585,10 +590,10 @@ RegisteredPointer.prototype.toWireType = function(destructors, handle) { // assumes that smart_ptr has an identical binary layout to // smart_ptr. I wonder if that's untrue for any common // smart pointer. - chad - ptr = handle.$$.pointeeType.smartPointerType.rawConstructor( + ptr = pointeeType.smartPointerType.rawConstructor( ptr, handle.$$.smartPtr); - destructors.push(handle.$$.pointeeType.smartPointerType.rawDestructor); + destructors.push(pointeeType.smartPointerType.rawDestructor); destructors.push(ptr); } return ptr; @@ -724,15 +729,16 @@ function __embind_register_smart_ptr( this.$$.smartPtr = undefined; this.$$.ptr = undefined; }; + var registeredPointer = new RegisteredPointer( name, pointeeType.registeredClass, + pointeeType, Handle, true, rawGetPointee, rawConstructor, rawDestructor); - registeredPointer.pointeeType = pointeeType; registerType(rawType, registeredPointer); pointeeType.smartPointerType = registeredPointer; } @@ -806,20 +812,26 @@ function __embind_register_class( Handle.memberType = {}; // todo: clean this up! - var registeredClass = new RegisteredPointer(name, registeredClass, Handle, false); - var type = registeredClass; - registerType(rawType, registeredClass); - registeredClass.pointeeType = registeredClass; + var type = new RegisteredPointer(name, registeredClass, undefined, Handle, false); + type.pointeeType = type; + registerType(rawType, type); + + registerType(rawPointerType, new RegisteredPointer( + name + '*', + registeredClass, + type, + Handle, + false)); - var registeredClass = new RegisteredPointer(name + '*', registeredClass, Handle, false); - registerType(rawPointerType, registeredClass); - registeredClass.pointeeType = type; // todo: implement const pointers (no modification Javascript side) - var registeredClass = new RegisteredPointer(name + ' const*', registeredClass, Handle, false); - registerType(rawConstPointerType, registeredClass); - registeredClass.pointeeType = type; + registerType(rawConstPointerType, new RegisteredPointer( + name + ' const*', + registeredClass, + type, + Handle, + false)); - type.constructor = createNamedFunction(type.name, function() { + type.constructor = createNamedFunction(name, function() { if (Object.getPrototypeOf(this) !== Handle.prototype) { throw new BindingError("Use 'new' to construct " + name); } From c1114a970b3eb8cb022aad9345289c81279c3c38 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Wed, 6 Mar 2013 00:16:55 -0800 Subject: [PATCH 158/258] registerType doesn't modify the type converter anymore --- src/embind/embind.js | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 393319d30..447bea1b0 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -115,6 +115,9 @@ function createInheritedFunctionOrProperty(name, type, nameInBaseClass, baseClas } function collectRegisteredBaseClasses(rawType) { + if (undefined === rawType) { + return []; + } var rawBaseTypes = Module.__getBaseClasses(rawType); var baseTypes = []; for (var i = 0; i < rawBaseTypes.size(); i++) { @@ -180,7 +183,6 @@ function registerType(rawType, registeredInstance) { if (typeRegistry.hasOwnProperty(rawType)) { throwBindingError("Cannot register type '" + name + "' twice"); } - registeredInstance.rawType = rawType; typeRegistry[rawType] = registeredInstance; } @@ -549,8 +551,9 @@ function __embind_register_struct_field( }); } -function RegisteredPointer(name, registeredClass, pointeeType, Handle, isSmartPointer, rawGetPointee, rawConstructor, rawDestructor) { +function RegisteredPointer(name, rawType, registeredClass, pointeeType, Handle, isSmartPointer, rawGetPointee, rawConstructor, rawDestructor) { this.name = name; + this.rawType = rawType; this.registeredClass = registeredClass; this.pointeeType = pointeeType; this.Handle = Handle; // <-- I think I can kill this @@ -732,6 +735,7 @@ function __embind_register_smart_ptr( var registeredPointer = new RegisteredPointer( name, + rawType, pointeeType.registeredClass, pointeeType, Handle, @@ -812,12 +816,19 @@ function __embind_register_class( Handle.memberType = {}; // todo: clean this up! - var type = new RegisteredPointer(name, registeredClass, undefined, Handle, false); - type.pointeeType = type; + var type = new RegisteredPointer( + name, + rawType, + registeredClass, + undefined, + Handle, + false); + type.pointeeType = type; // :( registerType(rawType, type); registerType(rawPointerType, new RegisteredPointer( name + '*', + rawPointerType, registeredClass, type, Handle, @@ -826,6 +837,7 @@ function __embind_register_class( // todo: implement const pointers (no modification Javascript side) registerType(rawConstPointerType, new RegisteredPointer( name + ' const*', + rawConstPointerType, registeredClass, type, Handle, From 8be7ebc9ccc6281a9e7481340d28255567608920 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Wed, 6 Mar 2013 03:17:51 -0800 Subject: [PATCH 159/258] Explicitly specify base classes. Alas, but it must be done for instanceof :( --- src/embind/embind.js | 17 ++++++++++------ system/include/emscripten/bind.h | 34 ++++++++++++++++++++++++++++---- system/lib/embind/bind.cpp | 10 ---------- 3 files changed, 41 insertions(+), 20 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 447bea1b0..b9507d5f8 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -69,6 +69,7 @@ function _embind_repr(v) { var typeRegistry = {}; var deferredRegistrations = []; +var baseClasses = {}; // rawType -> rawBaseType function requestDeferredRegistration(registrationFunction) { deferredRegistrations.push(registrationFunction); @@ -118,10 +119,10 @@ function collectRegisteredBaseClasses(rawType) { if (undefined === rawType) { return []; } - var rawBaseTypes = Module.__getBaseClasses(rawType); + var rawBaseTypes = baseClasses[rawType] || []; var baseTypes = []; - for (var i = 0; i < rawBaseTypes.size(); i++) { - var baseType = typeRegistry[rawBaseTypes.get(i)]; + for (var i = 0; i < rawBaseTypes.length; i++) { + var baseType = typeRegistry[rawBaseTypes[i]]; if (baseType) { baseTypes.push(baseType); } else { @@ -750,7 +751,7 @@ function __embind_register_smart_ptr( function ClassHandle() { } -function RegisteredClass(name, isPolymorphic, baseClass) { +function RegisteredClass(name, isPolymorphic, baseClassRawType) { this.name = name; this.isPolymorphic = isPolymorphic; } @@ -760,7 +761,7 @@ function __embind_register_class( rawType, rawPointerType, rawConstPointerType, - baseClassType, + baseClassRawType, isPolymorphic, name, rawDestructor @@ -768,7 +769,11 @@ function __embind_register_class( name = Pointer_stringify(name); rawDestructor = FUNCTION_TABLE[rawDestructor]; - var registeredClass = new RegisteredClass(name, isPolymorphic); + if (baseClassRawType) { + baseClasses[rawType] = [baseClassRawType]; + } + + var registeredClass = new RegisteredClass(name, isPolymorphic, baseClassRawType); var Handle = createNamedFunction(name, function(ptr) { Object.defineProperty(this, '$$', { diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index cbc5cb8e9..24a7bf71e 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -611,19 +611,45 @@ namespace emscripten { #define EMSCRIPTEN_WRAPPER(T) \ T(const ::emscripten::val& v): wrapper(v) {} - // TODO: support base class - template + namespace internal { + struct NoBaseClass { + static TYPEID get() { + return 0; + } + + template + static void verify() { + } + }; + } + + template + struct base { + static internal::TYPEID get() { + return internal::TypeID::get(); + } + + template + static void verify() { + static_assert(!std::is_same::value, "Base must not have same type as class"); + static_assert(std::is_base_of::value, "Derived class must derive from base"); + } + }; + + template class class_ { public: class_(const char* name) { using namespace internal; + BaseSpecifier::template verify(); + registerStandardTypes(); _embind_register_class( TypeID::get(), TypeID>::get(), TypeID>::get(), - 0, + BaseSpecifier::get(), std::is_polymorphic::value, name, reinterpret_cast(&raw_destructor)); @@ -672,7 +698,7 @@ namespace emscripten { using namespace internal; // TODO: unique or anonymous name - class_("WrapperType") + class_>("WrapperType") .template constructor() ; diff --git a/system/lib/embind/bind.cpp b/system/lib/embind/bind.cpp index 081db2cdd..cbff7cf1d 100755 --- a/system/lib/embind/bind.cpp +++ b/system/lib/embind/bind.cpp @@ -133,15 +133,6 @@ namespace emscripten { return derivationPath; } - std::vector __getBaseClasses(int tp) { - std::vector baseTypes = __internalGetBaseClasses((const __cxxabiv1::__class_type_info*)tp); - std::vector bases; - for (int j = 0; j < baseTypes.size(); j++) { - bases.emplace_back((int)baseTypes[j]); - } - return bases; - } - extern "C" { // These three routines constitute an extension to the compiler's support for dynamic type conversion. // They are used by embind.js to implement automatic downcasting of return values which are pointers to @@ -229,7 +220,6 @@ namespace emscripten { // conversion for the return value. This has the unfortunate side-effect of exposing it to third party // developers, but perhaps the double underscore will scare them away from calling it. function("__getDerivationPath", &__getDerivationPath); - function("__getBaseClasses", &__getBaseClasses); function("__peek32", &__peek32, allow_raw_pointers()); })); } From 3f44445f45aae6b9014985d352dfb74674728881 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Wed, 6 Mar 2013 03:38:28 -0800 Subject: [PATCH 160/258] further simplifications --- src/embind/embind.js | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index b9507d5f8..3f10d886e 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -115,28 +115,28 @@ function createInheritedFunctionOrProperty(name, type, nameInBaseClass, baseClas } } -function collectRegisteredBaseClasses(rawType) { +function collectRegisteredBaseClasses(type) { + var rawType = type.rawType; if (undefined === rawType) { return []; } - var rawBaseTypes = baseClasses[rawType] || []; - var baseTypes = []; - for (var i = 0; i < rawBaseTypes.length; i++) { - var baseType = typeRegistry[rawBaseTypes[i]]; - if (baseType) { - baseTypes.push(baseType); - } else { - baseTypes = baseTypes.concat(collectRegisteredBaseClasses(rawBaseTypes.get(i))); - } + var rawBaseType = baseClasses[rawType]; + if (!rawBaseType) { + return []; + } + var baseType = typeRegistry[rawBaseType]; + if (baseType) { + return [baseType]; + } else { + return collectRegisteredBaseClasses(baseType); } - return baseTypes; } function resolveType(type) { if (!type.resolved) { var baseClassType, name, baseProto; var inheritedNames = {}; - var baseTypes = collectRegisteredBaseClasses(type.rawType); + var baseTypes = collectRegisteredBaseClasses(type); for (var i = 0; i < baseTypes.length; i++) { var baseType = baseTypes[i]; resolveType(baseType); @@ -754,6 +754,7 @@ function ClassHandle() { function RegisteredClass(name, isPolymorphic, baseClassRawType) { this.name = name; this.isPolymorphic = isPolymorphic; + this.baseClassRawType = baseClassRawType; } // TODO: null pointers are always zero (not a Handle) in Javascript @@ -770,7 +771,7 @@ function __embind_register_class( rawDestructor = FUNCTION_TABLE[rawDestructor]; if (baseClassRawType) { - baseClasses[rawType] = [baseClassRawType]; + baseClasses[rawType] = baseClassRawType; } var registeredClass = new RegisteredClass(name, isPolymorphic, baseClassRawType); From ec62a534e990b20a3ff85cf7aa6da96e6f41e446 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Wed, 6 Mar 2013 03:44:42 -0800 Subject: [PATCH 161/258] Remove support for base class method/field name disambiguation: we don't use it yet and I have a method that will match the convention of JavaScript code --- src/embind/embind.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 3f10d886e..4086e2a50 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -155,11 +155,6 @@ function resolveType(type) { if (!type.Handle.prototype.hasOwnProperty(name) && inheritedNames[name].length === 1) { baseClassType = inheritedNames[name][0]; createInheritedFunctionOrProperty(name, type, name, baseClassType); - } else { - for (var j = 0; j < inheritedNames[name].length; j++) { - baseClassType = inheritedNames[name][j]; - createInheritedFunctionOrProperty(baseClassType.name+"_"+name, type, name, baseClassType); - } } } } From ba6204bd5ca458a3999b5f6f056b267bb1d92fed Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Wed, 6 Mar 2013 14:21:43 -0800 Subject: [PATCH 162/258] Rewrite how dependent type registrations are implemented. This reduces code size both in JS and C++. There are some known bugs. I'll get to those soon. --- src/embind/embind.js | 236 ++++++++++++------------------- system/include/emscripten/bind.h | 50 +++++-- system/lib/embind/bind.cpp | 47 +++--- 3 files changed, 149 insertions(+), 184 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 4086e2a50..f970317e8 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -67,127 +67,58 @@ function _embind_repr(v) { } } -var typeRegistry = {}; -var deferredRegistrations = []; var baseClasses = {}; // rawType -> rawBaseType -function requestDeferredRegistration(registrationFunction) { - deferredRegistrations.push(registrationFunction); -} +// typeID -> { toWireType: ..., fromWireType: ... } +var registeredTypes = {}; -function performDeferredRegistrations(){ - while(deferredRegistrations.length > 0) { - var registrationFunction = deferredRegistrations.shift(); - registrationFunction(); - } -} - -function createInheritedFunctionOrProperty(name, type, nameInBaseClass, baseClassType) { - function upcastingWrapper(method) { - return function() { - var baseClassPtr = ___staticPointerCast(this.$$.ptr, type.rawType, baseClassType.rawType); - if (baseClassPtr === this.$$.ptr) { - return method.apply(this, arguments); - } else { - var handle = this.clone(); - try { - handle.$$.ptr = baseClassPtr; - return method.apply(handle, arguments); - } finally { - handle.delete(); - } - } - }; - } - var baseClassPrototype = baseClassType.Handle.prototype; - if (baseClassPrototype.constructor.memberType[nameInBaseClass] === 'field') { - var baseClassDescriptor = Object.getOwnPropertyDescriptor(baseClassPrototype, nameInBaseClass); - Object.defineProperty(type.Handle.prototype, name, { - enumerable: true, - get: upcastingWrapper(baseClassDescriptor.get), - set: upcastingWrapper(baseClassDescriptor.set) - }); - type.Handle.memberType[name] = 'field'; - } else if (baseClassPrototype.constructor.memberType[nameInBaseClass] === 'method') { - var baseClassMethod = baseClassPrototype[nameInBaseClass]; - type.Handle.prototype[name] = createNamedFunction(name, upcastingWrapper(baseClassMethod)); - type.Handle.memberType[name] = 'method'; - } -} - -function collectRegisteredBaseClasses(type) { - var rawType = type.rawType; - if (undefined === rawType) { - return []; - } - var rawBaseType = baseClasses[rawType]; - if (!rawBaseType) { - return []; - } - var baseType = typeRegistry[rawBaseType]; - if (baseType) { - return [baseType]; - } else { - return collectRegisteredBaseClasses(baseType); - } -} - -function resolveType(type) { - if (!type.resolved) { - var baseClassType, name, baseProto; - var inheritedNames = {}; - var baseTypes = collectRegisteredBaseClasses(type); - for (var i = 0; i < baseTypes.length; i++) { - var baseType = baseTypes[i]; - resolveType(baseType); - baseProto = baseType.Handle.prototype; - for (name in baseProto) { - if (baseProto.hasOwnProperty(name) && baseType.Handle.memberType[name]) { - if (!(name in inheritedNames)) { - inheritedNames[name] = []; - } - inheritedNames[name].push(baseType); - } - } - } - for (name in inheritedNames) { - if (inheritedNames.hasOwnProperty(name)) { - if (!type.Handle.prototype.hasOwnProperty(name) && inheritedNames[name].length === 1) { - baseClassType = inheritedNames[name][0]; - createInheritedFunctionOrProperty(name, type, name, baseClassType); - } - } - } - type.resolved = true; - } -} - -function resolveBindings() { - performDeferredRegistrations(); - for (var rawType in typeRegistry) { - if (typeRegistry.hasOwnProperty(rawType)) { - resolveType(typeRegistry[rawType]); - } - } -} +// typeID -> [callback] +var awaitingDependencies = {}; function registerType(rawType, registeredInstance) { var name = registeredInstance.name; if (!rawType) { throwBindingError('type "' + name + '" must have a positive integer typeid pointer'); } - if (typeRegistry.hasOwnProperty(rawType)) { + if (registeredTypes.hasOwnProperty(rawType)) { throwBindingError("Cannot register type '" + name + "' twice"); } - typeRegistry[rawType] = registeredInstance; + + registeredTypes[rawType] = registeredInstance; + + if (awaitingDependencies.hasOwnProperty(rawType)) { + var callbacks = awaitingDependencies[rawType]; + delete awaitingDependencies[rawType]; + callbacks.forEach(function(cb) { + cb(); + }); + } } -function requireRegisteredType(rawType, humanName) { - var impl = typeRegistry[rawType]; - if (undefined === impl) { - throwBindingError(humanName + " has unknown type " + typeName(rawType)); +function whenDependentTypesAreResolved(dependentTypes, onComplete) { + var typeConverters = new Array(dependentTypes.length); + var unregisteredTypes = []; + var registered = 0; + dependentTypes.forEach(function(dt, i) { + if (registeredTypes.hasOwnProperty(dt)) { + typeConverters[i] = registeredTypes[dt]; + } else { + unregisteredTypes.push(dt); + if (!awaitingDependencies.hasOwnProperty(dt)) { + awaitingDependencies[dt] = []; + } + awaitingDependencies[dt].push(function() { + typeConverters[i] = registeredTypes[dt]; + ++registered; + if (registered === unregisteredTypes.length) { + onComplete(typeConverters); + } + }); + } + }); + if (0 === unregisteredTypes.length) { + onComplete(typeConverters); } - return impl; } function typeName(rawType) { @@ -205,17 +136,27 @@ function heap32VectorToArray(count, firstElement) { return array; } +function requireRegisteredType(rawType, humanName) { + var impl = registeredTypes[rawType]; + if (undefined === impl) { + throwBindingError(humanName + " has unknown type " + typeName(rawType)); + } + return impl; +} + +/* function requireArgumentTypes(rawArgTypes, name) { var argTypes = []; for (var i = 0; i < rawArgTypes.length; ++i) { - if (i === 0) { - argTypes[i] = requireRegisteredType(rawArgTypes[i], name + " return value"); - } else { - argTypes[i] = requireRegisteredType(rawArgTypes[i], name + " parameter " + i); - } + argTypes[i] = requireRegisteredType( + rawArgTypes[i], + i === 0 ? + name + " return value" : + name + " parameter " + i); } return argTypes; } +*/ function staticPointerCast(from, fromType, toType) { if (!from) { @@ -362,11 +303,10 @@ function makeInvoker(name, argCount, argTypes, invoker, fn) { } function __embind_register_function(name, argCount, rawArgTypesAddr, rawInvoker, fn) { - var rawArgTypes = heap32VectorToArray(argCount, rawArgTypesAddr); + var argTypes = heap32VectorToArray(argCount, rawArgTypesAddr); name = Pointer_stringify(name); rawInvoker = FUNCTION_TABLE[rawInvoker]; - requestDeferredRegistration(function() { - var argTypes = requireArgumentTypes(rawArgTypes, name); + whenDependentTypesAreResolved(argTypes, function(argTypes) { exposePublicSymbol(name, makeInvoker(name, argCount, argTypes, rawInvoker, fn)); }); } @@ -425,10 +365,11 @@ function __embind_register_tuple_element( getter = FUNCTION_TABLE[getter]; setter = FUNCTION_TABLE[setter]; memberPointer = copyMemberPointer(memberPointer, memberPointerSize); + var tupleType = requireRegisteredType(rawTupleType, 'tuple'); + // TODO: this could register elements out of order - requestDeferredRegistration(function() { - var tupleType = requireRegisteredType(rawTupleType, 'tuple'); - var type = requireRegisteredType(rawType, "element " + tupleType.name + "[" + tupleType.elements.length + "]"); + whenDependentTypesAreResolved([rawType], function(type) { + type = type[0]; tupleType.elements.push({ read: function(ptr) { return type.fromWireType(getter(ptr, memberPointer)); @@ -452,13 +393,14 @@ function __embind_register_tuple_element_accessor( setterSize, setter ) { + var tupleType = requireRegisteredType(rawTupleType, 'tuple'); rawStaticGetter = FUNCTION_TABLE[rawStaticGetter]; getter = copyMemberPointer(getter, getterSize); rawStaticSetter = FUNCTION_TABLE[rawStaticSetter]; setter = copyMemberPointer(setter, setterSize); - requestDeferredRegistration(function() { - var tupleType = requireRegisteredType(rawTupleType, 'tuple'); - var elementType = requireRegisteredType(rawElementType, "element " + tupleType.name + "[" + tupleType.elements.length + "]"); + + whenDependentTypesAreResolved([rawElementType], function(elementType) { + elementType = elementType[0]; tupleType.elements.push({ read: function(ptr) { return elementType.fromWireType(rawStaticGetter(ptr, HEAP32[getter >> 2])); @@ -526,14 +468,14 @@ function __embind_register_struct_field( memberPointerSize, memberPointer ) { + var structType = requireRegisteredType(rawStructType, 'struct'); fieldName = Pointer_stringify(fieldName); rawGetter = FUNCTION_TABLE[rawGetter]; rawSetter = FUNCTION_TABLE[rawSetter]; memberPointer = copyMemberPointer(memberPointer, memberPointerSize); // TODO: this could register elements out of order - requestDeferredRegistration(function() { - var structType = requireRegisteredType(rawStructType, 'struct'); - var fieldType = requireRegisteredType(rawFieldType, 'field "' + structType.name + '.' + fieldName + '"'); + whenDependentTypesAreResolved([rawFieldType], function(fieldType) { + fieldType = fieldType[0]; structType.fields[fieldName] = { read: function(ptr) { return fieldType.fromWireType(rawGetter(ptr, memberPointer)); @@ -638,7 +580,7 @@ RegisteredPointer.prototype.getDynamicDowncastType = function(ptr) { if (type && type !== this.pointeeType.rawType) { var derivation = Module.__getDerivationPath(type, this.pointeeType.rawType); for (var i = 0; i < derivation.size(); i++) { - downcastType = typeRegistry[derivation.get(i)]; + downcastType = registeredTypes[derivation.get(i)]; if (downcastType && (!this.isSmartPointer || downcastType.smartPointerType)) { break; } @@ -758,18 +700,34 @@ function __embind_register_class( rawPointerType, rawConstPointerType, baseClassRawType, + upcast, + downcast, isPolymorphic, name, rawDestructor ) { name = Pointer_stringify(name); rawDestructor = FUNCTION_TABLE[rawDestructor]; + upcast = FUNCTION_TABLE[upcast]; + downcast = FUNCTION_TABLE[downcast]; + var basePrototype; if (baseClassRawType) { baseClasses[rawType] = baseClassRawType; + + // TODO: allow registration of base after derived + var base = requireRegisteredType(baseClassRawType, 'base class'); + basePrototype = base.Handle.prototype; + } else { + basePrototype = ClassHandle.prototype; } - var registeredClass = new RegisteredClass(name, isPolymorphic, baseClassRawType); + var registeredClass = new RegisteredClass( + name, + isPolymorphic, + baseClassRawType, + upcast, + downcast); var Handle = createNamedFunction(name, function(ptr) { Object.defineProperty(this, '$$', { @@ -781,7 +739,7 @@ function __embind_register_class( }); }); - Handle.prototype = Object.create(ClassHandle.prototype, { + Handle.prototype = Object.create(basePrototype, { constructor: { value: Handle }, }); Handle.prototype.clone = function() { @@ -814,7 +772,6 @@ function __embind_register_class( } this.$$.ptr = undefined; }; - Handle.memberType = {}; // todo: clean this up! var type = new RegisteredPointer( @@ -867,13 +824,12 @@ function __embind_register_class_constructor( invoker, rawConstructor ) { + var classType = requireRegisteredType(rawClassType, 'class'); var rawArgTypes = heap32VectorToArray(argCount, rawArgTypesAddr); invoker = FUNCTION_TABLE[invoker]; - requestDeferredRegistration(function() { - var classType = requireRegisteredType(rawClassType, 'class'); + whenDependentTypesAreResolved(rawArgTypes, function(argTypes) { var humanName = 'constructor ' + classType.name; - var argTypes = requireArgumentTypes(rawArgTypes, humanName); classType.constructor.body = function() { if (arguments.length !== argCount - 1) { throwBindingError(humanName + ' called with ' + arguments.length + ' arguments, expected ' + (argCount-1)); @@ -902,14 +858,13 @@ function __embind_register_class_method( memberFunctionSize, memberFunction ) { + var classType = requireRegisteredType(rawClassType, 'class'); var rawArgTypes = heap32VectorToArray(argCount, rawArgTypesAddr); methodName = Pointer_stringify(methodName); rawInvoker = FUNCTION_TABLE[rawInvoker]; memberFunction = copyMemberPointer(memberFunction, memberFunctionSize); - requestDeferredRegistration(function() { - var classType = requireRegisteredType(rawClassType, 'class'); + whenDependentTypesAreResolved(rawArgTypes, function(argTypes) { var humanName = classType.name + '.' + methodName; - var argTypes = requireArgumentTypes(rawArgTypes, 'method ' + humanName); classType.Handle.prototype[methodName] = function() { if (!this.$$.ptr) { throwBindingError('cannot call emscripten binding method ' + humanName + ' on deleted object'); @@ -934,7 +889,6 @@ function __embind_register_class_method( runDestructors(destructors); return rv; }; - classType.Handle.memberType[methodName] = "method"; }); } @@ -946,13 +900,12 @@ function __embind_register_class_classmethod( rawInvoker, fn ) { + var classType = requireRegisteredType(rawClassType, 'class'); var rawArgTypes = heap32VectorToArray(argCount, rawArgTypesAddr); methodName = Pointer_stringify(methodName); rawInvoker = FUNCTION_TABLE[rawInvoker]; - requestDeferredRegistration(function() { - var classType = requireRegisteredType(rawClassType, 'class'); + whenDependentTypesAreResolved(rawArgTypes, function(argTypes) { var humanName = classType.name + '.' + methodName; - var argTypes = requireArgumentTypes(rawArgTypes, 'classmethod ' + humanName); classType.constructor[methodName] = makeInvoker(humanName, argCount, argTypes, rawInvoker, fn); }); } @@ -966,14 +919,14 @@ function __embind_register_class_field( memberPointerSize, memberPointer ) { + var classType = requireRegisteredType(rawClassType, 'class'); fieldName = Pointer_stringify(fieldName); getter = FUNCTION_TABLE[getter]; setter = FUNCTION_TABLE[setter]; memberPointer = copyMemberPointer(memberPointer, memberPointerSize); - requestDeferredRegistration(function() { - var classType = requireRegisteredType(rawClassType, 'class'); + whenDependentTypesAreResolved([rawFieldType], function(fieldType) { var humanName = classType.name + '.' + fieldName; - var fieldType = requireRegisteredType(rawFieldType, 'field ' + humanName); + fieldType = fieldType[0]; Object.defineProperty(classType.Handle.prototype, fieldName, { get: function() { if (!this.$$.ptr) { @@ -991,7 +944,6 @@ function __embind_register_class_field( }, enumerable: true }); - classType.Handle.memberType[fieldName] = "field"; }); } diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index 24a7bf71e..e8cedbe49 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -106,6 +106,8 @@ namespace emscripten { TYPEID pointerType, TYPEID constPointerType, TYPEID baseClassType, + GenericFunction upcast, + GenericFunction downcast, bool isPolymorphic, const char* className, GenericFunction destructor); @@ -159,8 +161,6 @@ namespace emscripten { GenericFunction destructor); } - extern void registerStandardTypes(); - class BindingsDefinition { public: template @@ -248,9 +248,6 @@ namespace emscripten { template void function(const char* name, ReturnType (*fn)(Args...), Policies...) { using namespace internal; - - registerStandardTypes(); - typename WithPolicies::template ArgTypeList args; _embind_register_function( name, @@ -415,7 +412,6 @@ namespace emscripten { class value_tuple { public: value_tuple(const char* name) { - internal::registerStandardTypes(); internal::_embind_register_tuple( internal::TypeID::get(), name, @@ -501,7 +497,6 @@ namespace emscripten { class value_struct { public: value_struct(const char* name) { - internal::registerStandardTypes(); internal::_embind_register_struct( internal::TypeID::get(), name, @@ -562,7 +557,6 @@ namespace emscripten { using namespace internal; typedef typename smart_ptr_trait::element_type PointeeType; - registerStandardTypes(); _embind_register_smart_ptr( TypeID::get(), TypeID::get(), @@ -613,27 +607,52 @@ namespace emscripten { namespace internal { struct NoBaseClass { + template + static void verify() { + } + static TYPEID get() { - return 0; + return nullptr; } template - static void verify() { + static GenericFunction getUpcaster() { + return nullptr; + } + + template + static GenericFunction getDowncaster() { + return nullptr; } }; } template struct base { - static internal::TYPEID get() { - return internal::TypeID::get(); - } - template static void verify() { static_assert(!std::is_same::value, "Base must not have same type as class"); static_assert(std::is_base_of::value, "Derived class must derive from base"); } + + static internal::TYPEID get() { + return internal::TypeID::get(); + } + + template + static internal::GenericFunction getUpcaster() { + return reinterpret_cast(&convertPointer); + } + + template + static internal::GenericFunction getDowncaster() { + return reinterpret_cast(&convertPointer); + } + + template + static To* convertPointer(From* ptr) { + return static_cast(ptr); + } }; template @@ -644,12 +663,13 @@ namespace emscripten { BaseSpecifier::template verify(); - registerStandardTypes(); _embind_register_class( TypeID::get(), TypeID>::get(), TypeID>::get(), BaseSpecifier::get(), + BaseSpecifier::template getUpcaster(), + BaseSpecifier::template getDowncaster(), std::is_polymorphic::value, name, reinterpret_cast(&raw_destructor)); diff --git a/system/lib/embind/bind.cpp b/system/lib/embind/bind.cpp index cbff7cf1d..6c792f787 100755 --- a/system/lib/embind/bind.cpp +++ b/system/lib/embind/bind.cpp @@ -66,33 +66,6 @@ namespace __cxxabiv1 { namespace emscripten { namespace internal { - void registerStandardTypes() { - static bool first = true; - if (first) { - first = false; - - _embind_register_void(TypeID::get(), "void"); - - _embind_register_bool(TypeID::get(), "bool", true, false); - - _embind_register_integer(TypeID::get(), "char"); - _embind_register_integer(TypeID::get(), "signed char"); - _embind_register_integer(TypeID::get(), "unsigned char"); - _embind_register_integer(TypeID::get(), "short"); - _embind_register_integer(TypeID::get(), "unsigned short"); - _embind_register_integer(TypeID::get(), "int"); - _embind_register_integer(TypeID::get(), "unsigned int"); - _embind_register_integer(TypeID::get(), "long"); - _embind_register_integer(TypeID::get(), "unsigned long"); - - _embind_register_float(TypeID::get(), "float"); - _embind_register_float(TypeID::get(), "double"); - - _embind_register_cstring(TypeID::get(), "std::string"); - _embind_register_emval(TypeID::get(), "emscripten::val"); - } - } - // __getDerivationPath returns an array of type_info pointers describing the derivation chain starting with // the derived type and proceeding toward (and ending with) the base type. Types are only included if they // appear on all possible derivation paths. @@ -216,6 +189,26 @@ namespace emscripten { } EMSCRIPTEN_BINDINGS(([]() { + _embind_register_void(TypeID::get(), "void"); + + _embind_register_bool(TypeID::get(), "bool", true, false); + + _embind_register_integer(TypeID::get(), "char"); + _embind_register_integer(TypeID::get(), "signed char"); + _embind_register_integer(TypeID::get(), "unsigned char"); + _embind_register_integer(TypeID::get(), "short"); + _embind_register_integer(TypeID::get(), "unsigned short"); + _embind_register_integer(TypeID::get(), "int"); + _embind_register_integer(TypeID::get(), "unsigned int"); + _embind_register_integer(TypeID::get(), "long"); + _embind_register_integer(TypeID::get(), "unsigned long"); + + _embind_register_float(TypeID::get(), "float"); + _embind_register_float(TypeID::get(), "double"); + + _embind_register_cstring(TypeID::get(), "std::string"); + _embind_register_emval(TypeID::get(), "emscripten::val"); + // We bind __getDerivationPath in order to take advantage of the std::vector to Javascript array // conversion for the return value. This has the unfortunate side-effect of exposing it to third party // developers, but perhaps the double underscore will scare them away from calling it. From 4f18c7eed89ad5f6b77c153f82618c8fe918c3c8 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Wed, 6 Mar 2013 19:16:19 -0800 Subject: [PATCH 163/258] test and fix for calling base class methods with pointer fixups --- src/embind/embind.js | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index f970317e8..696b0c988 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -688,10 +688,20 @@ function __embind_register_smart_ptr( function ClassHandle() { } -function RegisteredClass(name, isPolymorphic, baseClassRawType) { +function RegisteredClass( + name, + isPolymorphic, + baseClassRawType, + baseClass, + upcast, + downcast +) { this.name = name; this.isPolymorphic = isPolymorphic; this.baseClassRawType = baseClassRawType; + this.baseClass = baseClass; + this.upcast = upcast; + this.downcast = downcast; } // TODO: null pointers are always zero (not a Handle) in Javascript @@ -711,12 +721,15 @@ function __embind_register_class( upcast = FUNCTION_TABLE[upcast]; downcast = FUNCTION_TABLE[downcast]; + var baseClass; var basePrototype; + var depth; if (baseClassRawType) { baseClasses[rawType] = baseClassRawType; // TODO: allow registration of base after derived var base = requireRegisteredType(baseClassRawType, 'base class'); + baseClass = base.registeredClass; basePrototype = base.Handle.prototype; } else { basePrototype = ClassHandle.prototype; @@ -726,6 +739,7 @@ function __embind_register_class( name, isPolymorphic, baseClassRawType, + baseClass, upcast, downcast); @@ -849,6 +863,14 @@ function __embind_register_class_constructor( }); } +function upcastPointer(ptr, ptrClass, desiredClass) { + while (ptrClass !== desiredClass) { + ptr = ptrClass.upcast(ptr); + ptrClass = ptrClass.baseClass; + } + return ptr; +} + function __embind_register_class_method( rawClassType, methodName, @@ -873,9 +895,15 @@ function __embind_register_class_method( throwBindingError('emscripten binding method ' + humanName + ' called with ' + arguments.length + ' arguments, expected ' + (argCount-1)); } + // TODO: error if pointer type doesn't match signature + var ptr = upcastPointer( + this.$$.ptr, + this.$$.pointeeType.registeredClass, + classType.registeredClass); + var destructors = []; var args = new Array(argCount + 1); - args[0] = this.$$.ptr; + args[0] = ptr; args[1] = memberFunction; for (var i = 1; i < argCount; ++i) { args[i + 1] = argTypes[i].toWireType(destructors, arguments[i - 1]); @@ -900,6 +928,7 @@ function __embind_register_class_classmethod( rawInvoker, fn ) { + // todo: whenDependentTypesAreResolved var classType = requireRegisteredType(rawClassType, 'class'); var rawArgTypes = heap32VectorToArray(argCount, rawArgTypesAddr); methodName = Pointer_stringify(methodName); @@ -919,6 +948,7 @@ function __embind_register_class_field( memberPointerSize, memberPointer ) { + // todo: whenDependentTypesAreResolved var classType = requireRegisteredType(rawClassType, 'class'); fieldName = Pointer_stringify(fieldName); getter = FUNCTION_TABLE[getter]; From 2d45bdd07e5c45be824f42c715097341565cc8f3 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Wed, 6 Mar 2013 19:24:39 -0800 Subject: [PATCH 164/258] fix pointer adjustments in property access --- src/embind/embind.js | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 696b0c988..ea56097fc 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -962,14 +962,28 @@ function __embind_register_class_field( if (!this.$$.ptr) { throwBindingError('cannot access emscripten binding field ' + humanName + ' on deleted object'); } - return fieldType.fromWireType(getter(this.$$.ptr, memberPointer)); + + // TODO: error if pointer type doesn't match signature + var ptr = upcastPointer( + this.$$.ptr, + this.$$.pointeeType.registeredClass, + classType.registeredClass); + + return fieldType.fromWireType(getter(ptr, memberPointer)); }, set: function(v) { if (!this.$$.ptr) { throwBindingError('cannot modify emscripten binding field ' + humanName + ' on deleted object'); } + + // TODO: error if pointer type doesn't match signature + var ptr = upcastPointer( + this.$$.ptr, + this.$$.pointeeType.registeredClass, + classType.registeredClass); + var destructors = []; - setter(this.$$.ptr, memberPointer, fieldType.toWireType(destructors, v)); + setter(ptr, memberPointer, fieldType.toWireType(destructors, v)); runDestructors(destructors); }, enumerable: true From 6a1157c12b27a50dc129b05cdb47bf9adb8812b5 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Thu, 7 Mar 2013 02:12:35 -0800 Subject: [PATCH 165/258] Make it possible (again) to define a class before its base class is defined. --- src/embind/embind.js | 357 +++++++++++++++---------------- system/include/emscripten/bind.h | 4 +- 2 files changed, 178 insertions(+), 183 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index ea56097fc..a644c16c4 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -144,20 +144,6 @@ function requireRegisteredType(rawType, humanName) { return impl; } -/* -function requireArgumentTypes(rawArgTypes, name) { - var argTypes = []; - for (var i = 0; i < rawArgTypes.length; ++i) { - argTypes[i] = requireRegisteredType( - rawArgTypes[i], - i === 0 ? - name + " return value" : - name + " parameter " + i); - } - return argTypes; -} -*/ - function staticPointerCast(from, fromType, toType) { if (!from) { return from; @@ -620,69 +606,72 @@ function __embind_register_smart_ptr( rawGetPointee ) { name = Pointer_stringify(name); - var pointeeType = requireRegisteredType(rawPointeeType, 'class'); rawConstructor = FUNCTION_TABLE[rawConstructor]; rawDestructor = FUNCTION_TABLE[rawDestructor]; rawGetPointee = FUNCTION_TABLE[rawGetPointee]; - var Handle = createNamedFunction(name, function(ptr) { - Object.defineProperty(this, '$$', { - value: { - count: {value: 1}, - smartPtr: ptr, - ptr: rawGetPointee(ptr), - pointeeType: pointeeType, - }, + whenDependentTypesAreResolved([rawPointeeType], function(pointeeType) { + pointeeType = pointeeType[0]; + + var Handle = createNamedFunction(name, function(ptr) { + Object.defineProperty(this, '$$', { + value: { + count: {value: 1}, + smartPtr: ptr, + ptr: rawGetPointee(ptr), + pointeeType: pointeeType, + }, + }); }); + + // TODO: test for SmartPtr.prototype.constructor property? + // We likely want it distinct from pointeeType.prototype.constructor + Handle.prototype = Object.create(pointeeType.Handle.prototype); + + Handle.prototype.clone = function() { + if (!this.$$.ptr) { + throwBindingError(pointeeType.name + ' instance already deleted'); + } + + var clone = Object.create(Handle.prototype); + Object.defineProperty(clone, '$$', { + value: { + count: this.$$.count, + smartPtr: this.$$.smartPtr, + ptr: this.$$.ptr, + }, + }); + + clone.$$.count.value += 1; + return clone; + }; + + Handle.prototype['delete'] = function() { + if (!this.$$.ptr) { + throwBindingError(pointeeType.name + ' instance already deleted'); + } + + this.$$.count.value -= 1; + if (0 === this.$$.count.value) { + rawDestructor(this.$$.smartPtr); + } + this.$$.smartPtr = undefined; + this.$$.ptr = undefined; + }; + + var registeredPointer = new RegisteredPointer( + name, + rawType, + pointeeType.registeredClass, + pointeeType, + Handle, + true, + rawGetPointee, + rawConstructor, + rawDestructor); + registerType(rawType, registeredPointer); + pointeeType.smartPointerType = registeredPointer; }); - - // TODO: test for SmartPtr.prototype.constructor property? - // We likely want it distinct from pointeeType.prototype.constructor - Handle.prototype = Object.create(pointeeType.Handle.prototype); - - Handle.prototype.clone = function() { - if (!this.$$.ptr) { - throwBindingError(pointeeType.name + ' instance already deleted'); - } - - var clone = Object.create(Handle.prototype); - Object.defineProperty(clone, '$$', { - value: { - count: this.$$.count, - smartPtr: this.$$.smartPtr, - ptr: this.$$.ptr, - }, - }); - - clone.$$.count.value += 1; - return clone; - }; - - Handle.prototype['delete'] = function() { - if (!this.$$.ptr) { - throwBindingError(pointeeType.name + ' instance already deleted'); - } - - this.$$.count.value -= 1; - if (0 === this.$$.count.value) { - rawDestructor(this.$$.smartPtr); - } - this.$$.smartPtr = undefined; - this.$$.ptr = undefined; - }; - - var registeredPointer = new RegisteredPointer( - name, - rawType, - pointeeType.registeredClass, - pointeeType, - Handle, - true, - rawGetPointee, - rawConstructor, - rawDestructor); - registerType(rawType, registeredPointer); - pointeeType.smartPointerType = registeredPointer; } function ClassHandle() { @@ -704,7 +693,6 @@ function RegisteredClass( this.downcast = downcast; } -// TODO: null pointers are always zero (not a Handle) in Javascript function __embind_register_class( rawType, rawPointerType, @@ -721,114 +709,116 @@ function __embind_register_class( upcast = FUNCTION_TABLE[upcast]; downcast = FUNCTION_TABLE[downcast]; - var baseClass; - var basePrototype; - var depth; - if (baseClassRawType) { - baseClasses[rawType] = baseClassRawType; + whenDependentTypesAreResolved(baseClassRawType ? [baseClassRawType] : [], function(base) { + base = base[0]; - // TODO: allow registration of base after derived - var base = requireRegisteredType(baseClassRawType, 'base class'); - baseClass = base.registeredClass; - basePrototype = base.Handle.prototype; - } else { - basePrototype = ClassHandle.prototype; - } + var baseClass; + var basePrototype; + var depth; + if (baseClassRawType) { + baseClasses[rawType] = baseClassRawType; - var registeredClass = new RegisteredClass( - name, - isPolymorphic, - baseClassRawType, - baseClass, - upcast, - downcast); + baseClass = base.registeredClass; + basePrototype = base.Handle.prototype; + } else { + basePrototype = ClassHandle.prototype; + } - var Handle = createNamedFunction(name, function(ptr) { - Object.defineProperty(this, '$$', { - value: { - count: {value: 1, ptr: ptr}, - ptr: ptr, - pointeeType: type, + var registeredClass = new RegisteredClass( + name, + isPolymorphic, + baseClassRawType, + baseClass, + upcast, + downcast); + + var Handle = createNamedFunction(name, function(ptr) { + Object.defineProperty(this, '$$', { + value: { + count: {value: 1, ptr: ptr}, + ptr: ptr, + pointeeType: type, + } + }); + }); + + Handle.prototype = Object.create(basePrototype, { + constructor: { value: Handle }, + }); + Handle.prototype.clone = function() { + if (!this.$$.ptr) { + throwBindingError(type.name + ' instance already deleted'); } + + var clone = Object.create(Handle.prototype); + Object.defineProperty(clone, '$$', { + value: { + count: this.$$.count, + ptr: this.$$.ptr, + }, + }); + + clone.$$.count.value += 1; + return clone; + }; + + // 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) { + throwBindingError(type.name + ' instance already deleted'); // todo: but 'type' hasn't been resolved!?! + } + + this.$$.count.value -= 1; + if (0 === this.$$.count.value) { + rawDestructor(this.$$.count.ptr); + } + this.$$.ptr = undefined; + }; + + // todo: clean this up! + var type = new RegisteredPointer( + name, + rawType, + registeredClass, + undefined, + Handle, + false); + type.pointeeType = type; // :( + registerType(rawType, type); + + registerType(rawPointerType, new RegisteredPointer( + name + '*', + rawPointerType, + registeredClass, + type, + Handle, + false)); + + // todo: implement const pointers (no modification Javascript side) + registerType(rawConstPointerType, new RegisteredPointer( + name + ' const*', + rawConstPointerType, + registeredClass, + type, + Handle, + false)); + + type.constructor = createNamedFunction(name, function() { + if (Object.getPrototypeOf(this) !== Handle.prototype) { + throw new BindingError("Use 'new' to construct " + name); + } + var body = type.constructor_body; + if (undefined === body) { + throw new BindingError(name + " has no accessible constructor"); + } + return body.apply(this, arguments); }); + type.constructor.prototype = type.Handle.prototype; + type.constructor.type = type; + + exposePublicSymbol(name, type.constructor); }); - - Handle.prototype = Object.create(basePrototype, { - constructor: { value: Handle }, - }); - Handle.prototype.clone = function() { - if (!this.$$.ptr) { - throwBindingError(type.name + ' instance already deleted'); - } - - var clone = Object.create(Handle.prototype); - Object.defineProperty(clone, '$$', { - value: { - count: this.$$.count, - ptr: this.$$.ptr, - }, - }); - - clone.$$.count.value += 1; - return clone; - }; - - // 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) { - throwBindingError(type.name + ' instance already deleted'); // todo: but 'type' hasn't been resolved!?! - } - - this.$$.count.value -= 1; - if (0 === this.$$.count.value) { - rawDestructor(this.$$.count.ptr); - } - this.$$.ptr = undefined; - }; - - // todo: clean this up! - var type = new RegisteredPointer( - name, - rawType, - registeredClass, - undefined, - Handle, - false); - type.pointeeType = type; // :( - registerType(rawType, type); - - registerType(rawPointerType, new RegisteredPointer( - name + '*', - rawPointerType, - registeredClass, - type, - Handle, - false)); - - // todo: implement const pointers (no modification Javascript side) - registerType(rawConstPointerType, new RegisteredPointer( - name + ' const*', - rawConstPointerType, - registeredClass, - type, - Handle, - false)); - - type.constructor = createNamedFunction(name, function() { - if (Object.getPrototypeOf(this) !== Handle.prototype) { - throw new BindingError("Use 'new' to construct " + name); - } - var body = type.constructor.body; - if (undefined === body) { - throw new BindingError(name + " has no accessible constructor"); - } - return body.apply(this, arguments); - }); - type.constructor.prototype = type.Handle.prototype; - type.constructor.type = type; - - exposePublicSymbol(name, type.constructor); } function __embind_register_class_constructor( @@ -838,13 +828,14 @@ function __embind_register_class_constructor( invoker, rawConstructor ) { - var classType = requireRegisteredType(rawClassType, 'class'); var rawArgTypes = heap32VectorToArray(argCount, rawArgTypesAddr); invoker = FUNCTION_TABLE[invoker]; - whenDependentTypesAreResolved(rawArgTypes, function(argTypes) { + whenDependentTypesAreResolved([rawClassType].concat(rawArgTypes), function(argTypes) { + var classType = argTypes[0]; + argTypes = argTypes.slice(1); var humanName = 'constructor ' + classType.name; - classType.constructor.body = function() { + classType.constructor_body = function() { if (arguments.length !== argCount - 1) { throwBindingError(humanName + ' called with ' + arguments.length + ' arguments, expected ' + (argCount-1)); } @@ -880,12 +871,14 @@ function __embind_register_class_method( memberFunctionSize, memberFunction ) { - var classType = requireRegisteredType(rawClassType, 'class'); var rawArgTypes = heap32VectorToArray(argCount, rawArgTypesAddr); methodName = Pointer_stringify(methodName); rawInvoker = FUNCTION_TABLE[rawInvoker]; memberFunction = copyMemberPointer(memberFunction, memberFunctionSize); - whenDependentTypesAreResolved(rawArgTypes, function(argTypes) { + + whenDependentTypesAreResolved([rawClassType].concat(rawArgTypes), function(argTypes) { + var classType = argTypes[0]; + argTypes = argTypes.slice(1); var humanName = classType.name + '.' + methodName; classType.Handle.prototype[methodName] = function() { if (!this.$$.ptr) { @@ -949,14 +942,14 @@ function __embind_register_class_field( memberPointer ) { // todo: whenDependentTypesAreResolved - var classType = requireRegisteredType(rawClassType, 'class'); fieldName = Pointer_stringify(fieldName); getter = FUNCTION_TABLE[getter]; setter = FUNCTION_TABLE[setter]; memberPointer = copyMemberPointer(memberPointer, memberPointerSize); - whenDependentTypesAreResolved([rawFieldType], function(fieldType) { + whenDependentTypesAreResolved([rawClassType, rawFieldType], function(converters) { + var classType = converters[0]; + var fieldType = converters[1]; var humanName = classType.name + '.' + fieldName; - fieldType = fieldType[0]; Object.defineProperty(classType.Handle.prototype, fieldName, { get: function() { if (!this.$$.ptr) { diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index e8cedbe49..e8a8dc1dd 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -658,7 +658,9 @@ namespace emscripten { template class class_ { public: - class_(const char* name) { + class_() = delete; + + explicit class_(const char* name) { using namespace internal; BaseSpecifier::template verify(); From b4b75b51bf0e8c72aa8514eaf708b1d1aae30a8e Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Thu, 7 Mar 2013 03:08:35 -0800 Subject: [PATCH 166/258] Is this okay? It passes tests. --- src/embind/embind.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index a644c16c4..19c248128 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -517,10 +517,10 @@ RegisteredPointer.prototype.toWireType = function(destructors, handle) { // assumes that smart_ptr has an identical binary layout to // smart_ptr. I wonder if that's untrue for any common // smart pointer. - chad - ptr = pointeeType.smartPointerType.rawConstructor( + ptr = this.rawConstructor( ptr, handle.$$.smartPtr); - destructors.push(pointeeType.smartPointerType.rawDestructor); + destructors.push(this.rawDestructor); destructors.push(ptr); } return ptr; From 612340cec2f3761efffc450e1de0adb445a45036 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Thu, 7 Mar 2013 15:32:47 -0800 Subject: [PATCH 167/258] oh hey, you can use .push with multiple arguments Conflicts: src/embind/embind.js --- src/embind/embind.js | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 19c248128..5d6e6c21e 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -233,8 +233,7 @@ function __embind_register_cstring(rawType, name) { for (var i = 0; i < length; ++i) { HEAPU8[ptr + 4 + i] = value.charCodeAt(i); } - destructors.push(_free); - destructors.push(ptr); + destructors.push(_free, ptr); return ptr; }, }); @@ -324,8 +323,7 @@ function __embind_register_tuple(rawType, name, rawConstructor, rawDestructor) { for (var i = 0; i < len; ++i) { this.elements[i].write(ptr, o[i]); } - destructors.push(rawDestructor); - destructors.push(ptr); + destructors.push(rawDestructor, ptr); return ptr; }, }); @@ -438,8 +436,7 @@ function __embind_register_struct( for (fieldName in fields) { fields[fieldName].write(ptr, o[fieldName]); } - destructors.push(rawDestructor); - destructors.push(ptr); + destructors.push(rawDestructor, ptr); return ptr; }, }); @@ -520,8 +517,7 @@ RegisteredPointer.prototype.toWireType = function(destructors, handle) { ptr = this.rawConstructor( ptr, handle.$$.smartPtr); - destructors.push(this.rawDestructor); - destructors.push(ptr); + destructors.push(this.rawDestructor, ptr); } return ptr; }; @@ -1042,8 +1038,7 @@ function __embind_register_interface( toWireType: function(destructors, o) { var handle = __emval_register(o); var ptr = this.rawConstructor(handle); - destructors.push(this.rawDestructor); - destructors.push(ptr); + destructors.push(this.rawDestructor, ptr); return ptr; }, }); From 6a0976ce2e1006cda317625e294aa878045e305a Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Thu, 7 Mar 2013 15:42:13 -0800 Subject: [PATCH 168/258] checkpoint work towards unifying the binding type for classes and smart pointers. --- src/embind/embind.js | 8 +++++++- system/include/emscripten/bind.h | 6 +++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 5d6e6c21e..83c3841f9 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -491,7 +491,13 @@ RegisteredPointer.prototype.isPolymorphic = function() { RegisteredPointer.prototype.toWireType = function(destructors, handle) { var fromRawType; if (handle === null) { - return 0; // todo: maybe this should return a zero-initialized smart pointer object + if (this.isSmartPointer) { + var ptr = this.rawConstructor(0, 0); + destructors.push(this.rawDestructor, ptr); + return ptr; + } else { + return 0; + } } if (!(handle instanceof ClassHandle)) { throwBindingError('Expected pointer or null, got ' + IMVU.repr(handle)); diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index e8a8dc1dd..7dcfa61b9 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -543,7 +543,11 @@ namespace emscripten { typename smart_ptr_trait::element_type* ptr, SmartPointerType* basePtr ) { - return new SmartPointerType(smart_ptr_trait::share(*basePtr, ptr)); + if (ptr) { + return new SmartPointerType(smart_ptr_trait::share(*basePtr, ptr)); + } else { + return new SmartPointerType; + } } template From 3dd168f24a0294e7f306514d0bb71985457ab976 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Thu, 7 Mar 2013 15:55:25 -0800 Subject: [PATCH 169/258] checkpoint work towards unifying the binding type for classes and smart pointers. --- system/include/emscripten/wire.h | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/system/include/emscripten/wire.h b/system/include/emscripten/wire.h index d4efc3acf..f782d49e2 100755 --- a/system/include/emscripten/wire.h +++ b/system/include/emscripten/wire.h @@ -241,11 +241,7 @@ namespace emscripten { } static shared_ptr fromWireType(WireType wt) { - if (wt) { - return shared_ptr(*wt); - } else { - return shared_ptr(); - } + return *wt; } static void destroy(WireType p) { From 3ac210596939afee0397fac8d298975d2982bff1 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Thu, 7 Mar 2013 16:04:16 -0800 Subject: [PATCH 170/258] Kill the custom bindingtype for shared_ptr :) --- system/include/emscripten/wire.h | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/system/include/emscripten/wire.h b/system/include/emscripten/wire.h index f782d49e2..5126c4be7 100755 --- a/system/include/emscripten/wire.h +++ b/system/include/emscripten/wire.h @@ -231,24 +231,6 @@ namespace emscripten { } }; - template - struct BindingType> { - typedef std::shared_ptr shared_ptr; - typedef std::shared_ptr* WireType; - - static WireType toWireType(shared_ptr p) { - return new shared_ptr(p); - } - - static shared_ptr fromWireType(WireType wt) { - return *wt; - } - - static void destroy(WireType p) { - delete p; - } - }; - template struct EnumBindingType { typedef Enum WireType; From e7faeb6d50e26e74d21cc06b7eae8929752f4526 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Thu, 7 Mar 2013 16:29:10 -0800 Subject: [PATCH 171/258] reorder bindingtypes to make it easy to read --- system/include/emscripten/wire.h | 55 ++++++++++++++++---------------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/system/include/emscripten/wire.h b/system/include/emscripten/wire.h index 5126c4be7..07919a5e5 100755 --- a/system/include/emscripten/wire.h +++ b/system/include/emscripten/wire.h @@ -231,20 +231,6 @@ namespace emscripten { } }; - template - struct EnumBindingType { - typedef Enum WireType; - - static WireType toWireType(Enum v) { - return v; - } - static Enum fromWireType(WireType v) { - return v; - } - static void destroy(WireType) { - } - }; - template struct GenericBindingType { typedef typename std::remove_reference::type ActualT; @@ -263,6 +249,7 @@ namespace emscripten { } }; + // Is this necessary? template struct GenericBindingType> { typedef typename BindingType::WireType WireType; @@ -272,6 +259,33 @@ namespace emscripten { } }; + template + struct EnumBindingType { + typedef Enum WireType; + + static WireType toWireType(Enum v) { + return v; + } + static Enum fromWireType(WireType v) { + return v; + } + static void destroy(WireType) { + } + }; + + // catch-all generic binding + template + struct BindingType : std::conditional< + std::is_enum::value, + EnumBindingType, + GenericBindingType >::type + {}; + + template + auto toWireType(const T& v) -> typename BindingType::WireType { + return BindingType::toWireType(v); + } + template struct WireDeleter { typedef typename BindingType::WireType WireType; @@ -286,18 +300,5 @@ namespace emscripten { WireType wt; }; - - // catch-all generic binding - template - struct BindingType : std::conditional< - std::is_enum::value, - EnumBindingType, - GenericBindingType >::type - {}; - - template - auto toWireType(const T& v) -> typename BindingType::WireType { - return BindingType::toWireType(v); - } } } From 6a14788d73acac51770fd68d63cb8c5531efdfb2 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Thu, 7 Mar 2013 17:30:06 -0800 Subject: [PATCH 172/258] It is illegal to pass null as a by-value argument. --- src/embind/embind.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/embind/embind.js b/src/embind/embind.js index 83c3841f9..ed659a2b1 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -491,6 +491,10 @@ RegisteredPointer.prototype.isPolymorphic = function() { RegisteredPointer.prototype.toWireType = function(destructors, handle) { var fromRawType; if (handle === null) { + if (this.pointeeType === this) { + throwBindingError('null is not a valid ' + this.name); + } + if (this.isSmartPointer) { var ptr = this.rawConstructor(0, 0); destructors.push(this.rawDestructor, ptr); From 29425235762674d01013f73f669918df989fc311 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Thu, 7 Mar 2013 17:50:53 -0800 Subject: [PATCH 173/258] methods now give sensible error messages when passing incompatible objects as 'this' --- src/embind/embind.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/embind/embind.js b/src/embind/embind.js index ed659a2b1..470b03f70 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -887,6 +887,9 @@ function __embind_register_class_method( argTypes = argTypes.slice(1); var humanName = classType.name + '.' + methodName; classType.Handle.prototype[methodName] = function() { + if (!(this instanceof classType.constructor)) { + throwBindingError(humanName + ' incompatible with object of type ' + this.constructor.name); + } if (!this.$$.ptr) { throwBindingError('cannot call emscripten binding method ' + humanName + ' on deleted object'); } From 3be57ee250e6971708f60615ca26c3eba6ef80da Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Thu, 7 Mar 2013 18:05:26 -0800 Subject: [PATCH 174/258] Validate this when calling methods or property accessors --- src/embind/embind.js | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 470b03f70..940abaaa9 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -868,6 +868,15 @@ function upcastPointer(ptr, ptrClass, desiredClass) { return ptr; } +function validateThis(this_, classType, humanName) { + if (!(this_ instanceof classType.constructor)) { + throwBindingError(humanName + ' incompatible with object of type ' + this_.constructor.name); + } + if (!this_.$$.ptr) { + throwBindingError('cannot call emscripten binding method ' + humanName + ' on deleted object'); + } +} + function __embind_register_class_method( rawClassType, methodName, @@ -887,17 +896,11 @@ function __embind_register_class_method( argTypes = argTypes.slice(1); var humanName = classType.name + '.' + methodName; classType.Handle.prototype[methodName] = function() { - if (!(this instanceof classType.constructor)) { - throwBindingError(humanName + ' incompatible with object of type ' + this.constructor.name); - } - if (!this.$$.ptr) { - throwBindingError('cannot call emscripten binding method ' + humanName + ' on deleted object'); - } + validateThis(this, classType, humanName); if (arguments.length !== argCount - 1) { throwBindingError('emscripten binding method ' + humanName + ' called with ' + arguments.length + ' arguments, expected ' + (argCount-1)); } - // TODO: error if pointer type doesn't match signature var ptr = upcastPointer( this.$$.ptr, this.$$.pointeeType.registeredClass, @@ -930,7 +933,6 @@ function __embind_register_class_classmethod( rawInvoker, fn ) { - // todo: whenDependentTypesAreResolved var classType = requireRegisteredType(rawClassType, 'class'); var rawArgTypes = heap32VectorToArray(argCount, rawArgTypesAddr); methodName = Pointer_stringify(methodName); @@ -950,7 +952,6 @@ function __embind_register_class_field( memberPointerSize, memberPointer ) { - // todo: whenDependentTypesAreResolved fieldName = Pointer_stringify(fieldName); getter = FUNCTION_TABLE[getter]; setter = FUNCTION_TABLE[setter]; @@ -961,11 +962,8 @@ function __embind_register_class_field( var humanName = classType.name + '.' + fieldName; Object.defineProperty(classType.Handle.prototype, fieldName, { get: function() { - if (!this.$$.ptr) { - throwBindingError('cannot access emscripten binding field ' + humanName + ' on deleted object'); - } + validateThis(this, classType, humanName + ' getter'); - // TODO: error if pointer type doesn't match signature var ptr = upcastPointer( this.$$.ptr, this.$$.pointeeType.registeredClass, @@ -974,11 +972,8 @@ function __embind_register_class_field( return fieldType.fromWireType(getter(ptr, memberPointer)); }, set: function(v) { - if (!this.$$.ptr) { - throwBindingError('cannot modify emscripten binding field ' + humanName + ' on deleted object'); - } + validateThis(this, classType, humanName + ' setter'); - // TODO: error if pointer type doesn't match signature var ptr = upcastPointer( this.$$.ptr, this.$$.pointeeType.registeredClass, From 138b83710a8fa76b2d1585eca5a8a293776c835f Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Thu, 7 Mar 2013 18:14:59 -0800 Subject: [PATCH 175/258] Improve error output if calling emscripten methods with invalid 'this' --- src/embind/embind.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 940abaaa9..87512ddac 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -869,8 +869,11 @@ function upcastPointer(ptr, ptrClass, desiredClass) { } function validateThis(this_, classType, humanName) { + if (!(this_ instanceof Object)) { + throwBindingError(humanName + ' with invalid "this": ' + this_); + } if (!(this_ instanceof classType.constructor)) { - throwBindingError(humanName + ' incompatible with object of type ' + this_.constructor.name); + throwBindingError(humanName + ' incompatible with "this" of type ' + this_.constructor.name); } if (!this_.$$.ptr) { throwBindingError('cannot call emscripten binding method ' + humanName + ' on deleted object'); From 66b9991931f6e69c86437ed25b0f0072af4e9a62 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Thu, 7 Mar 2013 18:21:55 -0800 Subject: [PATCH 176/258] .method -> .function --- src/embind/embind.js | 2 +- system/include/emscripten/bind.h | 34 ++++++++++++++++---------------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 87512ddac..bd952791f 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -880,7 +880,7 @@ function validateThis(this_, classType, humanName) { } } -function __embind_register_class_method( +function __embind_register_class_function( rawClassType, methodName, argCount, diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index 7dcfa61b9..6e68c2064 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -119,7 +119,7 @@ namespace emscripten { GenericFunction invoker, GenericFunction constructor); - void _embind_register_class_method( + void _embind_register_class_function( TYPEID classType, const char* methodName, unsigned argCount, @@ -735,11 +735,11 @@ namespace emscripten { } template - class_& method(const char* methodName, ReturnType (ClassType::*memberFunction)(Args...), Policies...) { + class_& function(const char* methodName, ReturnType (ClassType::*memberFunction)(Args...), Policies...) { using namespace internal; typename WithPolicies::template ArgTypeList args; - _embind_register_class_method( + _embind_register_class_function( TypeID::get(), methodName, args.count, @@ -751,11 +751,11 @@ namespace emscripten { } template - class_& method(const char* methodName, ReturnType (ClassType::*memberFunction)(Args...) const, Policies...) { + class_& function(const char* methodName, ReturnType (ClassType::*memberFunction)(Args...) const, Policies...) { using namespace internal; typename WithPolicies::template ArgTypeList args; - _embind_register_class_method( + _embind_register_class_function( TypeID::get(), methodName, args.count, @@ -767,11 +767,11 @@ namespace emscripten { } template - class_& method(const char* methodName, ReturnType (*function)(ClassType& ptr, Args...), Policies...) { + class_& function(const char* methodName, ReturnType (*function)(ClassType& ptr, Args...), Policies...) { using namespace internal; typename WithPolicies::template ArgTypeList args; - _embind_register_class_method( + _embind_register_class_function( TypeID::get(), methodName, args.count, @@ -783,11 +783,11 @@ namespace emscripten { } template - class_& method(const char* methodName, ReturnType (*function)(const ClassType& ptr, Args...), Policies...) { + class_& function(const char* methodName, ReturnType (*function)(const ClassType& ptr, Args...), Policies...) { using namespace internal; typename WithPolicies::template ArgTypeList args; - _embind_register_class_method( + _embind_register_class_function( TypeID::get(), methodName, args.count, @@ -830,7 +830,7 @@ namespace emscripten { template class_& calloperator(const char* methodName, Policies... policies) { - return method(methodName, &ClassType::operator(), policies...); + return function(methodName, &ClassType::operator(), policies...); } }; @@ -870,10 +870,10 @@ namespace emscripten { void (VecType::*push_back)(const T&) = &VecType::push_back; return class_>(name) .template constructor<>() - .method("push_back", push_back) - .method("size", &VecType::size) - .method("get", &internal::VectorAccess::get) - .method("set", &internal::VectorAccess::set) + .function("push_back", push_back) + .function("size", &VecType::size) + .function("get", &internal::VectorAccess::get) + .function("set", &internal::VectorAccess::set) ; } @@ -912,9 +912,9 @@ namespace emscripten { return class_(name) .template constructor<>() - .method("size", &MapType::size) - .method("get", internal::MapAccess::get) - .method("set", internal::MapAccess::set) + .function("size", &MapType::size) + .function("get", internal::MapAccess::get) + .function("set", internal::MapAccess::set) ; } From e698d8bbdb77dc7a3c10e2fa9f1676dee194f3ab Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Thu, 7 Mar 2013 18:26:16 -0800 Subject: [PATCH 177/258] classmethod -> class_function --- src/embind/embind.js | 2 +- system/include/emscripten/bind.h | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index bd952791f..6595973e7 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -928,7 +928,7 @@ function __embind_register_class_function( }); } -function __embind_register_class_classmethod( +function __embind_register_class_class_function( rawClassType, methodName, argCount, diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index 6e68c2064..2c4df7d30 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -137,7 +137,7 @@ namespace emscripten { size_t memberPointerSize, void* memberPointer); - void _embind_register_class_classmethod( + void _embind_register_class_class_function( TYPEID classType, const char* methodName, unsigned argCount, @@ -728,7 +728,7 @@ namespace emscripten { .template constructor() ; - return classmethod( + return class_function( "implement", &operator_new, allow_raw_pointer()); @@ -814,11 +814,11 @@ namespace emscripten { } template - class_& classmethod(const char* methodName, ReturnType (*classMethod)(Args...), Policies...) { + class_& class_function(const char* methodName, ReturnType (*classMethod)(Args...), Policies...) { using namespace internal; typename WithPolicies::template ArgTypeList args; - _embind_register_class_classmethod( + _embind_register_class_class_function( TypeID::get(), methodName, args.count, From 770221aa384a9feb24d992ff697e6fba99049832 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Thu, 7 Mar 2013 18:28:03 -0800 Subject: [PATCH 178/258] Kill some duplication --- src/embind/embind.js | 29 +++++++++-------------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 6595973e7..9c5b52e8f 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -878,6 +878,11 @@ function validateThis(this_, classType, humanName) { if (!this_.$$.ptr) { throwBindingError('cannot call emscripten binding method ' + humanName + ' on deleted object'); } + + return upcastPointer( + this_.$$.ptr, + this_.$$.pointeeType.registeredClass, + classType.registeredClass); } function __embind_register_class_function( @@ -899,16 +904,12 @@ function __embind_register_class_function( argTypes = argTypes.slice(1); var humanName = classType.name + '.' + methodName; classType.Handle.prototype[methodName] = function() { - validateThis(this, classType, humanName); if (arguments.length !== argCount - 1) { throwBindingError('emscripten binding method ' + humanName + ' called with ' + arguments.length + ' arguments, expected ' + (argCount-1)); } - var ptr = upcastPointer( - this.$$.ptr, - this.$$.pointeeType.registeredClass, - classType.registeredClass); - + var ptr = validateThis(this, classType, humanName); + var destructors = []; var args = new Array(argCount + 1); args[0] = ptr; @@ -965,23 +966,11 @@ function __embind_register_class_field( var humanName = classType.name + '.' + fieldName; Object.defineProperty(classType.Handle.prototype, fieldName, { get: function() { - validateThis(this, classType, humanName + ' getter'); - - var ptr = upcastPointer( - this.$$.ptr, - this.$$.pointeeType.registeredClass, - classType.registeredClass); - + var ptr = validateThis(this, classType, humanName + ' getter'); return fieldType.fromWireType(getter(ptr, memberPointer)); }, set: function(v) { - validateThis(this, classType, humanName + ' setter'); - - var ptr = upcastPointer( - this.$$.ptr, - this.$$.pointeeType.registeredClass, - classType.registeredClass); - + var ptr = validateThis(this, classType, humanName + ' setter'); var destructors = []; setter(ptr, memberPointer, fieldType.toWireType(destructors, v)); runDestructors(destructors); From b72bbd39389fed75dc6f0ecb594540a209363eff Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Thu, 7 Mar 2013 18:42:32 -0800 Subject: [PATCH 179/258] .field -> .property --- src/embind/embind.js | 2 +- system/include/emscripten/bind.h | 52 ++++++++++++++++---------------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 9c5b52e8f..68cd0e7dc 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -947,7 +947,7 @@ function __embind_register_class_class_function( }); } -function __embind_register_class_field( +function __embind_register_class_property( rawClassType, fieldName, rawFieldType, diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index 2c4df7d30..1923013f8 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -128,7 +128,7 @@ namespace emscripten { size_t memberFunctionSize, void* memberFunction); - void _embind_register_class_field( + void _embind_register_class_property( TYPEID classType, const char* fieldName, TYPEID fieldType, @@ -363,17 +363,17 @@ namespace emscripten { } }; - template - struct FieldAccess { - typedef FieldType ClassType::*MemberPointer; - typedef internal::BindingType FieldBinding; - typedef typename FieldBinding::WireType WireType; + template + struct MemberAccess { + typedef MemberType ClassType::*MemberPointer; + typedef internal::BindingType MemberBinding; + typedef typename MemberBinding::WireType WireType; static WireType get( ClassType& ptr, const MemberPointer& field ) { - return FieldBinding::toWireType(ptr.*field); + return MemberBinding::toWireType(ptr.*field); } static void set( @@ -381,7 +381,7 @@ namespace emscripten { const MemberPointer& field, WireType value ) { - ptr.*field = FieldBinding::fromWireType(value); + ptr.*field = MemberBinding::fromWireType(value); } template @@ -389,7 +389,7 @@ namespace emscripten { ClassType& ptr, const Getter& getter ) { - return FieldBinding::toWireType(getter(ptr)); + return MemberBinding::toWireType(getter(ptr)); } template @@ -398,7 +398,7 @@ namespace emscripten { const Setter& setter, WireType value ) { - setter(ptr, FieldBinding::fromWireType(value)); + setter(ptr, MemberBinding::fromWireType(value)); } }; @@ -424,8 +424,8 @@ namespace emscripten { internal::_embind_register_tuple_element( internal::TypeID::get(), internal::TypeID::get(), - reinterpret_cast(&internal::FieldAccess::get), - reinterpret_cast(&internal::FieldAccess::set), + reinterpret_cast(&internal::MemberAccess::get), + reinterpret_cast(&internal::MemberAccess::set), sizeof(field), &field); @@ -437,10 +437,10 @@ namespace emscripten { internal::_embind_register_tuple_element_accessor( internal::TypeID::get(), internal::TypeID::get(), - reinterpret_cast(&internal::FieldAccess::template propertyGet), + reinterpret_cast(&internal::MemberAccess::template propertyGet), sizeof(getter), &getter, - reinterpret_cast(&internal::FieldAccess::template propertySet), + reinterpret_cast(&internal::MemberAccess::template propertySet), sizeof(setter), &setter); return *this; @@ -451,10 +451,10 @@ namespace emscripten { internal::_embind_register_tuple_element_accessor( internal::TypeID::get(), internal::TypeID::get(), - reinterpret_cast(&internal::FieldAccess::template propertyGet), + reinterpret_cast(&internal::MemberAccess::template propertyGet), sizeof(getter), &getter, - reinterpret_cast(&internal::FieldAccess::template propertySet), + reinterpret_cast(&internal::MemberAccess::template propertySet), sizeof(setter), &setter); return *this; @@ -465,10 +465,10 @@ namespace emscripten { internal::_embind_register_tuple_element_accessor( internal::TypeID::get(), internal::TypeID::get(), - reinterpret_cast(&internal::FieldAccess::template propertyGet), + reinterpret_cast(&internal::MemberAccess::template propertyGet), sizeof(getter), &getter, - reinterpret_cast(&internal::FieldAccess::template propertySet), + reinterpret_cast(&internal::MemberAccess::template propertySet), sizeof(setter), &setter); return *this; @@ -479,10 +479,10 @@ namespace emscripten { internal::_embind_register_tuple_element_accessor( internal::TypeID::get(), internal::TypeID::get(), - reinterpret_cast(&internal::FieldAccess::template propertyGet), + reinterpret_cast(&internal::MemberAccess::template propertyGet), sizeof(getter), &getter, - reinterpret_cast(&internal::FieldAccess::template propertySet), + reinterpret_cast(&internal::MemberAccess::template propertySet), sizeof(setter), &setter); return *this; @@ -510,8 +510,8 @@ namespace emscripten { internal::TypeID::get(), fieldName, internal::TypeID::get(), - reinterpret_cast(&internal::FieldAccess::get), - reinterpret_cast(&internal::FieldAccess::set), + reinterpret_cast(&internal::MemberAccess::get), + reinterpret_cast(&internal::MemberAccess::set), sizeof(field), &field); @@ -799,15 +799,15 @@ namespace emscripten { } template - class_& field(const char* fieldName, FieldType ClassType::*field) { + class_& property(const char* fieldName, FieldType ClassType::*field) { using namespace internal; - _embind_register_class_field( + _embind_register_class_property( TypeID::get(), fieldName, TypeID::get(), - reinterpret_cast(&FieldAccess::get), - reinterpret_cast(&FieldAccess::set), + reinterpret_cast(&MemberAccess::get), + reinterpret_cast(&MemberAccess::set), sizeof(field), &field); return *this; From b2fb12ec07ac4ca271f7138f2bf0fa61f5a701e8 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Fri, 8 Mar 2013 00:17:49 -0800 Subject: [PATCH 180/258] I just learned about the sizeof... operator --- system/include/emscripten/wire.h | 21 ++------------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/system/include/emscripten/wire.h b/system/include/emscripten/wire.h index 07919a5e5..3e4a77d97 100755 --- a/system/include/emscripten/wire.h +++ b/system/include/emscripten/wire.h @@ -52,21 +52,6 @@ namespace emscripten { } }; - // count<> - - template - struct count; - - template<> - struct count<> { - constexpr static unsigned value = 0; - }; - - template - struct count { - constexpr static unsigned value = 1 + count::value; - }; - // ExecutePolicies<> template @@ -118,15 +103,13 @@ namespace emscripten { struct WithPolicies { template struct ArgTypeList { - enum { args_count = count::value }; - ArgTypeList() { - count = args_count; + count = sizeof...(Args); ArgTypes<0, Args...>::template fill(types); } unsigned count; - TYPEID types[args_count]; + TYPEID types[sizeof...(Args)]; }; }; From 4d5ff234202ea95a01e5882cf215cd03ce83260d Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Fri, 8 Mar 2013 12:44:16 -0800 Subject: [PATCH 181/258] give handles access to their associated pointer type --- src/embind/embind.js | 168 ++++++++++++++++++++++--------------------- 1 file changed, 87 insertions(+), 81 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 68cd0e7dc..f4d2fc6ec 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -550,7 +550,7 @@ RegisteredPointer.prototype.fromWireType = function(ptr) { this.destructor(ptr); return null; } - return new this.Handle(ptr); + return new this.Handle(this, ptr); }; // todo: could this return the actual type if not polymorphic? @@ -603,83 +603,7 @@ RegisteredPointer.prototype.fromWireTypeAutoDowncast = function(ptr) { // ptr is return handle; }; -function __embind_register_smart_ptr( - rawType, - rawPointeeType, - name, - rawConstructor, - rawDestructor, - rawGetPointee -) { - name = Pointer_stringify(name); - rawConstructor = FUNCTION_TABLE[rawConstructor]; - rawDestructor = FUNCTION_TABLE[rawDestructor]; - rawGetPointee = FUNCTION_TABLE[rawGetPointee]; - - whenDependentTypesAreResolved([rawPointeeType], function(pointeeType) { - pointeeType = pointeeType[0]; - - var Handle = createNamedFunction(name, function(ptr) { - Object.defineProperty(this, '$$', { - value: { - count: {value: 1}, - smartPtr: ptr, - ptr: rawGetPointee(ptr), - pointeeType: pointeeType, - }, - }); - }); - - // TODO: test for SmartPtr.prototype.constructor property? - // We likely want it distinct from pointeeType.prototype.constructor - Handle.prototype = Object.create(pointeeType.Handle.prototype); - - Handle.prototype.clone = function() { - if (!this.$$.ptr) { - throwBindingError(pointeeType.name + ' instance already deleted'); - } - - var clone = Object.create(Handle.prototype); - Object.defineProperty(clone, '$$', { - value: { - count: this.$$.count, - smartPtr: this.$$.smartPtr, - ptr: this.$$.ptr, - }, - }); - - clone.$$.count.value += 1; - return clone; - }; - - Handle.prototype['delete'] = function() { - if (!this.$$.ptr) { - throwBindingError(pointeeType.name + ' instance already deleted'); - } - - this.$$.count.value -= 1; - if (0 === this.$$.count.value) { - rawDestructor(this.$$.smartPtr); - } - this.$$.smartPtr = undefined; - this.$$.ptr = undefined; - }; - - var registeredPointer = new RegisteredPointer( - name, - rawType, - pointeeType.registeredClass, - pointeeType, - Handle, - true, - rawGetPointee, - rawConstructor, - rawDestructor); - registerType(rawType, registeredPointer); - pointeeType.smartPointerType = registeredPointer; - }); -} - +// root of all class handles in embind function ClassHandle() { } @@ -738,12 +662,15 @@ function __embind_register_class( upcast, downcast); - var Handle = createNamedFunction(name, function(ptr) { + var Handle = createNamedFunction(name, function(registeredPointer, ptr) { Object.defineProperty(this, '$$', { value: { - count: {value: 1, ptr: ptr}, + registeredPointer: registeredPointer, + count: { + value: 1, + ptr: ptr }, // todo: is this necessary? ptr: ptr, - pointeeType: type, + pointeeType: type, // todo: is this necessary? } }); }); @@ -980,6 +907,85 @@ function __embind_register_class_property( }); } +function __embind_register_smart_ptr( + rawType, + rawPointeeType, + name, + rawConstructor, + rawDestructor, + rawGetPointee +) { + name = Pointer_stringify(name); + rawConstructor = FUNCTION_TABLE[rawConstructor]; + rawDestructor = FUNCTION_TABLE[rawDestructor]; + rawGetPointee = FUNCTION_TABLE[rawGetPointee]; + + whenDependentTypesAreResolved([rawPointeeType], function(pointeeType) { + pointeeType = pointeeType[0]; + + var Handle = createNamedFunction(name, function(registeredPointer, ptr) { + Object.defineProperty(this, '$$', { + value: { + registeredPointer: registeredPointer, + count: {value: 1}, + smartPtr: ptr, + ptr: rawGetPointee(ptr), + // todo: is this necessary? + pointeeType: pointeeType, + }, + }); + }); + + // TODO: test for SmartPtr.prototype.constructor property? + // We likely want it distinct from pointeeType.prototype.constructor + Handle.prototype = Object.create(pointeeType.Handle.prototype); + + Handle.prototype.clone = function() { + if (!this.$$.ptr) { + throwBindingError(pointeeType.name + ' instance already deleted'); + } + + var clone = Object.create(Handle.prototype); + Object.defineProperty(clone, '$$', { + value: { + count: this.$$.count, + smartPtr: this.$$.smartPtr, + ptr: this.$$.ptr, + }, + }); + + clone.$$.count.value += 1; + return clone; + }; + + Handle.prototype['delete'] = function() { + if (!this.$$.ptr) { + throwBindingError(pointeeType.name + ' instance already deleted'); + } + + this.$$.count.value -= 1; + if (0 === this.$$.count.value) { + rawDestructor(this.$$.smartPtr); + } + this.$$.smartPtr = undefined; + this.$$.ptr = undefined; + }; + + var registeredPointer = new RegisteredPointer( + name, + rawType, + pointeeType.registeredClass, + pointeeType, + Handle, + true, + rawGetPointee, + rawConstructor, + rawDestructor); + registerType(rawType, registeredPointer); + pointeeType.smartPointerType = registeredPointer; + }); +} + function __embind_register_enum( rawType, name From c7d580829e3423d2e0eea242006ebd6dce02c772 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Fri, 8 Mar 2013 13:00:30 -0800 Subject: [PATCH 182/258] count.ptr appears to be untested right now. remove it until we know we need it. --- src/embind/embind.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index f4d2fc6ec..ac4bf1dfd 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -666,9 +666,7 @@ function __embind_register_class( Object.defineProperty(this, '$$', { value: { registeredPointer: registeredPointer, - count: { - value: 1, - ptr: ptr }, // todo: is this necessary? + count: { value: 1 }, ptr: ptr, pointeeType: type, // todo: is this necessary? } @@ -695,8 +693,6 @@ function __embind_register_class( return clone; }; - // 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) { throwBindingError(type.name + ' instance already deleted'); // todo: but 'type' hasn't been resolved!?! @@ -704,7 +700,7 @@ function __embind_register_class( this.$$.count.value -= 1; if (0 === this.$$.count.value) { - rawDestructor(this.$$.count.ptr); + rawDestructor(this.$$.ptr); } this.$$.ptr = undefined; }; From 88245a330e2db841f1eca2b49eae7e00e1292260 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Fri, 8 Mar 2013 15:21:47 -0800 Subject: [PATCH 183/258] CastError extend BindingError --- src/embind/embind.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index ac4bf1dfd..ace6e7fc0 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -8,7 +8,7 @@ /*global ___staticPointerCast: false*/ var BindingError = Module.BindingError = extendError(Error, 'BindingError'); -var CastError = Module.CastError = extendError(Error, 'CastError'); +var CastError = Module.CastError = extendError(BindingError, 'CastError'); function throwBindingError(value) { throw new BindingError(value); From 8a58711ab61ba62e10c87e7a1d873c38ff0757e0 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Fri, 8 Mar 2013 15:45:20 -0800 Subject: [PATCH 184/258] Throw an error if calling non-const methods on const objects. --- src/embind/embind.js | 11 ++++++++++- system/include/emscripten/bind.h | 5 +++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index ace6e7fc0..b52f9ff72 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -472,12 +472,13 @@ function __embind_register_struct_field( }); } -function RegisteredPointer(name, rawType, registeredClass, pointeeType, Handle, isSmartPointer, rawGetPointee, rawConstructor, rawDestructor) { +function RegisteredPointer(name, rawType, registeredClass, pointeeType, Handle, isConst, isSmartPointer, rawGetPointee, rawConstructor, rawDestructor) { this.name = name; this.rawType = rawType; this.registeredClass = registeredClass; this.pointeeType = pointeeType; this.Handle = Handle; // <-- I think I can kill this + this.isConst = isConst; this.isSmartPointer = isSmartPointer; this.rawGetPointee = rawGetPointee; this.rawConstructor = rawConstructor; @@ -712,6 +713,7 @@ function __embind_register_class( registeredClass, undefined, Handle, + false, false); type.pointeeType = type; // :( registerType(rawType, type); @@ -722,6 +724,7 @@ function __embind_register_class( registeredClass, type, Handle, + false, false)); // todo: implement const pointers (no modification Javascript side) @@ -731,6 +734,7 @@ function __embind_register_class( registeredClass, type, Handle, + true, false)); type.constructor = createNamedFunction(name, function() { @@ -813,6 +817,7 @@ function __embind_register_class_function( methodName, argCount, rawArgTypesAddr, + isConst, rawInvoker, memberFunctionSize, memberFunction @@ -832,6 +837,9 @@ function __embind_register_class_function( } var ptr = validateThis(this, classType, humanName); + if (!isConst && this.$$.registeredPointer.isConst) { + throwBindingError('Cannot call non-const method ' + humanName + ' on const reference'); + } var destructors = []; var args = new Array(argCount + 1); @@ -973,6 +981,7 @@ function __embind_register_smart_ptr( pointeeType.registeredClass, pointeeType, Handle, + false, true, rawGetPointee, rawConstructor, diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index 1923013f8..bdcde6255 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -124,6 +124,7 @@ namespace emscripten { const char* methodName, unsigned argCount, TYPEID argTypes[], + bool isConst, GenericFunction invoker, size_t memberFunctionSize, void* memberFunction); @@ -744,6 +745,7 @@ namespace emscripten { methodName, args.count, args.types, + false, reinterpret_cast(&MethodInvoker::invoke), sizeof(memberFunction), &memberFunction); @@ -760,6 +762,7 @@ namespace emscripten { methodName, args.count, args.types, + true, reinterpret_cast(&ConstMethodInvoker::invoke), sizeof(memberFunction), &memberFunction); @@ -776,6 +779,7 @@ namespace emscripten { methodName, args.count, args.types, + false, reinterpret_cast(&FunctionInvoker::invoke), sizeof(function), &function); @@ -792,6 +796,7 @@ namespace emscripten { methodName, args.count, args.types, + true, reinterpret_cast(&FunctionInvoker::invoke), sizeof(function), &function); From 2025ba8cef32874d40be093edf4d560931e3b399 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Fri, 8 Mar 2013 15:58:36 -0800 Subject: [PATCH 185/258] Illegal to pass const pointers into non-const parameters. --- src/embind/embind.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/embind/embind.js b/src/embind/embind.js index b52f9ff72..ccab4c48d 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -510,6 +510,9 @@ RegisteredPointer.prototype.toWireType = function(destructors, handle) { if (this.isSmartPointer && undefined === handle.$$.smartPtr) { throwBindingError('Passing raw pointer to smart pointer is illegal'); } + if (!this.isConst && handle.$$.registeredPointer.isConst) { + throwBindingError('Cannot pass argument of type ' + handle.$$.registeredPointer.name + ' to parameter of type ' + this.name); + } var pointeeType = handle.$$.pointeeType; if (pointeeType.isPolymorphic()) { fromRawType = pointeeType.getDynamicRawPointerType(handle.$$.ptr); From b54dbc819583d608d1a16c9c48fe9831443e96a4 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Fri, 8 Mar 2013 16:03:57 -0800 Subject: [PATCH 186/258] Oops I did this already. --- src/embind/embind.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index ccab4c48d..f44ee176a 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -730,7 +730,6 @@ function __embind_register_class( false, false)); - // todo: implement const pointers (no modification Javascript side) registerType(rawConstPointerType, new RegisteredPointer( name + ' const*', rawConstPointerType, From 76bd2d5cc47468eaae1d9149d997c63b72aa18d3 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Fri, 8 Mar 2013 16:17:35 -0800 Subject: [PATCH 187/258] Work towards killing some pointeeType references. --- src/embind/embind.js | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index f44ee176a..5aae8669e 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -472,12 +472,26 @@ function __embind_register_struct_field( }); } -function RegisteredPointer(name, rawType, registeredClass, pointeeType, Handle, isConst, isSmartPointer, rawGetPointee, rawConstructor, rawDestructor) { +// I guarantee there is a way to simplify the following data structure. +function RegisteredPointer( + name, + rawType, + registeredClass, + pointeeType, + Handle, + isReference, + isConst, + isSmartPointer, + rawGetPointee, + rawConstructor, + rawDestructor +) { this.name = name; this.rawType = rawType; this.registeredClass = registeredClass; this.pointeeType = pointeeType; this.Handle = Handle; // <-- I think I can kill this + this.isReference = isReference; this.isConst = isConst; this.isSmartPointer = isSmartPointer; this.rawGetPointee = rawGetPointee; @@ -492,7 +506,7 @@ RegisteredPointer.prototype.isPolymorphic = function() { RegisteredPointer.prototype.toWireType = function(destructors, handle) { var fromRawType; if (handle === null) { - if (this.pointeeType === this) { + if (this.isReference) { throwBindingError('null is not a valid ' + this.name); } @@ -557,7 +571,6 @@ RegisteredPointer.prototype.fromWireType = function(ptr) { return new this.Handle(this, ptr); }; -// todo: could this return the actual type if not polymorphic? RegisteredPointer.prototype.getDynamicRawPointerType = function(ptr) { var type = null; if (this.isPolymorphic()) { @@ -716,6 +729,7 @@ function __embind_register_class( registeredClass, undefined, Handle, + true, false, false); type.pointeeType = type; // :( @@ -728,6 +742,7 @@ function __embind_register_class( type, Handle, false, + false, false)); registerType(rawConstPointerType, new RegisteredPointer( @@ -736,6 +751,7 @@ function __embind_register_class( registeredClass, type, Handle, + false, true, false)); @@ -810,7 +826,7 @@ function validateThis(this_, classType, humanName) { return upcastPointer( this_.$$.ptr, - this_.$$.pointeeType.registeredClass, + this_.$$.registeredPointer.registeredClass, classType.registeredClass); } @@ -984,6 +1000,7 @@ function __embind_register_smart_ptr( pointeeType, Handle, false, + false, true, rawGetPointee, rawConstructor, From 40976b0286d7b647777adc037976925c4c1505fd Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Fri, 8 Mar 2013 16:20:45 -0800 Subject: [PATCH 188/258] tiny refactoring --- src/embind/embind.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 5aae8669e..82b9c5d4d 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -607,13 +607,12 @@ RegisteredPointer.prototype.fromWireTypeAutoDowncast = function(ptr) { // ptr is } var toType = this.getDynamicDowncastType(ptr); if (toType) { - var fromType = this.pointeeType; if (this.isSmartPointer) { handle = toType.smartPointerType.fromWireType(ptr); } else { handle = toType.fromWireType(ptr); } - handle.$$.ptr = staticPointerCast(handle.$$.ptr, fromType.rawType, toType.rawType); + handle.$$.ptr = staticPointerCast(handle.$$.ptr, this.pointeeType.rawType, toType.rawType); } else { handle = this.fromWireType(ptr); } From 808e914ce1a0a0388d0bb6f6c4dadf935ad35f4e Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Mon, 11 Mar 2013 13:02:30 -0700 Subject: [PATCH 189/258] Just toodling around with smart pointer binding syntax --- system/include/emscripten/bind.h | 40 ++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index bdcde6255..fd2e928a5 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -660,6 +660,23 @@ namespace emscripten { } }; + template + struct ptr { + typedef PointerType pointer_type; + }; + + namespace internal { + template + struct is_ptr { + enum { value = false }; + }; + + template + struct is_ptr> { + enum { value = true }; + }; + }; + template class class_ { public: @@ -670,16 +687,19 @@ namespace emscripten { BaseSpecifier::template verify(); - _embind_register_class( - TypeID::get(), - TypeID>::get(), - TypeID>::get(), - BaseSpecifier::get(), - BaseSpecifier::template getUpcaster(), - BaseSpecifier::template getDowncaster(), - std::is_polymorphic::value, - name, - reinterpret_cast(&raw_destructor)); + if (is_ptr::value) { + } else { + _embind_register_class( + TypeID::get(), + TypeID>::get(), + TypeID>::get(), + BaseSpecifier::get(), + BaseSpecifier::template getUpcaster(), + BaseSpecifier::template getDowncaster(), + std::is_polymorphic::value, + name, + reinterpret_cast(&raw_destructor)); + } } template From 495c40ce186d5b852f3bc3430058a24a94c72dba Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Mon, 11 Mar 2013 13:12:42 -0700 Subject: [PATCH 190/258] enable_if is scary and cool --- system/include/emscripten/bind.h | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index fd2e928a5..4b9968dc8 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -682,24 +682,22 @@ namespace emscripten { public: class_() = delete; + template::value>::type> explicit class_(const char* name) { using namespace internal; BaseSpecifier::template verify(); - if (is_ptr::value) { - } else { - _embind_register_class( - TypeID::get(), - TypeID>::get(), - TypeID>::get(), - BaseSpecifier::get(), - BaseSpecifier::template getUpcaster(), - BaseSpecifier::template getDowncaster(), - std::is_polymorphic::value, - name, - reinterpret_cast(&raw_destructor)); - } + _embind_register_class( + TypeID::get(), + TypeID>::get(), + TypeID>::get(), + BaseSpecifier::get(), + BaseSpecifier::template getUpcaster(), + BaseSpecifier::template getDowncaster(), + std::is_polymorphic::value, + name, + reinterpret_cast(&raw_destructor)); } template From de9a8d6fecd4f699b2f80d908c8c597dcc8f04bc Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Mon, 11 Mar 2013 18:42:13 -0700 Subject: [PATCH 191/258] make smart_ptr a method of class_ instead of a global --- system/include/emscripten/bind.h | 34 +++++++++++++++++--------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index 4b9968dc8..e3593196a 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -557,20 +557,6 @@ namespace emscripten { } } - template - void smart_ptr(const char* name) { - using namespace internal; - typedef typename smart_ptr_trait::element_type PointeeType; - - _embind_register_smart_ptr( - TypeID::get(), - TypeID::get(), - name, - reinterpret_cast(&raw_smart_pointer_constructor), - reinterpret_cast(&raw_destructor), - reinterpret_cast(&get_pointee)); - }; - //////////////////////////////////////////////////////////////////////////////// // CLASSES //////////////////////////////////////////////////////////////////////////////// @@ -700,6 +686,23 @@ namespace emscripten { reinterpret_cast(&raw_destructor)); } + template + class_& smart_ptr() { + using namespace internal; + typedef typename smart_ptr_trait::element_type PointeeType; + + // TODO: assert that PointeeType is identical to ClassType + + _embind_register_smart_ptr( + TypeID::get(), + TypeID::get(), + "SmartPtr", // TODO: generate unique name + reinterpret_cast(&raw_smart_pointer_constructor), + reinterpret_cast(&raw_destructor), + reinterpret_cast(&get_pointee)); + return *this; + }; + template class_& constructor(Policies... policies) { return constructor( @@ -725,8 +728,7 @@ namespace emscripten { class_& smart_ptr_constructor(SmartPtr (*factory)(Args...), Policies...) { using namespace internal; - // TODO: generate unique name - smart_ptr("SmartPtr"); + smart_ptr(); typename WithPolicies::template ArgTypeList args; _embind_register_class_constructor( From 7c49e9f8866314d688b07f99abb34ccdac442db3 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Tue, 12 Mar 2013 18:37:24 -0700 Subject: [PATCH 192/258] :O it works. Use a sharing policy approach to autocasting pointers. --- src/embind/embind.js | 51 ++++++++++++----- system/include/emscripten/bind.h | 95 ++++++++++++++++++++++---------- 2 files changed, 104 insertions(+), 42 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 82b9c5d4d..c52c6e4d3 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -482,8 +482,10 @@ function RegisteredPointer( isReference, isConst, isSmartPointer, + sharingPolicy, rawGetPointee, rawConstructor, + rawShare, rawDestructor ) { this.name = name; @@ -494,8 +496,10 @@ function RegisteredPointer( this.isReference = isReference; this.isConst = isConst; this.isSmartPointer = isSmartPointer; + this.sharingPolicy = sharingPolicy; this.rawGetPointee = rawGetPointee; this.rawConstructor = rawConstructor; + this.rawShare = rawShare; this.rawDestructor = rawDestructor; } @@ -511,7 +515,7 @@ RegisteredPointer.prototype.toWireType = function(destructors, handle) { } if (this.isSmartPointer) { - var ptr = this.rawConstructor(0, 0); + var ptr = this.rawConstructor(); destructors.push(this.rawDestructor, ptr); return ptr; } else { @@ -538,14 +542,30 @@ RegisteredPointer.prototype.toWireType = function(destructors, handle) { } var ptr = staticPointerCast(handle.$$.ptr, fromRawType, this.pointeeType.rawType); if (this.isSmartPointer) { - // If this is for smart ptr type conversion, I think it - // assumes that smart_ptr has an identical binary layout to - // smart_ptr. I wonder if that's untrue for any common - // smart pointer. - chad - ptr = this.rawConstructor( - ptr, - handle.$$.smartPtr); - destructors.push(this.rawDestructor, ptr); + switch (this.sharingPolicy) { + case 0: // NONE + throwBindingError('NONE sharing policy not yet supported'); + break; + + case 1: // INTRUSIVE + throwBindingError('INTRUSIVE sharing policy not yet supported'); + break; + + case 2: // BY_EMVAL + var clonedHandle = handle.clone(); + ptr = this.rawShare( + ptr, + __emval_register(function() { + clonedHandle.delete(); + }) + ); + destructors.push(this.rawDestructor, ptr); + break; + + default: + throwBindingError('Unsupporting sharing policy'); + + } } return ptr; }; @@ -932,14 +952,17 @@ function __embind_register_smart_ptr( rawType, rawPointeeType, name, + sharingPolicy, + rawGetPointee, rawConstructor, - rawDestructor, - rawGetPointee + rawShare, + rawDestructor ) { name = Pointer_stringify(name); - rawConstructor = FUNCTION_TABLE[rawConstructor]; - rawDestructor = FUNCTION_TABLE[rawDestructor]; rawGetPointee = FUNCTION_TABLE[rawGetPointee]; + rawConstructor = FUNCTION_TABLE[rawConstructor]; + rawShare = FUNCTION_TABLE[rawShare]; + rawDestructor = FUNCTION_TABLE[rawDestructor]; whenDependentTypesAreResolved([rawPointeeType], function(pointeeType) { pointeeType = pointeeType[0]; @@ -1001,8 +1024,10 @@ function __embind_register_smart_ptr( false, false, true, + sharingPolicy, rawGetPointee, rawConstructor, + rawShare, rawDestructor); registerType(rawType, registeredPointer); pointeeType.smartPointerType = registeredPointer; diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index e3593196a..6f6a62558 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -11,6 +11,12 @@ #include namespace emscripten { + enum class sharing_policy { + NONE = 0, + INTRUSIVE = 1, + BY_EMVAL = 2, + }; + namespace internal { typedef void (*GenericFunction)(); typedef long GenericEnumValue; @@ -97,9 +103,11 @@ namespace emscripten { TYPEID pointerType, TYPEID pointeeType, const char* pointerName, + sharing_policy sharingPolicy, + GenericFunction getPointee, GenericFunction constructor, - GenericFunction destructor, - GenericFunction getPointee); + GenericFunction share, + GenericFunction destructor); void _embind_register_class( TYPEID classType, @@ -524,38 +532,62 @@ namespace emscripten { // SMART POINTERS //////////////////////////////////////////////////////////////////////////////// + template + struct default_smart_ptr_trait { + static sharing_policy get_sharing_policy() { + return sharing_policy::NONE; + } + + static void* share(void* v) { + return 0; // no sharing + } + }; + // specialize if you have a different pointer type template - struct smart_ptr_trait { + struct smart_ptr_trait : public default_smart_ptr_trait { + typedef typename PointerType::element_type element_type; + + static element_type* get(const PointerType& ptr) { + return ptr.get(); + } + }; + + template + struct smart_ptr_trait> { + typedef std::shared_ptr PointerType; typedef typename PointerType::element_type element_type; static element_type* get(const PointerType& ptr) { return ptr.get(); } - static PointerType share(const PointerType& r, element_type* ptr) { - return PointerType(r, ptr); + static sharing_policy get_sharing_policy() { + return sharing_policy::BY_EMVAL; } - }; - namespace internal { - template - SmartPointerType* raw_smart_pointer_constructor( - typename smart_ptr_trait::element_type* ptr, - SmartPointerType* basePtr - ) { - if (ptr) { - return new SmartPointerType(smart_ptr_trait::share(*basePtr, ptr)); - } else { - return new SmartPointerType; + static std::shared_ptr* share(PointeeType* p, internal::EM_VAL v) { + return new std::shared_ptr( + p, + val_deleter(val::take_ownership(v))); + } + + private: + class val_deleter { + public: + val_deleter() = delete; + explicit val_deleter(val v) + : v(v) + {} + void operator()(void const*) { + v(); + // eventually we'll need to support emptied out val + v = val::undefined(); } - } - - template - typename smart_ptr_trait::element_type* get_pointee(const PointerType& ptr) { - return smart_ptr_trait::get(ptr); - } - } + private: + val v; + }; + }; //////////////////////////////////////////////////////////////////////////////// // CLASSES @@ -689,17 +721,22 @@ namespace emscripten { template class_& smart_ptr() { using namespace internal; - typedef typename smart_ptr_trait::element_type PointeeType; - + // TODO: assert that PointeeType is identical to ClassType + typedef smart_ptr_trait PointerTrait; + typedef typename PointerTrait::element_type PointeeType; + + _embind_register_smart_ptr( TypeID::get(), TypeID::get(), - "SmartPtr", // TODO: generate unique name - reinterpret_cast(&raw_smart_pointer_constructor), - reinterpret_cast(&raw_destructor), - reinterpret_cast(&get_pointee)); + "SmartPtr", // TODO: generate unique name, if one is needed at all + PointerTrait::get_sharing_policy(), + reinterpret_cast(&PointerTrait::get), + reinterpret_cast(&operator_new), + reinterpret_cast(&PointerTrait::share), + reinterpret_cast(&raw_destructor)); return *this; }; From 0badaf9b00289ffd204b88c8875f442822b1b704 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Wed, 13 Mar 2013 01:18:04 -0700 Subject: [PATCH 193/258] Allow smart pointers to have their actual type names. --- src/embind/embind.js | 29 ++++++++++++++++++----------- system/include/emscripten/bind.h | 2 +- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index c52c6e4d3..de6826aaf 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -522,8 +522,8 @@ RegisteredPointer.prototype.toWireType = function(destructors, handle) { return 0; } } - if (!(handle instanceof ClassHandle)) { - throwBindingError('Expected pointer or null, got ' + IMVU.repr(handle)); + if (!(handle instanceof this.registeredClass.constructor)) { + throwBindingError('Expected null or instance of ' + this.name + ', got ' + IMVU.repr(handle)); } if (this.isSmartPointer && undefined === handle.$$.smartPtr) { throwBindingError('Passing raw pointer to smart pointer is illegal'); @@ -645,6 +645,7 @@ function ClassHandle() { function RegisteredClass( name, + constructor, isPolymorphic, baseClassRawType, baseClass, @@ -652,6 +653,7 @@ function RegisteredClass( downcast ) { this.name = name; + this.constructor = constructor; this.isPolymorphic = isPolymorphic; this.baseClassRawType = baseClassRawType; this.baseClass = baseClass; @@ -690,14 +692,6 @@ function __embind_register_class( basePrototype = ClassHandle.prototype; } - var registeredClass = new RegisteredClass( - name, - isPolymorphic, - baseClassRawType, - baseClass, - upcast, - downcast); - var Handle = createNamedFunction(name, function(registeredPointer, ptr) { Object.defineProperty(this, '$$', { value: { @@ -709,6 +703,15 @@ function __embind_register_class( }); }); + var registeredClass = new RegisteredClass( + name, + Handle, + isPolymorphic, + baseClassRawType, + baseClass, + upcast, + downcast); + Handle.prototype = Object.create(basePrototype, { constructor: { value: Handle }, }); @@ -948,6 +951,10 @@ function __embind_register_class_property( }); } +function makeLegalFunctionName(name) { + return '_' + name.replace(/[^a-zA-Z0-9_]/g, '$'); +} + function __embind_register_smart_ptr( rawType, rawPointeeType, @@ -967,7 +974,7 @@ function __embind_register_smart_ptr( whenDependentTypesAreResolved([rawPointeeType], function(pointeeType) { pointeeType = pointeeType[0]; - var Handle = createNamedFunction(name, function(registeredPointer, ptr) { + var Handle = createNamedFunction(makeLegalFunctionName(name), function(registeredPointer, ptr) { Object.defineProperty(this, '$$', { value: { registeredPointer: registeredPointer, diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index 6f6a62558..f06ca69c1 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -731,7 +731,7 @@ namespace emscripten { _embind_register_smart_ptr( TypeID::get(), TypeID::get(), - "SmartPtr", // TODO: generate unique name, if one is needed at all + typeid(PointerType).name(), PointerTrait::get_sharing_policy(), reinterpret_cast(&PointerTrait::get), reinterpret_cast(&operator_new), From c8cdc3f73c486cc4ac2ac5da0de01f6072a092af Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Thu, 14 Mar 2013 00:22:27 -0700 Subject: [PATCH 194/258] Support the smart_ptr policy BY_EMVAL and pass the original smart pointer if the parameter has an identical type. --- src/embind/embind.js | 43 +++++++++++++++++++++---------------------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index de6826aaf..89669172a 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -508,7 +508,6 @@ RegisteredPointer.prototype.isPolymorphic = function() { }; RegisteredPointer.prototype.toWireType = function(destructors, handle) { - var fromRawType; if (handle === null) { if (this.isReference) { throwBindingError('null is not a valid ' + this.name); @@ -531,20 +530,17 @@ RegisteredPointer.prototype.toWireType = function(destructors, handle) { if (!this.isConst && handle.$$.registeredPointer.isConst) { throwBindingError('Cannot pass argument of type ' + handle.$$.registeredPointer.name + ' to parameter of type ' + this.name); } - var pointeeType = handle.$$.pointeeType; - if (pointeeType.isPolymorphic()) { - fromRawType = pointeeType.getDynamicRawPointerType(handle.$$.ptr); - } else { - fromRawType = pointeeType.rawType; - } - if (fromRawType === this.pointeeType.rawType) { - return this.isSmartPointer ? handle.$$.smartPtr : handle.$$.ptr; - } - var ptr = staticPointerCast(handle.$$.ptr, fromRawType, this.pointeeType.rawType); + var handleClass = handle.$$.registeredPointer.registeredClass; + var ptr = upcastPointer(handle.$$.ptr, handleClass, this.registeredClass); if (this.isSmartPointer) { switch (this.sharingPolicy) { case 0: // NONE - throwBindingError('NONE sharing policy not yet supported'); + // no upcasting + if (handle.$$.registeredPointer === this) { + ptr = handle.$$.smartPtr; + } else { + throwBindingError('NONE sharing policy not yet supported'); + } break; case 1: // INTRUSIVE @@ -552,19 +548,22 @@ RegisteredPointer.prototype.toWireType = function(destructors, handle) { break; case 2: // BY_EMVAL - var clonedHandle = handle.clone(); - ptr = this.rawShare( - ptr, - __emval_register(function() { - clonedHandle.delete(); - }) - ); - destructors.push(this.rawDestructor, ptr); - break; + if (handle.$$.registeredPointer === this) { + ptr = handle.$$.smartPtr; + } else { + var clonedHandle = handle.clone(); + ptr = this.rawShare( + ptr, + __emval_register(function() { + clonedHandle.delete(); + }) + ); + destructors.push(this.rawDestructor, ptr); + } + break; default: throwBindingError('Unsupporting sharing policy'); - } } return ptr; From ddb31f11310847b50e4e27f0d64390b55809eecd Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Thu, 14 Mar 2013 00:50:18 -0700 Subject: [PATCH 195/258] Implement a sane error message when passing an incompatible smart pointer type --- src/embind/embind.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 89669172a..2f30750f8 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -508,6 +508,11 @@ RegisteredPointer.prototype.isPolymorphic = function() { }; RegisteredPointer.prototype.toWireType = function(destructors, handle) { + var self = this; + function throwCannotConvert() { + throwBindingError('Cannot convert argument of type ' + handle.$$.registeredPointer.name + ' to parameter type ' + self.name); + } + if (handle === null) { if (this.isReference) { throwBindingError('null is not a valid ' + this.name); @@ -528,7 +533,7 @@ RegisteredPointer.prototype.toWireType = function(destructors, handle) { throwBindingError('Passing raw pointer to smart pointer is illegal'); } if (!this.isConst && handle.$$.registeredPointer.isConst) { - throwBindingError('Cannot pass argument of type ' + handle.$$.registeredPointer.name + ' to parameter of type ' + this.name); + throwCannotConvert(); } var handleClass = handle.$$.registeredPointer.registeredClass; var ptr = upcastPointer(handle.$$.ptr, handleClass, this.registeredClass); @@ -539,7 +544,7 @@ RegisteredPointer.prototype.toWireType = function(destructors, handle) { if (handle.$$.registeredPointer === this) { ptr = handle.$$.smartPtr; } else { - throwBindingError('NONE sharing policy not yet supported'); + throwCannotConvert(); } break; From fb92020f00a7ae5b43927927b1ec571afc161bf1 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Thu, 14 Mar 2013 01:03:39 -0700 Subject: [PATCH 196/258] Add a static assertion that a classes's smart_ptr is compatible --- system/include/emscripten/bind.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index f06ca69c1..10d0d926e 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -722,11 +722,10 @@ namespace emscripten { class_& smart_ptr() { using namespace internal; - // TODO: assert that PointeeType is identical to ClassType - typedef smart_ptr_trait PointerTrait; typedef typename PointerTrait::element_type PointeeType; + static_assert(std::is_same::type>::value, "smart pointer must point to this class"); _embind_register_smart_ptr( TypeID::get(), From 7d783d874a38b63515e8e3cc4c0c8ec15e428175 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Thu, 14 Mar 2013 01:18:18 -0700 Subject: [PATCH 197/258] Use typeid name for interface wrappers too. (Also introduce more uses of makeLegalFunctionName) --- src/embind/embind.js | 17 +++++++++++++---- system/include/emscripten/bind.h | 3 +-- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 2f30750f8..8810727d1 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -680,6 +680,7 @@ function __embind_register_class( rawDestructor = FUNCTION_TABLE[rawDestructor]; upcast = FUNCTION_TABLE[upcast]; downcast = FUNCTION_TABLE[downcast]; + var legalFunctionName = makeLegalFunctionName(name); whenDependentTypesAreResolved(baseClassRawType ? [baseClassRawType] : [], function(base) { base = base[0]; @@ -696,7 +697,7 @@ function __embind_register_class( basePrototype = ClassHandle.prototype; } - var Handle = createNamedFunction(name, function(registeredPointer, ptr) { + var Handle = createNamedFunction(legalFunctionName, function(registeredPointer, ptr) { Object.defineProperty(this, '$$', { value: { registeredPointer: registeredPointer, @@ -781,7 +782,7 @@ function __embind_register_class( true, false)); - type.constructor = createNamedFunction(name, function() { + type.constructor = createNamedFunction(legalFunctionName, function() { if (Object.getPrototypeOf(this) !== Handle.prototype) { throw new BindingError("Use 'new' to construct " + name); } @@ -794,7 +795,7 @@ function __embind_register_class( type.constructor.prototype = type.Handle.prototype; type.constructor.type = type; - exposePublicSymbol(name, type.constructor); + exposePublicSymbol(legalFunctionName, type.constructor); }); } @@ -955,8 +956,16 @@ function __embind_register_class_property( }); } +var char_0 = '0'.charCodeAt(0); +var char_9 = '9'.charCodeAt(0); function makeLegalFunctionName(name) { - return '_' + name.replace(/[^a-zA-Z0-9_]/g, '$'); + var rv = name.replace(/[^a-zA-Z0-9_]/g, '$'); + var f = rv.charCodeAt(0); + if (f >= char_0 && f <= char_9) { + return '_' + rv; + } else { + return rv; + } } function __embind_register_smart_ptr( diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index 10d0d926e..e58659716 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -780,8 +780,7 @@ namespace emscripten { class_& allow_subclass() { using namespace internal; - // TODO: unique or anonymous name - class_>("WrapperType") + class_>(typeid(WrapperType).name()) .template constructor() ; From 8d178bb7a2fa5ee0751f4a4ec391db276e601d95 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Thu, 14 Mar 2013 01:31:47 -0700 Subject: [PATCH 198/258] Kill some dead todos --- src/embind/embind.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 8810727d1..805c8b1db 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -703,7 +703,6 @@ function __embind_register_class( registeredPointer: registeredPointer, count: { value: 1 }, ptr: ptr, - pointeeType: type, // todo: is this necessary? } }); }); @@ -739,7 +738,7 @@ function __embind_register_class( Handle.prototype['delete'] = function() { if (!this.$$.ptr) { - throwBindingError(type.name + ' instance already deleted'); // todo: but 'type' hasn't been resolved!?! + throwBindingError(type.name + ' instance already deleted'); } this.$$.count.value -= 1; @@ -994,14 +993,10 @@ function __embind_register_smart_ptr( count: {value: 1}, smartPtr: ptr, ptr: rawGetPointee(ptr), - // todo: is this necessary? - pointeeType: pointeeType, }, }); }); - // TODO: test for SmartPtr.prototype.constructor property? - // We likely want it distinct from pointeeType.prototype.constructor Handle.prototype = Object.create(pointeeType.Handle.prototype); Handle.prototype.clone = function() { From cb2533c8538eba077c08102ae85f00a1231a14ca Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Thu, 14 Mar 2013 02:18:23 -0700 Subject: [PATCH 199/258] Rename registeredPointer to ptrType --- src/embind/embind.js | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 805c8b1db..0eefef360 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -510,7 +510,7 @@ RegisteredPointer.prototype.isPolymorphic = function() { RegisteredPointer.prototype.toWireType = function(destructors, handle) { var self = this; function throwCannotConvert() { - throwBindingError('Cannot convert argument of type ' + handle.$$.registeredPointer.name + ' to parameter type ' + self.name); + throwBindingError('Cannot convert argument of type ' + handle.$$.ptrType.name + ' to parameter type ' + self.name); } if (handle === null) { @@ -532,16 +532,16 @@ RegisteredPointer.prototype.toWireType = function(destructors, handle) { if (this.isSmartPointer && undefined === handle.$$.smartPtr) { throwBindingError('Passing raw pointer to smart pointer is illegal'); } - if (!this.isConst && handle.$$.registeredPointer.isConst) { + if (!this.isConst && handle.$$.ptrType.isConst) { throwCannotConvert(); } - var handleClass = handle.$$.registeredPointer.registeredClass; + var handleClass = handle.$$.ptrType.registeredClass; var ptr = upcastPointer(handle.$$.ptr, handleClass, this.registeredClass); if (this.isSmartPointer) { switch (this.sharingPolicy) { case 0: // NONE // no upcasting - if (handle.$$.registeredPointer === this) { + if (handle.$$.ptrType === this) { ptr = handle.$$.smartPtr; } else { throwCannotConvert(); @@ -553,7 +553,7 @@ RegisteredPointer.prototype.toWireType = function(destructors, handle) { break; case 2: // BY_EMVAL - if (handle.$$.registeredPointer === this) { + if (handle.$$.ptrType === this) { ptr = handle.$$.smartPtr; } else { var clonedHandle = handle.clone(); @@ -643,7 +643,7 @@ RegisteredPointer.prototype.fromWireTypeAutoDowncast = function(ptr) { // ptr is return handle; }; -// root of all class handles in embind +// root of all pointer and smart pointer handles in embind function ClassHandle() { } @@ -697,10 +697,10 @@ function __embind_register_class( basePrototype = ClassHandle.prototype; } - var Handle = createNamedFunction(legalFunctionName, function(registeredPointer, ptr) { + var Handle = createNamedFunction(legalFunctionName, function(ptrType, ptr) { Object.defineProperty(this, '$$', { value: { - registeredPointer: registeredPointer, + ptrType: ptrType, count: { value: 1 }, ptr: ptr, } @@ -852,7 +852,7 @@ function validateThis(this_, classType, humanName) { return upcastPointer( this_.$$.ptr, - this_.$$.registeredPointer.registeredClass, + this_.$$.ptrType.registeredClass, classType.registeredClass); } @@ -881,7 +881,7 @@ function __embind_register_class_function( } var ptr = validateThis(this, classType, humanName); - if (!isConst && this.$$.registeredPointer.isConst) { + if (!isConst && this.$$.ptrType.isConst) { throwBindingError('Cannot call non-const method ' + humanName + ' on const reference'); } @@ -986,10 +986,10 @@ function __embind_register_smart_ptr( whenDependentTypesAreResolved([rawPointeeType], function(pointeeType) { pointeeType = pointeeType[0]; - var Handle = createNamedFunction(makeLegalFunctionName(name), function(registeredPointer, ptr) { + var Handle = createNamedFunction(makeLegalFunctionName(name), function(ptrType, ptr) { Object.defineProperty(this, '$$', { value: { - registeredPointer: registeredPointer, + ptrType: ptrType, count: {value: 1}, smartPtr: ptr, ptr: rawGetPointee(ptr), From a09b543bf434b0f5e27b776f7a5a752f3e1c317d Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Thu, 14 Mar 2013 15:45:44 -0700 Subject: [PATCH 200/258] make a note that it's sometimes okay to pass raw pointers to smart pointers --- src/embind/embind.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/embind/embind.js b/src/embind/embind.js index 0eefef360..d664435fe 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -529,6 +529,8 @@ RegisteredPointer.prototype.toWireType = function(destructors, handle) { if (!(handle instanceof this.registeredClass.constructor)) { throwBindingError('Expected null or instance of ' + this.name + ', got ' + IMVU.repr(handle)); } + // TODO: this is not strictly true + // It seems legal to support BY_EMVAL and INTRUSIVE conversions from raw pointers to smart pointers if (this.isSmartPointer && undefined === handle.$$.smartPtr) { throwBindingError('Passing raw pointer to smart pointer is illegal'); } From bb23c470f034601b70136a3eb56f829e84674bc4 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Mon, 18 Mar 2013 16:33:44 -0700 Subject: [PATCH 201/258] Give each class a typeid function --- src/embind/embind.js | 5 +++++ system/include/emscripten/bind.h | 11 +++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index d664435fe..834aed740 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -655,6 +655,7 @@ function RegisteredClass( isPolymorphic, baseClassRawType, baseClass, + getActualType, upcast, downcast ) { @@ -663,6 +664,7 @@ function RegisteredClass( this.isPolymorphic = isPolymorphic; this.baseClassRawType = baseClassRawType; this.baseClass = baseClass; + this.getActualType = getActualType; this.upcast = upcast; this.downcast = downcast; } @@ -672,6 +674,7 @@ function __embind_register_class( rawPointerType, rawConstPointerType, baseClassRawType, + getActualType, upcast, downcast, isPolymorphic, @@ -680,6 +683,7 @@ function __embind_register_class( ) { name = Pointer_stringify(name); rawDestructor = FUNCTION_TABLE[rawDestructor]; + getActualType = FUNCTION_TABLE[getActualType]; upcast = FUNCTION_TABLE[upcast]; downcast = FUNCTION_TABLE[downcast]; var legalFunctionName = makeLegalFunctionName(name); @@ -715,6 +719,7 @@ function __embind_register_class( isPolymorphic, baseClassRawType, baseClass, + getActualType, upcast, downcast); diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index e58659716..cd208d370 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -114,6 +114,7 @@ namespace emscripten { TYPEID pointerType, TYPEID constPointerType, TYPEID baseClassType, + GenericFunction getActualType, GenericFunction upcast, GenericFunction downcast, bool isPolymorphic, @@ -648,6 +649,11 @@ namespace emscripten { return nullptr; } }; + + template + inline TYPEID getActualType(T* ptr) { + return reinterpret_cast(&typeid(ptr)); + }; } template @@ -692,7 +698,7 @@ namespace emscripten { template struct is_ptr> { enum { value = true }; - }; + }; }; template @@ -711,9 +717,10 @@ namespace emscripten { TypeID>::get(), TypeID>::get(), BaseSpecifier::get(), + reinterpret_cast(&getActualType), BaseSpecifier::template getUpcaster(), BaseSpecifier::template getDowncaster(), - std::is_polymorphic::value, + std::is_polymorphic::value, // TODO: may not be necessary name, reinterpret_cast(&raw_destructor)); } From fc3e58b6915368401a01da8587f014a94a31373d Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Mon, 18 Mar 2013 16:53:01 -0700 Subject: [PATCH 202/258] Kill fromWireTypeAutoDowncast --- src/embind/embind.js | 22 +++++++--------------- src/embind/emval.js | 2 +- 2 files changed, 8 insertions(+), 16 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 834aed740..d3690a659 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -277,11 +277,7 @@ function makeInvoker(name, argCount, argTypes, invoker, fn) { args[i] = argTypes[i].toWireType(destructors, arguments[i - 1]); } var rv = invoker.apply(null, args); - if (argTypes[0].fromWireTypeAutoDowncast) { - rv = argTypes[0].fromWireTypeAutoDowncast(rv); - } else { - rv = argTypes[0].fromWireType(rv); - } + rv = argTypes[0].fromWireType(rv); runDestructors(destructors); return rv; }; @@ -589,7 +585,7 @@ RegisteredPointer.prototype.destructor = function(ptr) { } }; -RegisteredPointer.prototype.fromWireType = function(ptr) { +RegisteredPointer.prototype._fromWireType = function(ptr) { if (!this.getPointee(ptr)) { this.destructor(ptr); return null; @@ -625,7 +621,7 @@ RegisteredPointer.prototype.getDynamicDowncastType = function(ptr) { return downcastType; }; -RegisteredPointer.prototype.fromWireTypeAutoDowncast = function(ptr) { // ptr is a raw pointer (or a raw smartpointer) +RegisteredPointer.prototype.fromWireType = function(ptr) { // ptr is a raw pointer (or a raw smartpointer) var handle; if (!this.getPointee(ptr)) { this.destructor(ptr); @@ -634,13 +630,13 @@ RegisteredPointer.prototype.fromWireTypeAutoDowncast = function(ptr) { // ptr is var toType = this.getDynamicDowncastType(ptr); if (toType) { if (this.isSmartPointer) { - handle = toType.smartPointerType.fromWireType(ptr); + handle = toType.smartPointerType._fromWireType(ptr); } else { - handle = toType.fromWireType(ptr); + handle = toType._fromWireType(ptr); } handle.$$.ptr = staticPointerCast(handle.$$.ptr, this.pointeeType.rawType, toType.rawType); } else { - handle = this.fromWireType(ptr); + handle = this._fromWireType(ptr); } return handle; }; @@ -900,11 +896,7 @@ function __embind_register_class_function( args[i + 1] = argTypes[i].toWireType(destructors, arguments[i - 1]); } var rv = rawInvoker.apply(null, args); - if (argTypes[0].fromWireTypeAutoDowncast) { - rv = argTypes[0].fromWireTypeAutoDowncast(rv); - } else { - rv = argTypes[0].fromWireType(rv); - } + rv = argTypes[0].fromWireType(rv); runDestructors(destructors); return rv; }; diff --git a/src/embind/emval.js b/src/embind/emval.js index 37a4970ec..c60c62e06 100755 --- a/src/embind/emval.js +++ b/src/embind/emval.js @@ -69,7 +69,7 @@ function __emval_new_cstring(v) { function __emval_take_value(type, v) { type = requireRegisteredType(type, '_emval_take_value'); - v = type.fromWireTypeAutoDowncast ? type.fromWireTypeAutoDowncast(v) : type.fromWireType(v); + v = type.fromWireType(v); return __emval_register(v); } From ecab6656e88f9399410f651c8fe4300dec103712 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Mon, 18 Mar 2013 18:40:24 -0700 Subject: [PATCH 203/258] Break some cyclic object dependencies. --- src/embind/embind.js | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index d3690a659..f6c926b74 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -473,7 +473,6 @@ function RegisteredPointer( name, rawType, registeredClass, - pointeeType, Handle, isReference, isConst, @@ -487,7 +486,6 @@ function RegisteredPointer( this.name = name; this.rawType = rawType; this.registeredClass = registeredClass; - this.pointeeType = pointeeType; this.Handle = Handle; // <-- I think I can kill this this.isReference = isReference; this.isConst = isConst; @@ -608,8 +606,8 @@ RegisteredPointer.prototype.getDynamicRawPointerType = function(ptr) { RegisteredPointer.prototype.getDynamicDowncastType = function(ptr) { var downcastType = null; var type = this.getDynamicRawPointerType(ptr); - if (type && type !== this.pointeeType.rawType) { - var derivation = Module.__getDerivationPath(type, this.pointeeType.rawType); + if (type && type !== this.registeredClass.rawType) { + var derivation = Module.__getDerivationPath(type, this.registeredClass.rawType); for (var i = 0; i < derivation.size(); i++) { downcastType = registeredTypes[derivation.get(i)]; if (downcastType && (!this.isSmartPointer || downcastType.smartPointerType)) { @@ -634,7 +632,7 @@ RegisteredPointer.prototype.fromWireType = function(ptr) { // ptr is a raw point } else { handle = toType._fromWireType(ptr); } - handle.$$.ptr = staticPointerCast(handle.$$.ptr, this.pointeeType.rawType, toType.rawType); + handle.$$.ptr = staticPointerCast(handle.$$.ptr, this.registeredClass.rawType, toType.rawType); } else { handle = this._fromWireType(ptr); } @@ -647,6 +645,7 @@ function ClassHandle() { function RegisteredClass( name, + rawType, constructor, isPolymorphic, baseClassRawType, @@ -656,6 +655,7 @@ function RegisteredClass( downcast ) { this.name = name; + this.rawType = rawType; this.constructor = constructor; this.isPolymorphic = isPolymorphic; this.baseClassRawType = baseClassRawType; @@ -711,6 +711,7 @@ function __embind_register_class( var registeredClass = new RegisteredClass( name, + rawType, Handle, isPolymorphic, baseClassRawType, @@ -756,19 +757,16 @@ function __embind_register_class( name, rawType, registeredClass, - undefined, Handle, true, false, false); - type.pointeeType = type; // :( registerType(rawType, type); registerType(rawPointerType, new RegisteredPointer( name + '*', rawPointerType, registeredClass, - type, Handle, false, false, @@ -778,7 +776,6 @@ function __embind_register_class( name + ' const*', rawConstPointerType, registeredClass, - type, Handle, false, true, @@ -1033,7 +1030,6 @@ function __embind_register_smart_ptr( name, rawType, pointeeType.registeredClass, - pointeeType, Handle, false, false, From d235d3e4958154f8d6fdaea098e86b8495ee407e Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Mon, 18 Mar 2013 18:57:21 -0700 Subject: [PATCH 204/258] Simplify RegisteredPointer#fromWireType some --- src/embind/embind.js | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index f6c926b74..520ca269d 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -583,14 +583,6 @@ RegisteredPointer.prototype.destructor = function(ptr) { } }; -RegisteredPointer.prototype._fromWireType = function(ptr) { - if (!this.getPointee(ptr)) { - this.destructor(ptr); - return null; - } - return new this.Handle(this, ptr); -}; - RegisteredPointer.prototype.getDynamicRawPointerType = function(ptr) { var type = null; if (this.isPolymorphic()) { @@ -620,6 +612,10 @@ RegisteredPointer.prototype.getDynamicDowncastType = function(ptr) { }; RegisteredPointer.prototype.fromWireType = function(ptr) { // ptr is a raw pointer (or a raw smartpointer) + function makeHandle(registeredType, ptr) { + return new registeredType.Handle(registeredType, ptr); + } + var handle; if (!this.getPointee(ptr)) { this.destructor(ptr); @@ -628,13 +624,13 @@ RegisteredPointer.prototype.fromWireType = function(ptr) { // ptr is a raw point var toType = this.getDynamicDowncastType(ptr); if (toType) { if (this.isSmartPointer) { - handle = toType.smartPointerType._fromWireType(ptr); + handle = makeHandle(toType.smartPointerType, ptr); } else { - handle = toType._fromWireType(ptr); + handle = makeHandle(toType, ptr); } handle.$$.ptr = staticPointerCast(handle.$$.ptr, this.registeredClass.rawType, toType.rawType); } else { - handle = this._fromWireType(ptr); + handle = makeHandle(this, ptr); } return handle; }; From 9ec4c29864a967186156a95a9ec8062d05ffa3c9 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Tue, 19 Mar 2013 01:15:49 -0700 Subject: [PATCH 205/258] Kill some embind duplication --- src/embind/embind.js | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 520ca269d..de87dceb7 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -661,6 +661,14 @@ function RegisteredClass( this.downcast = downcast; } +function shallowCopy(o) { + var rv = {}; + for (var k in o) { + rv[k] = o[k]; + } + return rv; +} + function __embind_register_class( rawType, rawPointerType, @@ -726,10 +734,7 @@ function __embind_register_class( var clone = Object.create(Handle.prototype); Object.defineProperty(clone, '$$', { - value: { - count: this.$$.count, - ptr: this.$$.ptr, - }, + value: shallowCopy(this.$$), }); clone.$$.count.value += 1; @@ -998,11 +1003,7 @@ function __embind_register_smart_ptr( var clone = Object.create(Handle.prototype); Object.defineProperty(clone, '$$', { - value: { - count: this.$$.count, - smartPtr: this.$$.smartPtr, - ptr: this.$$.ptr, - }, + value: shallowCopy(this.$$), }); clone.$$.count.value += 1; From 09effb641c418d7b8751b04f215d7192075b72cb Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Tue, 19 Mar 2013 03:11:45 -0700 Subject: [PATCH 206/258] Simplify upcasting/downcasting, allow multiple smart pointer types per class, and use the same ClassHandle prototype so class handles and smart pointer handles share implementations for .clone() and .delete() --- src/embind/embind.js | 304 +++++++++++++++++-------------- system/include/emscripten/bind.h | 6 +- 2 files changed, 171 insertions(+), 139 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index de87dceb7..c1f9552d9 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -3,12 +3,14 @@ /*global FUNCTION_TABLE, HEAP32, HEAPU8*/ /*global Pointer_stringify*/ /*global __emval_register, _emval_handle_array, __emval_decref*/ -/*global ___getDynamicPointerType: false*/ /*global ___typeName:false*/ -/*global ___staticPointerCast: false*/ +var InternalError = Module.InternalError = extendError(Error, 'InternalError'); var BindingError = Module.BindingError = extendError(Error, 'BindingError'); -var CastError = Module.CastError = extendError(BindingError, 'CastError'); + +function throwInternalError(value) { + throw new InternalError(value); +} function throwBindingError(value) { throw new BindingError(value); @@ -75,6 +77,9 @@ var registeredTypes = {}; // typeID -> [callback] var awaitingDependencies = {}; +// class typeID -> {pointerType: ..., constPointerType: ...} +var registeredPointers = {}; + function registerType(rawType, registeredInstance) { var name = registeredInstance.name; if (!rawType) { @@ -144,17 +149,6 @@ function requireRegisteredType(rawType, humanName) { return impl; } -function staticPointerCast(from, fromType, toType) { - if (!from) { - return from; - } - var to = ___staticPointerCast(from, fromType, toType); - if (to <= 0) { - throw new CastError("Pointer conversion from " + typeName(fromType) + " to " + typeName(toType) + " is not available"); - } - return to; -} - function __embind_register_void(rawType, name) { name = Pointer_stringify(name); registerType(rawType, { @@ -468,7 +462,7 @@ function __embind_register_struct_field( }); } -// I guarantee there is a way to simplify the following data structure. +// todo: I guarantee there is a way to simplify the following data structure. function RegisteredPointer( name, rawType, @@ -477,6 +471,7 @@ function RegisteredPointer( isReference, isConst, isSmartPointer, + pointeeType, sharingPolicy, rawGetPointee, rawConstructor, @@ -490,6 +485,7 @@ function RegisteredPointer( this.isReference = isReference; this.isConst = isConst; this.isSmartPointer = isSmartPointer; + this.pointeeType = pointeeType; this.sharingPolicy = sharingPolicy; this.rawGetPointee = rawGetPointee; this.rawConstructor = rawConstructor; @@ -497,14 +493,16 @@ function RegisteredPointer( this.rawDestructor = rawDestructor; } -RegisteredPointer.prototype.isPolymorphic = function() { - return this.registeredClass.isPolymorphic; -}; - RegisteredPointer.prototype.toWireType = function(destructors, handle) { var self = this; function throwCannotConvert() { - throwBindingError('Cannot convert argument of type ' + handle.$$.ptrType.name + ' to parameter type ' + self.name); + var name; + if (handle.$$.smartPtrType) { + name = handle.$$.smartPtrType.name; + } else { + name = handle.$$.ptrType.name; + } + throwBindingError('Cannot convert argument of type ' + name + ' to parameter type ' + self.name); } if (handle === null) { @@ -537,7 +535,7 @@ RegisteredPointer.prototype.toWireType = function(destructors, handle) { switch (this.sharingPolicy) { case 0: // NONE // no upcasting - if (handle.$$.ptrType === this) { + if (handle.$$.smartPtrType === this) { ptr = handle.$$.smartPtr; } else { throwCannotConvert(); @@ -549,7 +547,7 @@ RegisteredPointer.prototype.toWireType = function(destructors, handle) { break; case 2: // BY_EMVAL - if (handle.$$.ptrType === this) { + if (handle.$$.smartPtrType === this) { ptr = handle.$$.smartPtr; } else { var clonedHandle = handle.clone(); @@ -583,68 +581,133 @@ RegisteredPointer.prototype.destructor = function(ptr) { } }; -RegisteredPointer.prototype.getDynamicRawPointerType = function(ptr) { - var type = null; - if (this.isPolymorphic()) { - if (this.rawGetPointee) { - type = ___getDynamicPointerType(this.rawGetPointee(ptr)); - } else { - type = ___getDynamicPointerType(ptr); - } - } - return type; -}; +RegisteredPointer.prototype.fromWireType = function(ptr) { + // ptr is a raw pointer (or a raw smartpointer) -RegisteredPointer.prototype.getDynamicDowncastType = function(ptr) { - var downcastType = null; - var type = this.getDynamicRawPointerType(ptr); - if (type && type !== this.registeredClass.rawType) { - var derivation = Module.__getDerivationPath(type, this.registeredClass.rawType); - for (var i = 0; i < derivation.size(); i++) { - downcastType = registeredTypes[derivation.get(i)]; - if (downcastType && (!this.isSmartPointer || downcastType.smartPointerType)) { - break; - } - } - derivation.delete(); - } - return downcastType; -}; - -RegisteredPointer.prototype.fromWireType = function(ptr) { // ptr is a raw pointer (or a raw smartpointer) - function makeHandle(registeredType, ptr) { - return new registeredType.Handle(registeredType, ptr); - } - - var handle; - if (!this.getPointee(ptr)) { + // rawPointer is a maybe-null raw pointer + var rawPointer = this.getPointee(ptr); + if (!rawPointer) { this.destructor(ptr); return null; } - var toType = this.getDynamicDowncastType(ptr); - if (toType) { + + function makeDefaultHandle() { if (this.isSmartPointer) { - handle = makeHandle(toType.smartPointerType, ptr); + return makeClassHandle(this.Handle.prototype, { + ptrType: this.pointeeType, + ptr: rawPointer, + smartPtrType: this, + smartPtr: ptr, + }); } else { - handle = makeHandle(toType, ptr); + return makeClassHandle(this.Handle.prototype, { + ptrType: this, + ptr: ptr, + }); } - handle.$$.ptr = staticPointerCast(handle.$$.ptr, this.registeredClass.rawType, toType.rawType); - } else { - handle = makeHandle(this, ptr); } - return handle; + + var actualType = this.registeredClass.getActualType(rawPointer); + var registeredPointerRecord = registeredPointers[actualType]; + if (!registeredPointerRecord) { + return makeDefaultHandle.call(this); + } + + var toType; + if (this.isConst) { + toType = registeredPointerRecord.constPointerType; + } else { + toType = registeredPointerRecord.pointerType; + } + var dp = downcastPointer( + rawPointer, + this.registeredClass, + toType.registeredClass); + if (dp === null) { + return makeDefaultHandle.call(this); + } + if (this.isSmartPointer) { + return makeClassHandle(toType.Handle.prototype, { + ptrType: toType, + ptr: dp, + smartPtrType: this, + smartPtr: ptr, + }); + } else { + return makeClassHandle(toType.Handle.prototype, { + ptrType: toType, + ptr: dp, + }); + } }; +function makeClassHandle(prototype, record) { + if (!record.ptrType || !record.ptr) { + throwInternalError('makeClassHandle requires ptr and ptrType'); + } + var hasSmartPtrType = !!record.smartPtrType; + var hasSmartPtr = !!record.smartPtr; + if (hasSmartPtrType !== hasSmartPtr) { + throwInternalError('Both smartPtrType and smartPtr must be specified'); + } + record.count = { value: 1 }; + return Object.create(prototype, { + '$$': { + value: record, + }, + }); +} + // root of all pointer and smart pointer handles in embind function ClassHandle() { } +function getInstanceTypeName(handle) { + return handle.$$.ptrType.registeredClass.name; +} + +ClassHandle.prototype.clone = function() { + if (!this.$$.ptr) { + throwBindingError(getInstanceTypeName(this) + ' instance already deleted'); + } + + var clone = Object.create(Object.getPrototypeOf(this), { + '$$': { + value: shallowCopy(this.$$), + } + }); + + clone.$$.count.value += 1; + return clone; +}; + +function runDestructor(handle) { + var $$ = handle.$$; + if ($$.smartPtr) { + $$.smartPtrType.rawDestructor($$.smartPtr); + } else { + $$.ptrType.registeredClass.rawDestructor($$.ptr); + } +} + +ClassHandle.prototype['delete'] = function() { + if (!this.$$.ptr) { + throwBindingError(getInstanceTypeName(this) + ' instance already deleted'); + } + + this.$$.count.value -= 1; + if (0 === this.$$.count.value) { + runDestructor(this); + } + this.$$.smartPtr = undefined; + this.$$.ptr = undefined; +}; + function RegisteredClass( name, rawType, constructor, - isPolymorphic, - baseClassRawType, + rawDestructor, baseClass, getActualType, upcast, @@ -653,8 +716,7 @@ function RegisteredClass( this.name = name; this.rawType = rawType; this.constructor = constructor; - this.isPolymorphic = isPolymorphic; - this.baseClassRawType = baseClassRawType; + this.rawDestructor = rawDestructor; this.baseClass = baseClass; this.getActualType = getActualType; this.upcast = upcast; @@ -677,7 +739,6 @@ function __embind_register_class( getActualType, upcast, downcast, - isPolymorphic, name, rawDestructor ) { @@ -713,46 +774,20 @@ function __embind_register_class( }); }); + Handle.prototype = Object.create(basePrototype, { + constructor: { value: Handle }, + }); + var registeredClass = new RegisteredClass( name, rawType, Handle, - isPolymorphic, - baseClassRawType, + rawDestructor, baseClass, getActualType, upcast, downcast); - Handle.prototype = Object.create(basePrototype, { - constructor: { value: Handle }, - }); - Handle.prototype.clone = function() { - if (!this.$$.ptr) { - throwBindingError(type.name + ' instance already deleted'); - } - - var clone = Object.create(Handle.prototype); - Object.defineProperty(clone, '$$', { - value: shallowCopy(this.$$), - }); - - clone.$$.count.value += 1; - return clone; - }; - - Handle.prototype['delete'] = function() { - if (!this.$$.ptr) { - throwBindingError(type.name + ' instance already deleted'); - } - - this.$$.count.value -= 1; - if (0 === this.$$.count.value) { - rawDestructor(this.$$.ptr); - } - this.$$.ptr = undefined; - }; - // todo: clean this up! var type = new RegisteredPointer( name, @@ -764,23 +799,30 @@ function __embind_register_class( false); registerType(rawType, type); - registerType(rawPointerType, new RegisteredPointer( + var pointerType = new RegisteredPointer( name + '*', rawPointerType, registeredClass, Handle, false, false, - false)); + false); + registerType(rawPointerType, pointerType); - registerType(rawConstPointerType, new RegisteredPointer( + var constPointerType = new RegisteredPointer( name + ' const*', rawConstPointerType, registeredClass, Handle, false, true, - false)); + false); + registerType(rawConstPointerType, constPointerType); + + registeredPointers[rawType] = { + pointerType: pointerType, + constPointerType: constPointerType + }; type.constructor = createNamedFunction(legalFunctionName, function() { if (Object.getPrototypeOf(this) !== Handle.prototype) { @@ -832,6 +874,18 @@ function __embind_register_class_constructor( }); } +function downcastPointer(ptr, ptrClass, desiredClass) { + if (ptrClass === desiredClass) { + return ptr; + } + if (undefined === desiredClass.baseClass) { + return null; // no conversion + } + // O(depth) stack space used + return desiredClass.downcast( + downcastPointer(ptr, ptrClass, desiredClass.baseClass)); +} + function upcastPointer(ptr, ptrClass, desiredClass) { while (ptrClass !== desiredClass) { ptr = ptrClass.upcast(ptr); @@ -983,46 +1037,23 @@ function __embind_register_smart_ptr( whenDependentTypesAreResolved([rawPointeeType], function(pointeeType) { pointeeType = pointeeType[0]; - var Handle = createNamedFunction(makeLegalFunctionName(name), function(ptrType, ptr) { + var Handle = createNamedFunction(makeLegalFunctionName(name), function(smartPtrType, smartPtr, ptrType, ptr) { + if (arguments.length !== 4) { + throwBindingError("internal error"); + } Object.defineProperty(this, '$$', { value: { - ptrType: ptrType, count: {value: 1}, - smartPtr: ptr, - ptr: rawGetPointee(ptr), + ptrType: ptrType, + ptr: ptr, + smartPtrType: registeredPointer, + smartPtr: smartPtr, }, }); }); Handle.prototype = Object.create(pointeeType.Handle.prototype); - Handle.prototype.clone = function() { - if (!this.$$.ptr) { - throwBindingError(pointeeType.name + ' instance already deleted'); - } - - var clone = Object.create(Handle.prototype); - Object.defineProperty(clone, '$$', { - value: shallowCopy(this.$$), - }); - - clone.$$.count.value += 1; - return clone; - }; - - Handle.prototype['delete'] = function() { - if (!this.$$.ptr) { - throwBindingError(pointeeType.name + ' instance already deleted'); - } - - this.$$.count.value -= 1; - if (0 === this.$$.count.value) { - rawDestructor(this.$$.smartPtr); - } - this.$$.smartPtr = undefined; - this.$$.ptr = undefined; - }; - var registeredPointer = new RegisteredPointer( name, rawType, @@ -1030,14 +1061,15 @@ function __embind_register_smart_ptr( Handle, false, false, + // smart pointer properties true, + pointeeType, sharingPolicy, rawGetPointee, rawConstructor, rawShare, rawDestructor); registerType(rawType, registeredPointer); - pointeeType.smartPointerType = registeredPointer; }); } diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index cd208d370..a1b2a015f 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -117,7 +117,6 @@ namespace emscripten { GenericFunction getActualType, GenericFunction upcast, GenericFunction downcast, - bool isPolymorphic, const char* className, GenericFunction destructor); @@ -650,9 +649,11 @@ namespace emscripten { } }; + // NOTE: this returns the class type, not the pointer type template inline TYPEID getActualType(T* ptr) { - return reinterpret_cast(&typeid(ptr)); + assert(ptr); + return reinterpret_cast(&typeid(*ptr)); }; } @@ -720,7 +721,6 @@ namespace emscripten { reinterpret_cast(&getActualType), BaseSpecifier::template getUpcaster(), BaseSpecifier::template getDowncaster(), - std::is_polymorphic::value, // TODO: may not be necessary name, reinterpret_cast(&raw_destructor)); } From 78568fe4c2ba8fd014fd8af23453ac8b2d0728f5 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Tue, 19 Mar 2013 03:22:47 -0700 Subject: [PATCH 207/258] Remove some dead code --- src/embind/embind.js | 4 + system/lib/embind/bind.cpp | 208 ++++--------------------------------- 2 files changed, 27 insertions(+), 185 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index c1f9552d9..35d9dfdf5 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -470,6 +470,8 @@ function RegisteredPointer( Handle, isReference, isConst, + + // smart pointer properties isSmartPointer, pointeeType, sharingPolicy, @@ -484,6 +486,8 @@ function RegisteredPointer( this.Handle = Handle; // <-- I think I can kill this this.isReference = isReference; this.isConst = isConst; + + // smart pointer properties this.isSmartPointer = isSmartPointer; this.pointeeType = pointeeType; this.sharingPolicy = sharingPolicy; diff --git a/system/lib/embind/bind.cpp b/system/lib/embind/bind.cpp index 6c792f787..e844e52e5 100755 --- a/system/lib/embind/bind.cpp +++ b/system/lib/embind/bind.cpp @@ -9,167 +9,9 @@ using namespace emscripten; -namespace __cxxabiv1 { - std::vector __internalGetBaseClasses(const __class_type_info* cti) { - std::vector bases; - - const __si_class_type_info* scti = dynamic_cast(cti); - if (scti) { - bases.emplace_back(scti->__base_type); - } else { - const __vmi_class_type_info* vcti = dynamic_cast(cti); - if (vcti) { - for (int i = 0; i < vcti->__base_count; i++) { - bases.emplace_back(vcti->__base_info[i].__base_type); - } - } - } - return bases; - } - - int __getBaseOffset(const __class_type_info* ctiDv, const __class_type_info* ctiBs) { - int offset = 0; - - const __vmi_class_type_info* vcti = dynamic_cast(ctiDv); - if (vcti) { - for (int i = 0; i < vcti->__base_count; i++) { - if (vcti->__base_info[i].__base_type == ctiBs) { - offset = vcti->__base_info[i].__offset_flags >> __base_class_type_info::__offset_shift; - break; - } - } - } - return offset; - } - - void __getDerivationPaths(const __class_type_info* dv, const __class_type_info* bs, std::vectorpath, std::vector>& paths) { - std::vector newPath(path); - newPath.emplace_back(dv); - if (dv == bs) { - paths.emplace_back(newPath); - } else { - std::vector bases = __internalGetBaseClasses(dv); - for (int i = 0; i < bases.size(); i++) { - __getDerivationPaths(bases[i], bs, newPath, paths); - } - } - } - - int __pathOffset(std::vector path) { - int offset = 0; - for (int i = 0; i < path.size()-1; i++) { - offset += __getBaseOffset(path[i], path[i+1]); - } - return offset; - } -} - namespace emscripten { namespace internal { - // __getDerivationPath returns an array of type_info pointers describing the derivation chain starting with - // the derived type and proceeding toward (and ending with) the base type. Types are only included if they - // appear on all possible derivation paths. - std::vector __getDerivationPath(int dv, const int bs) { - std::vector> paths; - - 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(dv1); - const __cxxabiv1::__class_type_info* bs2 = dynamic_cast(bs1); - - if (dv2 && bs2) { - __cxxabiv1::__getDerivationPaths(dv2, bs2, std::vector(), paths); - } - - std::vector derivationPath; - if (paths.size() > 0) { - for (int j = 0; j < paths[0].size(); j++) { - bool disqualified = false; - for (int i = 1; i < paths.size(); i++) { - bool found = false; - for (int j2 = 0; j2 < paths[i].size(); j2++) { - if (paths[i][j2] == paths[0][j]) { - found = true; - break; - } - } - if (!found) { - disqualified = true; - break; - } - } - if (!disqualified) { - derivationPath.emplace_back((int)paths[0][j]); - } - } - } - return derivationPath; - } - extern "C" { - // These three routines constitute an extension to the compiler's support for dynamic type conversion. - // They are used by embind.js to implement automatic downcasting of return values which are pointers to - // polymorphic objects. - - void* EMSCRIPTEN_KEEPALIVE __staticPointerCast(void* p, const void* from, void* to) { - std::vector> paths; - int direction = 1; - - 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(from1); - const __cxxabiv1::__class_type_info* to2 = dynamic_cast(to1); - - if (from2 && to2) { - __cxxabiv1::__getDerivationPaths(from2, to2, std::vector(), paths); - if (paths.size() == 0) { - __cxxabiv1::__getDerivationPaths(to2, from2, std::vector(), paths); - direction = -1; - } - } - - int offset = -1; - for (int i = 0; i < paths.size(); i++) { - if (offset < 0) { - offset = __cxxabiv1::__pathOffset(paths[i]); - } else { - if (offset != __cxxabiv1::__pathOffset(paths[i])) { - return (void *)-2; - } - } - } - if (offset < 0) { - return (void *)-1; - } - if (p == 0) { - return (void *)0; - } - return (void *)((int)p + offset * direction); - } - - // __getDynamicPointerType returns (for polymorphic types only!) the type of the instance actually - // pointed to. - const void* EMSCRIPTEN_KEEPALIVE __getDynamicPointerType(void* p) { - void** vtable = *reinterpret_cast(p); - return vtable[-1]; - } - - // Calls to __dynamic_cast are generated by the compiler to implement dynamic_cast<>() -- its prototype is - // not available through any header file. It is called directly here because it allows run-time - // specification of the target pointer type (which can only be specified at compile time when using - // dynamic_cast<>(). - void* __dynamic_cast(void*, const std::type_info*, const std::type_info*, void*); - - // __dynamicPointerCast performs a C++ dynamic_cast<>() operation, but allowing run-time specification of - // the from and to pointer types. - void* EMSCRIPTEN_KEEPALIVE __dynamicPointerCast(void* p, void* to) { - void* ret = __staticPointerCast(p, __getDynamicPointerType(p), to); - if (ret < 0) { - return 0; - } - return ret; - } - const char* EMSCRIPTEN_KEEPALIVE __typeName(const std::type_info* ti) { size_t nameLen = std::min(strlen(ti->name()), 1024U); char* name = (char *)malloc(nameLen+1); @@ -188,33 +30,6 @@ namespace emscripten { return *reinterpret_cast(p); } - EMSCRIPTEN_BINDINGS(([]() { - _embind_register_void(TypeID::get(), "void"); - - _embind_register_bool(TypeID::get(), "bool", true, false); - - _embind_register_integer(TypeID::get(), "char"); - _embind_register_integer(TypeID::get(), "signed char"); - _embind_register_integer(TypeID::get(), "unsigned char"); - _embind_register_integer(TypeID::get(), "short"); - _embind_register_integer(TypeID::get(), "unsigned short"); - _embind_register_integer(TypeID::get(), "int"); - _embind_register_integer(TypeID::get(), "unsigned int"); - _embind_register_integer(TypeID::get(), "long"); - _embind_register_integer(TypeID::get(), "unsigned long"); - - _embind_register_float(TypeID::get(), "float"); - _embind_register_float(TypeID::get(), "double"); - - _embind_register_cstring(TypeID::get(), "std::string"); - _embind_register_emval(TypeID::get(), "emscripten::val"); - - // We bind __getDerivationPath in order to take advantage of the std::vector to Javascript array - // conversion for the return value. This has the unfortunate side-effect of exposing it to third party - // developers, but perhaps the double underscore will scare them away from calling it. - function("__getDerivationPath", &__getDerivationPath); - function("__peek32", &__peek32, allow_raw_pointers()); - })); } JSInterface* create_js_interface(EM_VAL e) { @@ -223,3 +38,26 @@ namespace emscripten { } } +EMSCRIPTEN_BINDINGS(([]() { + using namespace emscripten::internal; + + _embind_register_void(TypeID::get(), "void"); + + _embind_register_bool(TypeID::get(), "bool", true, false); + + _embind_register_integer(TypeID::get(), "char"); + _embind_register_integer(TypeID::get(), "signed char"); + _embind_register_integer(TypeID::get(), "unsigned char"); + _embind_register_integer(TypeID::get(), "short"); + _embind_register_integer(TypeID::get(), "unsigned short"); + _embind_register_integer(TypeID::get(), "int"); + _embind_register_integer(TypeID::get(), "unsigned int"); + _embind_register_integer(TypeID::get(), "long"); + _embind_register_integer(TypeID::get(), "unsigned long"); + + _embind_register_float(TypeID::get(), "float"); + _embind_register_float(TypeID::get(), "double"); + + _embind_register_cstring(TypeID::get(), "std::string"); + _embind_register_emval(TypeID::get(), "emscripten::val"); +})); From 4c10a08fc0d4f070a1e770c28aacfed226021456 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Tue, 19 Mar 2013 03:42:26 -0700 Subject: [PATCH 208/258] Simplify things even further, remove the Handle functions and replace with an instance prototype and a constructor function. --- src/embind/embind.js | 88 +++++++++++++++----------------------------- 1 file changed, 29 insertions(+), 59 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 35d9dfdf5..e839793c1 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -467,7 +467,6 @@ function RegisteredPointer( name, rawType, registeredClass, - Handle, isReference, isConst, @@ -483,7 +482,6 @@ function RegisteredPointer( this.name = name; this.rawType = rawType; this.registeredClass = registeredClass; - this.Handle = Handle; // <-- I think I can kill this this.isReference = isReference; this.isConst = isConst; @@ -597,14 +595,14 @@ RegisteredPointer.prototype.fromWireType = function(ptr) { function makeDefaultHandle() { if (this.isSmartPointer) { - return makeClassHandle(this.Handle.prototype, { + return makeClassHandle(this.registeredClass.instancePrototype, { ptrType: this.pointeeType, ptr: rawPointer, smartPtrType: this, smartPtr: ptr, }); } else { - return makeClassHandle(this.Handle.prototype, { + return makeClassHandle(this.registeredClass.instancePrototype, { ptrType: this, ptr: ptr, }); @@ -631,14 +629,14 @@ RegisteredPointer.prototype.fromWireType = function(ptr) { return makeDefaultHandle.call(this); } if (this.isSmartPointer) { - return makeClassHandle(toType.Handle.prototype, { + return makeClassHandle(toType.registeredClass.instancePrototype, { ptrType: toType, ptr: dp, smartPtrType: this, smartPtr: ptr, }); } else { - return makeClassHandle(toType.Handle.prototype, { + return makeClassHandle(toType.registeredClass.instancePrototype, { ptrType: toType, ptr: dp, }); @@ -711,6 +709,7 @@ function RegisteredClass( name, rawType, constructor, + instancePrototype, rawDestructor, baseClass, getActualType, @@ -720,6 +719,7 @@ function RegisteredClass( this.name = name; this.rawType = rawType; this.constructor = constructor; + this.instancePrototype = instancePrototype; this.rawDestructor = rawDestructor; this.baseClass = baseClass; this.getActualType = getActualType; @@ -763,29 +763,33 @@ function __embind_register_class( baseClasses[rawType] = baseClassRawType; baseClass = base.registeredClass; - basePrototype = base.Handle.prototype; + basePrototype = baseClass.instancePrototype; } else { basePrototype = ClassHandle.prototype; } - var Handle = createNamedFunction(legalFunctionName, function(ptrType, ptr) { - Object.defineProperty(this, '$$', { - value: { - ptrType: ptrType, - count: { value: 1 }, - ptr: ptr, - } - }); + var constructor = createNamedFunction(legalFunctionName, function() { + if (Object.getPrototypeOf(this) !== instancePrototype) { + throw new BindingError("Use 'new' to construct " + name); + } + var body = registeredClass.constructor_body; + if (undefined === body) { + throw new BindingError(name + " has no accessible constructor"); + } + return body.apply(this, arguments); }); - - Handle.prototype = Object.create(basePrototype, { - constructor: { value: Handle }, + + var instancePrototype = Object.create(basePrototype, { + constructor: { value: constructor }, }); + constructor.prototype = instancePrototype; + var registeredClass = new RegisteredClass( name, rawType, - Handle, + constructor, + instancePrototype, rawDestructor, baseClass, getActualType, @@ -797,7 +801,6 @@ function __embind_register_class( name, rawType, registeredClass, - Handle, true, false, false); @@ -807,7 +810,6 @@ function __embind_register_class( name + '*', rawPointerType, registeredClass, - Handle, false, false, false); @@ -817,7 +819,6 @@ function __embind_register_class( name + ' const*', rawConstPointerType, registeredClass, - Handle, false, true, false); @@ -828,20 +829,7 @@ function __embind_register_class( constPointerType: constPointerType }; - type.constructor = createNamedFunction(legalFunctionName, function() { - if (Object.getPrototypeOf(this) !== Handle.prototype) { - throw new BindingError("Use 'new' to construct " + name); - } - var body = type.constructor_body; - if (undefined === body) { - throw new BindingError(name + " has no accessible constructor"); - } - return body.apply(this, arguments); - }); - type.constructor.prototype = type.Handle.prototype; - type.constructor.type = type; - - exposePublicSymbol(legalFunctionName, type.constructor); + exposePublicSymbol(legalFunctionName, constructor); }); } @@ -859,7 +847,7 @@ function __embind_register_class_constructor( var classType = argTypes[0]; argTypes = argTypes.slice(1); var humanName = 'constructor ' + classType.name; - classType.constructor_body = function() { + classType.registeredClass.constructor_body = function() { if (arguments.length !== argCount - 1) { throwBindingError(humanName + ' called with ' + arguments.length + ' arguments, expected ' + (argCount-1)); } @@ -902,7 +890,7 @@ function validateThis(this_, classType, humanName) { if (!(this_ instanceof Object)) { throwBindingError(humanName + ' with invalid "this": ' + this_); } - if (!(this_ instanceof classType.constructor)) { + if (!(this_ instanceof classType.registeredClass.constructor)) { throwBindingError(humanName + ' incompatible with "this" of type ' + this_.constructor.name); } if (!this_.$$.ptr) { @@ -934,7 +922,7 @@ function __embind_register_class_function( var classType = argTypes[0]; argTypes = argTypes.slice(1); var humanName = classType.name + '.' + methodName; - classType.Handle.prototype[methodName] = function() { + classType.registeredClass.instancePrototype[methodName] = function() { if (arguments.length !== argCount - 1) { throwBindingError('emscripten binding method ' + humanName + ' called with ' + arguments.length + ' arguments, expected ' + (argCount-1)); } @@ -973,7 +961,7 @@ function __embind_register_class_class_function( rawInvoker = FUNCTION_TABLE[rawInvoker]; whenDependentTypesAreResolved(rawArgTypes, function(argTypes) { var humanName = classType.name + '.' + methodName; - classType.constructor[methodName] = makeInvoker(humanName, argCount, argTypes, rawInvoker, fn); + classType.registeredClass.constructor[methodName] = makeInvoker(humanName, argCount, argTypes, rawInvoker, fn); }); } @@ -994,7 +982,7 @@ function __embind_register_class_property( var classType = converters[0]; var fieldType = converters[1]; var humanName = classType.name + '.' + fieldName; - Object.defineProperty(classType.Handle.prototype, fieldName, { + Object.defineProperty(classType.registeredClass.instancePrototype, fieldName, { get: function() { var ptr = validateThis(this, classType, humanName + ' getter'); return fieldType.fromWireType(getter(ptr, memberPointer)); @@ -1041,28 +1029,10 @@ function __embind_register_smart_ptr( whenDependentTypesAreResolved([rawPointeeType], function(pointeeType) { pointeeType = pointeeType[0]; - var Handle = createNamedFunction(makeLegalFunctionName(name), function(smartPtrType, smartPtr, ptrType, ptr) { - if (arguments.length !== 4) { - throwBindingError("internal error"); - } - Object.defineProperty(this, '$$', { - value: { - count: {value: 1}, - ptrType: ptrType, - ptr: ptr, - smartPtrType: registeredPointer, - smartPtr: smartPtr, - }, - }); - }); - - Handle.prototype = Object.create(pointeeType.Handle.prototype); - var registeredPointer = new RegisteredPointer( name, rawType, pointeeType.registeredClass, - Handle, false, false, // smart pointer properties From a39afe6758e9aadffdd526a7f646293d31389e54 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Tue, 19 Mar 2013 03:46:39 -0700 Subject: [PATCH 209/258] Code keeps disappearing :) --- src/embind/embind.js | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index e839793c1..48646dd75 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -465,7 +465,6 @@ function __embind_register_struct_field( // todo: I guarantee there is a way to simplify the following data structure. function RegisteredPointer( name, - rawType, registeredClass, isReference, isConst, @@ -480,7 +479,6 @@ function RegisteredPointer( rawDestructor ) { this.name = name; - this.rawType = rawType; this.registeredClass = registeredClass; this.isReference = isReference; this.isConst = isConst; @@ -707,7 +705,6 @@ ClassHandle.prototype['delete'] = function() { function RegisteredClass( name, - rawType, constructor, instancePrototype, rawDestructor, @@ -717,7 +714,6 @@ function RegisteredClass( downcast ) { this.name = name; - this.rawType = rawType; this.constructor = constructor; this.instancePrototype = instancePrototype; this.rawDestructor = rawDestructor; @@ -758,7 +754,6 @@ function __embind_register_class( var baseClass; var basePrototype; - var depth; if (baseClassRawType) { baseClasses[rawType] = baseClassRawType; @@ -787,7 +782,6 @@ function __embind_register_class( var registeredClass = new RegisteredClass( name, - rawType, constructor, instancePrototype, rawDestructor, @@ -796,19 +790,15 @@ function __embind_register_class( upcast, downcast); - // todo: clean this up! - var type = new RegisteredPointer( + registerType(rawType, new RegisteredPointer( name, - rawType, registeredClass, true, false, - false); - registerType(rawType, type); + false)); var pointerType = new RegisteredPointer( name + '*', - rawPointerType, registeredClass, false, false, @@ -817,7 +807,6 @@ function __embind_register_class( var constPointerType = new RegisteredPointer( name + ' const*', - rawConstPointerType, registeredClass, false, true, @@ -1031,7 +1020,6 @@ function __embind_register_smart_ptr( var registeredPointer = new RegisteredPointer( name, - rawType, pointeeType.registeredClass, false, false, From f6b4ee3e883db2dd787fcde94909c5e91b2f8a92 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Tue, 19 Mar 2013 03:49:20 -0700 Subject: [PATCH 210/258] RegisteredPointer and RegisteredClass are pretty simple now. --- src/embind/embind.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 48646dd75..caec79780 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -462,7 +462,6 @@ function __embind_register_struct_field( }); } -// todo: I guarantee there is a way to simplify the following data structure. function RegisteredPointer( name, registeredClass, From fe2bcf89921105ad3719655ec59905cc63be5c3d Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Tue, 19 Mar 2013 14:56:06 -0700 Subject: [PATCH 211/258] baseClasses is not necessary --- src/embind/embind.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index caec79780..48bf2a7f1 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -69,8 +69,6 @@ function _embind_repr(v) { } } -var baseClasses = {}; // rawType -> rawBaseType - // typeID -> { toWireType: ..., fromWireType: ... } var registeredTypes = {}; @@ -521,7 +519,8 @@ RegisteredPointer.prototype.toWireType = function(destructors, handle) { throwBindingError('Expected null or instance of ' + this.name + ', got ' + IMVU.repr(handle)); } // TODO: this is not strictly true - // It seems legal to support BY_EMVAL and INTRUSIVE conversions from raw pointers to smart pointers + // We could support BY_EMVAL conversions from raw pointers to smart pointers + // because the smart pointer can hold a reference to the handle if (this.isSmartPointer && undefined === handle.$$.smartPtr) { throwBindingError('Passing raw pointer to smart pointer is illegal'); } @@ -754,8 +753,6 @@ function __embind_register_class( var baseClass; var basePrototype; if (baseClassRawType) { - baseClasses[rawType] = baseClassRawType; - baseClass = base.registeredClass; basePrototype = baseClass.instancePrototype; } else { From 6f219fd0caad22aadad34dd46bc8043d299b4299 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Tue, 19 Mar 2013 16:43:03 -0700 Subject: [PATCH 212/258] Change EMSCRIPTEN_BINDINGS syntax and allow for out-of-order registration of value_tuple elements. --- src/embind/embind.js | 8 +++++--- system/include/emscripten/bind.h | 26 +++++++++++++++++--------- system/lib/embind/bind.cpp | 4 ++-- 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 48bf2a7f1..5dd631ff6 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -339,10 +339,12 @@ function __embind_register_tuple_element( memberPointer = copyMemberPointer(memberPointer, memberPointerSize); var tupleType = requireRegisteredType(rawTupleType, 'tuple'); - // TODO: this could register elements out of order + var index = tupleType.elements.length; + tupleType.elements.push(undefined); + whenDependentTypesAreResolved([rawType], function(type) { type = type[0]; - tupleType.elements.push({ + tupleType.elements[index] = { read: function(ptr) { return type.fromWireType(getter(ptr, memberPointer)); }, @@ -351,7 +353,7 @@ function __embind_register_tuple_element( setter(ptr, memberPointer, type.toWireType(destructors, o)); runDestructors(destructors); } - }); + }; }); } diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index a1b2a015f..b7b17f20b 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -169,14 +169,6 @@ namespace emscripten { GenericFunction constructor, GenericFunction destructor); } - - class BindingsDefinition { - public: - template - BindingsDefinition(Function fn) { - fn(); - } - }; } } @@ -1155,4 +1147,20 @@ namespace emscripten { }; } -#define EMSCRIPTEN_BINDINGS(fn) static emscripten::internal::BindingsDefinition anon_symbol(fn); +namespace emscripten { + namespace internal { + class BindingsDefinition { + public: + template + BindingsDefinition(Function fn) { + fn(); + } + }; + } +} + +#define EMSCRIPTEN_BINDINGS(name) \ + static struct BindingInitializer_##name { \ + BindingInitializer_##name(); \ + } BindingInitializer_##name##_instance; \ + BindingInitializer_##name::BindingInitializer_##name() diff --git a/system/lib/embind/bind.cpp b/system/lib/embind/bind.cpp index e844e52e5..6994ef0cb 100755 --- a/system/lib/embind/bind.cpp +++ b/system/lib/embind/bind.cpp @@ -38,7 +38,7 @@ namespace emscripten { } } -EMSCRIPTEN_BINDINGS(([]() { +EMSCRIPTEN_BINDINGS(native_and_builtin_types) { using namespace emscripten::internal; _embind_register_void(TypeID::get(), "void"); @@ -60,4 +60,4 @@ EMSCRIPTEN_BINDINGS(([]() { _embind_register_cstring(TypeID::get(), "std::string"); _embind_register_emval(TypeID::get(), "emscripten::val"); -})); +} From 46d7a60c3bf776b2994d091f2308ac777540f40c Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Tue, 19 Mar 2013 17:04:36 -0700 Subject: [PATCH 213/258] yay, out-of-order registration is tested --- src/embind/embind.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 5dd631ff6..d3ab0bf55 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -342,6 +342,7 @@ function __embind_register_tuple_element( var index = tupleType.elements.length; tupleType.elements.push(undefined); + // TODO: test incomplete registration of value tuples whenDependentTypesAreResolved([rawType], function(type) { type = type[0]; tupleType.elements[index] = { @@ -446,7 +447,7 @@ function __embind_register_struct_field( rawGetter = FUNCTION_TABLE[rawGetter]; rawSetter = FUNCTION_TABLE[rawSetter]; memberPointer = copyMemberPointer(memberPointer, memberPointerSize); - // TODO: this could register elements out of order + // TODO: test incomplete registration of value structs whenDependentTypesAreResolved([rawFieldType], function(fieldType) { fieldType = fieldType[0]; structType.fields[fieldName] = { From 41b65f63c49bdbdc185d108885ea1c791994c99a Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Wed, 20 Mar 2013 13:52:09 -0700 Subject: [PATCH 214/258] Fix a bug in embind when using const return values --- system/include/emscripten/wire.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/system/include/emscripten/wire.h b/system/include/emscripten/wire.h index 3e4a77d97..0c0f5eb81 100755 --- a/system/include/emscripten/wire.h +++ b/system/include/emscripten/wire.h @@ -181,6 +181,17 @@ namespace emscripten { } }; + template + struct BindingType { + typedef typename BindingType::WireType WireType; + static WireType toWireType(const T& v) { + return BindingType::toWireType(v); + } + static T fromWireType(WireType wt) { + return BindingType::fromWireType(wt); + } + }; + template struct BindingType { typedef typename BindingType::WireType WireType; From bfcc6330b29040d837d4b7de123c106f3f661b15 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Wed, 20 Mar 2013 17:51:15 -0700 Subject: [PATCH 215/258] Simplify getTypeName --- src/embind/embind.js | 22 ++++++++++++-------- system/lib/embind/bind.cpp | 42 ++++++++++++++++++-------------------- 2 files changed, 34 insertions(+), 30 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index d3ab0bf55..99d7005b2 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -3,10 +3,10 @@ /*global FUNCTION_TABLE, HEAP32, HEAPU8*/ /*global Pointer_stringify*/ /*global __emval_register, _emval_handle_array, __emval_decref*/ -/*global ___typeName:false*/ var InternalError = Module.InternalError = extendError(Error, 'InternalError'); var BindingError = Module.BindingError = extendError(Error, 'BindingError'); +var UnboundTypeError = Module.UnboundTypeError = extendError(BindingError, 'UnboundTypeError'); function throwInternalError(value) { throw new InternalError(value); @@ -124,11 +124,8 @@ function whenDependentTypesAreResolved(dependentTypes, onComplete) { } } -function typeName(rawType) { - var bt = ___typeName(rawType); - var rv = Pointer_stringify(bt); - _free(bt); - return rv; +function getTypeName(type) { + return Module._embind_getTypeName(type); } function heap32VectorToArray(count, firstElement) { @@ -142,7 +139,7 @@ function heap32VectorToArray(count, firstElement) { function requireRegisteredType(rawType, humanName) { var impl = registeredTypes[rawType]; if (undefined === impl) { - throwBindingError(humanName + " has unknown type " + typeName(rawType)); + throwBindingError(humanName + " has unknown type " + getTypeName(rawType)); } return impl; } @@ -279,8 +276,17 @@ function __embind_register_function(name, argCount, rawArgTypesAddr, rawInvoker, var argTypes = heap32VectorToArray(argCount, rawArgTypesAddr); name = Pointer_stringify(name); rawInvoker = FUNCTION_TABLE[rawInvoker]; + + var invoker = function() { + throw new UnboundTypeError('Cannot call ' + name + ' due to unbound types: UnboundFoo'); + } + + exposePublicSymbol(name, function() { + return invoker.apply(this, arguments); + }); + whenDependentTypesAreResolved(argTypes, function(argTypes) { - exposePublicSymbol(name, makeInvoker(name, argCount, argTypes, rawInvoker, fn)); + invoker = makeInvoker(name, argCount, argTypes, rawInvoker, fn); }); } diff --git a/system/lib/embind/bind.cpp b/system/lib/embind/bind.cpp index 6994ef0cb..04a30dfcc 100755 --- a/system/lib/embind/bind.cpp +++ b/system/lib/embind/bind.cpp @@ -1,5 +1,4 @@ #include -#include <../lib/libcxxabi/src/private_typeinfo.h> #include <../lib/libcxxabi/include/cxxabi.h> #include #include @@ -9,29 +8,26 @@ using namespace emscripten; +static std::string _embind_getTypeName(intptr_t ti_raw) { + auto ti = reinterpret_cast(ti_raw); + int stat; + char* demangled = abi::__cxa_demangle(ti->name(), NULL, NULL, &stat); + switch (stat) { + case -1: + return ""; + case -2: + return ""; + case -3: + return ""; + } + + std::string rv(demangled); + free(demangled); + return rv; +} + namespace emscripten { namespace internal { - extern "C" { - const char* EMSCRIPTEN_KEEPALIVE __typeName(const std::type_info* ti) { - size_t nameLen = std::min(strlen(ti->name()), 1024U); - char* name = (char *)malloc(nameLen+1); - int stat; - - __cxxabiv1::__cxa_demangle(ti->name(), name, &nameLen, &stat); - - if (stat != 0) { - strncpy(name, ti->name(), nameLen); - name[nameLen] = '\0'; - } - return name; - } - - size_t EMSCRIPTEN_KEEPALIVE __peek32(size_t p) { - return *reinterpret_cast(p); - } - - } - JSInterface* create_js_interface(EM_VAL e) { return new JSInterface(e); } @@ -60,4 +56,6 @@ EMSCRIPTEN_BINDINGS(native_and_builtin_types) { _embind_register_cstring(TypeID::get(), "std::string"); _embind_register_emval(TypeID::get(), "emscripten::val"); + + function("_embind_getTypeName", &_embind_getTypeName); } From 4d6377d15e3ba48f4a6d5eb311bbd8afb618cf0d Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Wed, 20 Mar 2013 17:52:09 -0700 Subject: [PATCH 216/258] jshint --- src/embind/embind.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 99d7005b2..72312af17 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -279,7 +279,7 @@ function __embind_register_function(name, argCount, rawArgTypesAddr, rawInvoker, var invoker = function() { throw new UnboundTypeError('Cannot call ' + name + ' due to unbound types: UnboundFoo'); - } + }; exposePublicSymbol(name, function() { return invoker.apply(this, arguments); From 0a6f5476f19792164e122c27ffca83baa69c1ca3 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Wed, 20 Mar 2013 18:39:45 -0700 Subject: [PATCH 217/258] If calling function that uses unbound types, give a sensible error message. --- src/embind/embind.js | 218 +++++++++++++++++++++++-------------- system/lib/embind/bind.cpp | 12 +- 2 files changed, 143 insertions(+), 87 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 72312af17..c27c49b2d 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -8,12 +8,34 @@ var InternalError = Module.InternalError = extendError(Error, 'InternalError'); var BindingError = Module.BindingError = extendError(Error, 'BindingError'); var UnboundTypeError = Module.UnboundTypeError = extendError(BindingError, 'UnboundTypeError'); -function throwInternalError(value) { - throw new InternalError(value); +function throwInternalError(message) { + throw new InternalError(message); } -function throwBindingError(value) { - throw new BindingError(value); +function throwBindingError(message) { + throw new BindingError(message); +} + +function throwUnboundTypeError(message, types) { + var unboundTypes = []; + var seen = {}; + function visit(type) { + if (seen[type]) { + return; + } + if (registeredTypes[type]) { + return; + } + if (typeDependencies[type]) { + typeDependencies[type].forEach(visit); + return; + } + unboundTypes.push(type); + seen[type] = true; + } + types.forEach(visit); + + throw new UnboundTypeError(message + ': ' + unboundTypes.map(getTypeName).join([', '])); } function exposePublicSymbol(name, value) { @@ -75,6 +97,9 @@ var registeredTypes = {}; // typeID -> [callback] var awaitingDependencies = {}; +// typeID -> [dependentTypes] +var typeDependencies = {}; + // class typeID -> {pointerType: ..., constPointerType: ...} var registeredPointers = {}; @@ -88,6 +113,7 @@ function registerType(rawType, registeredInstance) { } registeredTypes[rawType] = registeredInstance; + delete typeDependencies[rawType]; if (awaitingDependencies.hasOwnProperty(rawType)) { var callbacks = awaitingDependencies[rawType]; @@ -98,7 +124,21 @@ function registerType(rawType, registeredInstance) { } } -function whenDependentTypesAreResolved(dependentTypes, onComplete) { +function whenDependentTypesAreResolved(myTypes, dependentTypes, getTypeConverters) { + myTypes.forEach(function(type) { + typeDependencies[type] = dependentTypes; + }); + + function onComplete(typeConverters) { + var myTypeConverters = getTypeConverters(typeConverters); + if (myTypeConverters.length !== myTypes.length) { + throwInternalError('Mismatched type converter count'); + } + for (var i = 0; i < myTypes.length; ++i) { + registerType(myTypes[i], myTypeConverters[i]); + } + } + var typeConverters = new Array(dependentTypes.length); var unregisteredTypes = []; var registered = 0; @@ -278,15 +318,16 @@ function __embind_register_function(name, argCount, rawArgTypesAddr, rawInvoker, rawInvoker = FUNCTION_TABLE[rawInvoker]; var invoker = function() { - throw new UnboundTypeError('Cannot call ' + name + ' due to unbound types: UnboundFoo'); + throwUnboundTypeError('Cannot call ' + name + ' due to unbound types', argTypes); }; exposePublicSymbol(name, function() { return invoker.apply(this, arguments); }); - whenDependentTypesAreResolved(argTypes, function(argTypes) { + whenDependentTypesAreResolved([], argTypes, function(argTypes) { invoker = makeInvoker(name, argCount, argTypes, rawInvoker, fn); + return []; }); } @@ -349,7 +390,7 @@ function __embind_register_tuple_element( tupleType.elements.push(undefined); // TODO: test incomplete registration of value tuples - whenDependentTypesAreResolved([rawType], function(type) { + whenDependentTypesAreResolved([], [rawType], function(type) { type = type[0]; tupleType.elements[index] = { read: function(ptr) { @@ -361,6 +402,7 @@ function __embind_register_tuple_element( runDestructors(destructors); } }; + return []; }); } @@ -380,7 +422,7 @@ function __embind_register_tuple_element_accessor( rawStaticSetter = FUNCTION_TABLE[rawStaticSetter]; setter = copyMemberPointer(setter, setterSize); - whenDependentTypesAreResolved([rawElementType], function(elementType) { + whenDependentTypesAreResolved([], [rawElementType], function(elementType) { elementType = elementType[0]; tupleType.elements.push({ read: function(ptr) { @@ -395,6 +437,7 @@ function __embind_register_tuple_element_accessor( runDestructors(destructors); } }); + return []; }); } @@ -454,7 +497,7 @@ function __embind_register_struct_field( rawSetter = FUNCTION_TABLE[rawSetter]; memberPointer = copyMemberPointer(memberPointer, memberPointerSize); // TODO: test incomplete registration of value structs - whenDependentTypesAreResolved([rawFieldType], function(fieldType) { + whenDependentTypesAreResolved([], [rawFieldType], function(fieldType) { fieldType = fieldType[0]; structType.fields[fieldName] = { read: function(ptr) { @@ -466,6 +509,7 @@ function __embind_register_struct_field( runDestructors(destructors); } }; + return []; }); } @@ -756,75 +800,79 @@ function __embind_register_class( downcast = FUNCTION_TABLE[downcast]; var legalFunctionName = makeLegalFunctionName(name); - whenDependentTypesAreResolved(baseClassRawType ? [baseClassRawType] : [], function(base) { - base = base[0]; + whenDependentTypesAreResolved( + [rawType, rawPointerType, rawConstPointerType], + baseClassRawType ? [baseClassRawType] : [], + function(base) { + base = base[0]; - var baseClass; - var basePrototype; - if (baseClassRawType) { - baseClass = base.registeredClass; - basePrototype = baseClass.instancePrototype; - } else { - basePrototype = ClassHandle.prototype; + var baseClass; + var basePrototype; + if (baseClassRawType) { + baseClass = base.registeredClass; + basePrototype = baseClass.instancePrototype; + } else { + basePrototype = ClassHandle.prototype; + } + + var constructor = createNamedFunction(legalFunctionName, function() { + if (Object.getPrototypeOf(this) !== instancePrototype) { + throw new BindingError("Use 'new' to construct " + name); + } + var body = registeredClass.constructor_body; + if (undefined === body) { + throw new BindingError(name + " has no accessible constructor"); + } + return body.apply(this, arguments); + }); + + var instancePrototype = Object.create(basePrototype, { + constructor: { value: constructor }, + }); + + constructor.prototype = instancePrototype; + + var registeredClass = new RegisteredClass( + name, + constructor, + instancePrototype, + rawDestructor, + baseClass, + getActualType, + upcast, + downcast); + + var referenceConverter = new RegisteredPointer( + name, + registeredClass, + true, + false, + false); + + var pointerConverter = new RegisteredPointer( + name + '*', + registeredClass, + false, + false, + false); + + var constPointerConverter = new RegisteredPointer( + name + ' const*', + registeredClass, + false, + true, + false); + + registeredPointers[rawType] = { + pointerType: pointerConverter, + constPointerType: constPointerConverter + }; + + exposePublicSymbol(legalFunctionName, constructor); + + return [referenceConverter, pointerConverter, constPointerConverter]; } - - var constructor = createNamedFunction(legalFunctionName, function() { - if (Object.getPrototypeOf(this) !== instancePrototype) { - throw new BindingError("Use 'new' to construct " + name); - } - var body = registeredClass.constructor_body; - if (undefined === body) { - throw new BindingError(name + " has no accessible constructor"); - } - return body.apply(this, arguments); - }); - - var instancePrototype = Object.create(basePrototype, { - constructor: { value: constructor }, - }); - - constructor.prototype = instancePrototype; - - var registeredClass = new RegisteredClass( - name, - constructor, - instancePrototype, - rawDestructor, - baseClass, - getActualType, - upcast, - downcast); - - registerType(rawType, new RegisteredPointer( - name, - registeredClass, - true, - false, - false)); - - var pointerType = new RegisteredPointer( - name + '*', - registeredClass, - false, - false, - false); - registerType(rawPointerType, pointerType); - - var constPointerType = new RegisteredPointer( - name + ' const*', - registeredClass, - false, - true, - false); - registerType(rawConstPointerType, constPointerType); - - registeredPointers[rawType] = { - pointerType: pointerType, - constPointerType: constPointerType - }; - - exposePublicSymbol(legalFunctionName, constructor); - }); + ); } function __embind_register_class_constructor( @@ -837,7 +885,7 @@ function __embind_register_class_constructor( var rawArgTypes = heap32VectorToArray(argCount, rawArgTypesAddr); invoker = FUNCTION_TABLE[invoker]; - whenDependentTypesAreResolved([rawClassType].concat(rawArgTypes), function(argTypes) { + whenDependentTypesAreResolved([], [rawClassType].concat(rawArgTypes), function(argTypes) { var classType = argTypes[0]; argTypes = argTypes.slice(1); var humanName = 'constructor ' + classType.name; @@ -857,6 +905,7 @@ function __embind_register_class_constructor( return argTypes[0].fromWireType(ptr); }; + return []; }); } @@ -912,7 +961,7 @@ function __embind_register_class_function( rawInvoker = FUNCTION_TABLE[rawInvoker]; memberFunction = copyMemberPointer(memberFunction, memberFunctionSize); - whenDependentTypesAreResolved([rawClassType].concat(rawArgTypes), function(argTypes) { + whenDependentTypesAreResolved([], [rawClassType].concat(rawArgTypes), function(argTypes) { var classType = argTypes[0]; argTypes = argTypes.slice(1); var humanName = classType.name + '.' + methodName; @@ -938,6 +987,7 @@ function __embind_register_class_function( runDestructors(destructors); return rv; }; + return []; }); } @@ -953,9 +1003,10 @@ function __embind_register_class_class_function( var rawArgTypes = heap32VectorToArray(argCount, rawArgTypesAddr); methodName = Pointer_stringify(methodName); rawInvoker = FUNCTION_TABLE[rawInvoker]; - whenDependentTypesAreResolved(rawArgTypes, function(argTypes) { + whenDependentTypesAreResolved([], rawArgTypes, function(argTypes) { var humanName = classType.name + '.' + methodName; classType.registeredClass.constructor[methodName] = makeInvoker(humanName, argCount, argTypes, rawInvoker, fn); + return []; }); } @@ -972,7 +1023,7 @@ function __embind_register_class_property( getter = FUNCTION_TABLE[getter]; setter = FUNCTION_TABLE[setter]; memberPointer = copyMemberPointer(memberPointer, memberPointerSize); - whenDependentTypesAreResolved([rawClassType, rawFieldType], function(converters) { + whenDependentTypesAreResolved([], [rawClassType, rawFieldType], function(converters) { var classType = converters[0]; var fieldType = converters[1]; var humanName = classType.name + '.' + fieldName; @@ -989,6 +1040,7 @@ function __embind_register_class_property( }, enumerable: true }); + return []; }); } @@ -1020,7 +1072,7 @@ function __embind_register_smart_ptr( rawShare = FUNCTION_TABLE[rawShare]; rawDestructor = FUNCTION_TABLE[rawDestructor]; - whenDependentTypesAreResolved([rawPointeeType], function(pointeeType) { + whenDependentTypesAreResolved([rawType], [rawPointeeType], function(pointeeType) { pointeeType = pointeeType[0]; var registeredPointer = new RegisteredPointer( @@ -1036,7 +1088,7 @@ function __embind_register_smart_ptr( rawConstructor, rawShare, rawDestructor); - registerType(rawType, registeredPointer); + return [registeredPointer]; }); } diff --git a/system/lib/embind/bind.cpp b/system/lib/embind/bind.cpp index 04a30dfcc..a75ba96a2 100755 --- a/system/lib/embind/bind.cpp +++ b/system/lib/embind/bind.cpp @@ -12,6 +12,12 @@ static std::string _embind_getTypeName(intptr_t ti_raw) { auto ti = reinterpret_cast(ti_raw); int stat; char* demangled = abi::__cxa_demangle(ti->name(), NULL, NULL, &stat); + if (stat == 0) { + std::string rv(demangled); + free(demangled); + return rv; + } + switch (stat) { case -1: return ""; @@ -19,11 +25,9 @@ static std::string _embind_getTypeName(intptr_t ti_raw) { return ""; case -3: return ""; + default: + return ""; } - - std::string rv(demangled); - free(demangled); - return rv; } namespace emscripten { From e10ede1e07ee85d9d99bd34eb4ec8a62ae972616 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Wed, 20 Mar 2013 20:15:36 -0700 Subject: [PATCH 218/258] Add sensible error messages when working with classes that depend on unbound types. --- src/embind/embind.js | 174 ++++++++++++++++++++++++------------- system/lib/embind/bind.cpp | 37 ++++---- 2 files changed, 129 insertions(+), 82 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index c27c49b2d..ddfc4d439 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -3,6 +3,7 @@ /*global FUNCTION_TABLE, HEAP32, HEAPU8*/ /*global Pointer_stringify*/ /*global __emval_register, _emval_handle_array, __emval_decref*/ +/*global ___getTypeName*/ var InternalError = Module.InternalError = extendError(Error, 'InternalError'); var BindingError = Module.BindingError = extendError(Error, 'BindingError'); @@ -45,6 +46,13 @@ function exposePublicSymbol(name, value) { Module[name] = value; } +function replacePublicSymbol(name, value) { + if (!Module.hasOwnProperty(name)) { + throwInternalError('Replacing nonexistant public symbol'); + } + Module[name] = value; +} + // from https://github.com/imvu/imvujs/blob/master/src/error.js function extendError(baseErrorType, errorName) { var errorClass = createNamedFunction(errorName, function(message) { @@ -165,7 +173,10 @@ function whenDependentTypesAreResolved(myTypes, dependentTypes, getTypeConverter } function getTypeName(type) { - return Module._embind_getTypeName(type); + var ptr = ___getTypeName(type); + var rv = Pointer_stringify(ptr); + _free(ptr); + return rv; } function heap32VectorToArray(count, firstElement) { @@ -317,16 +328,12 @@ function __embind_register_function(name, argCount, rawArgTypesAddr, rawInvoker, name = Pointer_stringify(name); rawInvoker = FUNCTION_TABLE[rawInvoker]; - var invoker = function() { - throwUnboundTypeError('Cannot call ' + name + ' due to unbound types', argTypes); - }; - exposePublicSymbol(name, function() { - return invoker.apply(this, arguments); + throwUnboundTypeError('Cannot call ' + name + ' due to unbound types', argTypes); }); whenDependentTypesAreResolved([], argTypes, function(argTypes) { - invoker = makeInvoker(name, argCount, argTypes, rawInvoker, fn); + replacePublicSymbol(name, makeInvoker(name, argCount, argTypes, rawInvoker, fn)); return []; }); } @@ -800,6 +807,11 @@ function __embind_register_class( downcast = FUNCTION_TABLE[downcast]; var legalFunctionName = makeLegalFunctionName(name); + exposePublicSymbol(legalFunctionName, function() { + // this code cannot run if baseClassRawType is zero + throwUnboundTypeError('Cannot construct ' + name + ' due to unbound types', [baseClassRawType]); + }); + whenDependentTypesAreResolved( [rawType, rawPointerType, rawConstPointerType], baseClassRawType ? [baseClassRawType] : [], @@ -868,7 +880,7 @@ function __embind_register_class( constPointerType: constPointerConverter }; - exposePublicSymbol(legalFunctionName, constructor); + replacePublicSymbol(legalFunctionName, constructor); return [referenceConverter, pointerConverter, constPointerConverter]; } @@ -885,26 +897,33 @@ function __embind_register_class_constructor( var rawArgTypes = heap32VectorToArray(argCount, rawArgTypesAddr); invoker = FUNCTION_TABLE[invoker]; - whenDependentTypesAreResolved([], [rawClassType].concat(rawArgTypes), function(argTypes) { - var classType = argTypes[0]; - argTypes = argTypes.slice(1); + whenDependentTypesAreResolved([], [rawClassType], function(classType) { + classType = classType[0]; var humanName = 'constructor ' + classType.name; + classType.registeredClass.constructor_body = function() { - if (arguments.length !== argCount - 1) { - throwBindingError(humanName + ' called with ' + arguments.length + ' arguments, expected ' + (argCount-1)); - } - var destructors = []; - var args = new Array(argCount); - args[0] = rawConstructor; - for (var i = 1; i < argCount; ++i) { - args[i] = argTypes[i].toWireType(destructors, arguments[i - 1]); - } - - var ptr = invoker.apply(null, args); - runDestructors(destructors); - - return argTypes[0].fromWireType(ptr); + throwUnboundTypeError('Cannot construct ' + classType.name + ' due to unbound types', rawArgTypes); }; + + whenDependentTypesAreResolved([], rawArgTypes, function(argTypes) { + classType.registeredClass.constructor_body = function() { + if (arguments.length !== argCount - 1) { + throwBindingError(humanName + ' called with ' + arguments.length + ' arguments, expected ' + (argCount-1)); + } + var destructors = []; + var args = new Array(argCount); + args[0] = rawConstructor; + for (var i = 1; i < argCount; ++i) { + args[i] = argTypes[i].toWireType(destructors, arguments[i - 1]); + } + + var ptr = invoker.apply(null, args); + runDestructors(destructors); + + return argTypes[0].fromWireType(ptr); + }; + return []; + }); return []; }); } @@ -961,32 +980,39 @@ function __embind_register_class_function( rawInvoker = FUNCTION_TABLE[rawInvoker]; memberFunction = copyMemberPointer(memberFunction, memberFunctionSize); - whenDependentTypesAreResolved([], [rawClassType].concat(rawArgTypes), function(argTypes) { - var classType = argTypes[0]; - argTypes = argTypes.slice(1); + whenDependentTypesAreResolved([], [rawClassType], function(classType) { + classType = classType[0]; var humanName = classType.name + '.' + methodName; + classType.registeredClass.instancePrototype[methodName] = function() { - if (arguments.length !== argCount - 1) { - throwBindingError('emscripten binding method ' + humanName + ' called with ' + arguments.length + ' arguments, expected ' + (argCount-1)); - } - - var ptr = validateThis(this, classType, humanName); - if (!isConst && this.$$.ptrType.isConst) { - throwBindingError('Cannot call non-const method ' + humanName + ' on const reference'); - } - - var destructors = []; - var args = new Array(argCount + 1); - args[0] = ptr; - args[1] = memberFunction; - for (var i = 1; i < argCount; ++i) { - args[i + 1] = argTypes[i].toWireType(destructors, arguments[i - 1]); - } - var rv = rawInvoker.apply(null, args); - rv = argTypes[0].fromWireType(rv); - runDestructors(destructors); - return rv; + throwUnboundTypeError('Cannot call ' + humanName + ' due to unbound types', rawArgTypes); }; + + whenDependentTypesAreResolved([], rawArgTypes, function(argTypes) { + classType.registeredClass.instancePrototype[methodName] = function() { + if (arguments.length !== argCount - 1) { + throwBindingError('emscripten binding method ' + humanName + ' called with ' + arguments.length + ' arguments, expected ' + (argCount-1)); + } + + var ptr = validateThis(this, classType, humanName); + if (!isConst && this.$$.ptrType.isConst) { + throwBindingError('Cannot call non-const method ' + humanName + ' on const reference'); + } + + var destructors = []; + var args = new Array(argCount + 1); + args[0] = ptr; + args[1] = memberFunction; + for (var i = 1; i < argCount; ++i) { + args[i + 1] = argTypes[i].toWireType(destructors, arguments[i - 1]); + } + var rv = rawInvoker.apply(null, args); + rv = argTypes[0].fromWireType(rv); + runDestructors(destructors); + return rv; + }; + return []; + }); return []; }); } @@ -999,13 +1025,21 @@ function __embind_register_class_class_function( rawInvoker, fn ) { - var classType = requireRegisteredType(rawClassType, 'class'); var rawArgTypes = heap32VectorToArray(argCount, rawArgTypesAddr); methodName = Pointer_stringify(methodName); rawInvoker = FUNCTION_TABLE[rawInvoker]; - whenDependentTypesAreResolved([], rawArgTypes, function(argTypes) { + whenDependentTypesAreResolved([], [rawClassType], function(classType) { + classType = classType[0]; var humanName = classType.name + '.' + methodName; - classType.registeredClass.constructor[methodName] = makeInvoker(humanName, argCount, argTypes, rawInvoker, fn); + + classType.registeredClass.constructor[methodName] = function() { + throwUnboundTypeError('Cannot call ' + humanName + ' due to unbound types', rawArgTypes); + }; + + whenDependentTypesAreResolved([], rawArgTypes, function(argTypes) { + classType.registeredClass.constructor[methodName] = makeInvoker(humanName, argCount, argTypes, rawInvoker, fn); + return []; + }); return []; }); } @@ -1023,23 +1057,39 @@ function __embind_register_class_property( getter = FUNCTION_TABLE[getter]; setter = FUNCTION_TABLE[setter]; memberPointer = copyMemberPointer(memberPointer, memberPointerSize); - whenDependentTypesAreResolved([], [rawClassType, rawFieldType], function(converters) { - var classType = converters[0]; - var fieldType = converters[1]; + whenDependentTypesAreResolved([], [rawClassType], function(classType) { + classType = classType[0]; var humanName = classType.name + '.' + fieldName; + Object.defineProperty(classType.registeredClass.instancePrototype, fieldName, { get: function() { - var ptr = validateThis(this, classType, humanName + ' getter'); - return fieldType.fromWireType(getter(ptr, memberPointer)); + throwUnboundTypeError('Cannot access ' + humanName + ' due to unbound types', [rawFieldType]); }, - set: function(v) { - var ptr = validateThis(this, classType, humanName + ' setter'); - var destructors = []; - setter(ptr, memberPointer, fieldType.toWireType(destructors, v)); - runDestructors(destructors); + set: function() { + throwUnboundTypeError('Cannot access ' + humanName + ' due to unbound types', [rawFieldType]); }, - enumerable: true + enumerable: true, + configurable: true }); + + whenDependentTypesAreResolved([], [rawFieldType], function(fieldType) { + fieldType = fieldType[0]; + Object.defineProperty(classType.registeredClass.instancePrototype, fieldName, { + get: function() { + var ptr = validateThis(this, classType, humanName + ' getter'); + return fieldType.fromWireType(getter(ptr, memberPointer)); + }, + set: function(v) { + var ptr = validateThis(this, classType, humanName + ' setter'); + var destructors = []; + setter(ptr, memberPointer, fieldType.toWireType(destructors, v)); + runDestructors(destructors); + }, + enumerable: true + }); + return []; + }); + return []; }); } diff --git a/system/lib/embind/bind.cpp b/system/lib/embind/bind.cpp index a75ba96a2..a5c878f5f 100755 --- a/system/lib/embind/bind.cpp +++ b/system/lib/embind/bind.cpp @@ -8,25 +8,24 @@ using namespace emscripten; -static std::string _embind_getTypeName(intptr_t ti_raw) { - auto ti = reinterpret_cast(ti_raw); - int stat; - char* demangled = abi::__cxa_demangle(ti->name(), NULL, NULL, &stat); - if (stat == 0) { - std::string rv(demangled); - free(demangled); - return rv; - } +extern "C" { + const char* EMSCRIPTEN_KEEPALIVE __getTypeName(const std::type_info* ti) { + int stat; + char* demangled = abi::__cxa_demangle(ti->name(), NULL, NULL, &stat); + if (stat == 0 && demangled) { + return demangled; + } - switch (stat) { - case -1: - return ""; - case -2: - return ""; - case -3: - return ""; - default: - return ""; + switch (stat) { + case -1: + return strdup(""); + case -2: + return strdup(""); + case -3: + return strdup(""); + default: + return strdup(""); + } } } @@ -60,6 +59,4 @@ EMSCRIPTEN_BINDINGS(native_and_builtin_types) { _embind_register_cstring(TypeID::get(), "std::string"); _embind_register_emval(TypeID::get(), "emscripten::val"); - - function("_embind_getTypeName", &_embind_getTypeName); } From 0b8597a8dbf36b26dfb76df8d11fa7377bd810f1 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Thu, 21 Mar 2013 01:23:25 -0700 Subject: [PATCH 219/258] Some tweaks to facilitate making value_struct and value_tuple simpler. --- system/include/emscripten/bind.h | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index b7b17f20b..ad7e7b35f 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -370,14 +370,14 @@ namespace emscripten { typedef internal::BindingType MemberBinding; typedef typename MemberBinding::WireType WireType; - static WireType get( - ClassType& ptr, + static WireType getWire( + const ClassType& ptr, const MemberPointer& field ) { return MemberBinding::toWireType(ptr.*field); } - static void set( + static void setWire( ClassType& ptr, const MemberPointer& field, WireType value @@ -387,7 +387,7 @@ namespace emscripten { template static WireType propertyGet( - ClassType& ptr, + const ClassType& ptr, const Getter& getter ) { return MemberBinding::toWireType(getter(ptr)); @@ -425,8 +425,8 @@ namespace emscripten { internal::_embind_register_tuple_element( internal::TypeID::get(), internal::TypeID::get(), - reinterpret_cast(&internal::MemberAccess::get), - reinterpret_cast(&internal::MemberAccess::set), + reinterpret_cast(&internal::MemberAccess::getWire), + reinterpret_cast(&internal::MemberAccess::setWire), sizeof(field), &field); @@ -511,8 +511,8 @@ namespace emscripten { internal::TypeID::get(), fieldName, internal::TypeID::get(), - reinterpret_cast(&internal::MemberAccess::get), - reinterpret_cast(&internal::MemberAccess::set), + reinterpret_cast(&internal::MemberAccess::getWire), + reinterpret_cast(&internal::MemberAccess::setWire), sizeof(field), &field); @@ -865,8 +865,8 @@ namespace emscripten { TypeID::get(), fieldName, TypeID::get(), - reinterpret_cast(&MemberAccess::get), - reinterpret_cast(&MemberAccess::set), + reinterpret_cast(&MemberAccess::getWire), + reinterpret_cast(&MemberAccess::setWire), sizeof(field), &field); return *this; From b7936b0a61a37b451b2e4f01cb02986d26640d35 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Sat, 23 Mar 2013 02:07:45 -0700 Subject: [PATCH 220/258] Unify 'this' handling in class functions. 'this' is just the first argument to the method. This removes several special cases and will remove even more soon. --- src/embind/embind.js | 23 +++--- system/include/emscripten/bind.h | 119 +++++++++++-------------------- 2 files changed, 50 insertions(+), 92 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index ddfc4d439..d5e8659de 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -958,7 +958,8 @@ function validateThis(this_, classType, humanName) { if (!this_.$$.ptr) { throwBindingError('cannot call emscripten binding method ' + humanName + ' on deleted object'); } - + + // todo: kill this return upcastPointer( this_.$$.ptr, this_.$$.ptrType.registeredClass, @@ -969,8 +970,7 @@ function __embind_register_class_function( rawClassType, methodName, argCount, - rawArgTypesAddr, - isConst, + rawArgTypesAddr, // [ReturnType, ThisType, Args...] rawInvoker, memberFunctionSize, memberFunction @@ -990,21 +990,18 @@ function __embind_register_class_function( whenDependentTypesAreResolved([], rawArgTypes, function(argTypes) { classType.registeredClass.instancePrototype[methodName] = function() { - if (arguments.length !== argCount - 1) { - throwBindingError('emscripten binding method ' + humanName + ' called with ' + arguments.length + ' arguments, expected ' + (argCount-1)); + if (arguments.length !== argCount - 2) { + throwBindingError(humanName + ' called with ' + arguments.length + ' arguments, expected ' + (argCount-1)); } - var ptr = validateThis(this, classType, humanName); - if (!isConst && this.$$.ptrType.isConst) { - throwBindingError('Cannot call non-const method ' + humanName + ' on const reference'); - } + validateThis(this, classType, humanName); var destructors = []; var args = new Array(argCount + 1); - args[0] = ptr; - args[1] = memberFunction; - for (var i = 1; i < argCount; ++i) { - args[i + 1] = argTypes[i].toWireType(destructors, arguments[i - 1]); + args[0] = memberFunction; + args[1] = argTypes[1].toWireType(destructors, this); + for (var i = 2; i < argCount; ++i) { + args[i] = argTypes[i].toWireType(destructors, arguments[i - 2]); } var rv = rawInvoker.apply(null, args); rv = argTypes[0].fromWireType(rv); diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index ad7e7b35f..76ae7df62 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -132,7 +132,6 @@ namespace emscripten { const char* methodName, unsigned argCount, TYPEID argTypes[], - bool isConst, GenericFunction invoker, size_t memberFunctionSize, void* memberFunction); @@ -186,6 +185,7 @@ namespace emscripten { static constexpr int index = 0; }; + /* template struct allow_raw_pointer { template @@ -197,6 +197,7 @@ namespace emscripten { >::type type; }; }; + */ // whitelist all raw pointers struct allow_raw_pointers { @@ -210,6 +211,11 @@ namespace emscripten { }; }; + // this is temporary until arg policies are reworked + template + struct allow_raw_pointer : public allow_raw_pointers { + }; + namespace internal { template struct Invoker { @@ -278,87 +284,62 @@ namespace emscripten { delete ptr; } - template + template struct FunctionInvoker { - typedef ReturnType (*FunctionPointer)(ClassType& ct, Args...); static typename internal::BindingType::WireType invoke( - ClassType* ptr, - FunctionPointer* function, + FunctionPointerType* function, + typename internal::BindingType::WireType wireThis, typename internal::BindingType::WireType... args ) { return internal::BindingType::toWireType( - (*function)(*ptr, internal::BindingType::fromWireType(args)...) + (*function)( + internal::BindingType::fromWireType(wireThis), + internal::BindingType::fromWireType(args)...) ); } }; - template - struct FunctionInvoker { - typedef void (*FunctionPointer)(ClassType& ct, Args...); + template + struct FunctionInvoker { static void invoke( - ClassType* ptr, - FunctionPointer* function, + FunctionPointerType* function, + typename internal::BindingType::WireType wireThis, typename internal::BindingType::WireType... args ) { - (*function)(*ptr, internal::BindingType::fromWireType(args)...); + (*function)( + internal::BindingType::fromWireType(wireThis), + internal::BindingType::fromWireType(args)...); } }; - template + template struct MethodInvoker { - typedef ReturnType (ClassType::*MemberPointer)(Args...); static typename internal::BindingType::WireType invoke( - ClassType* ptr, const MemberPointer& method, + typename internal::BindingType::WireType wireThis, typename internal::BindingType::WireType... args ) { return internal::BindingType::toWireType( - (ptr->*method)( + (internal::BindingType::fromWireType(wireThis)->*method)( internal::BindingType::fromWireType(args)... ) ); } }; - template - struct MethodInvoker { - typedef void (ClassType::*MemberPointer)(Args...); + template + struct MethodInvoker { static void invoke( - ClassType* ptr, const MemberPointer& method, + typename internal::BindingType::WireType wireThis, typename internal::BindingType::WireType... args ) { - return (ptr->*method)( - internal::BindingType::fromWireType(args)... - ); - } - }; - - template - struct ConstMethodInvoker { - typedef ReturnType (ClassType::*MemberPointer)(Args...) const; - static typename internal::BindingType::WireType invoke( - const ClassType* ptr, - const MemberPointer& method, - typename internal::BindingType::WireType... args - ) { - return internal::BindingType::toWireType( - (ptr->*method)( - internal::BindingType::fromWireType(args)... - ) - ); - } - }; - - template - struct ConstMethodInvoker { - typedef void (ClassType::*MemberPointer)(Args...) const; - static void invoke( - const ClassType* ptr, - const MemberPointer& method, - typename internal::BindingType::WireType... args - ) { - return (ptr->*method)( + return (internal::BindingType::fromWireType(wireThis)->*method)( internal::BindingType::fromWireType(args)... ); } @@ -793,14 +774,13 @@ namespace emscripten { class_& function(const char* methodName, ReturnType (ClassType::*memberFunction)(Args...), Policies...) { using namespace internal; - typename WithPolicies::template ArgTypeList args; + typename WithPolicies::template ArgTypeList, Args...> args; _embind_register_class_function( TypeID::get(), methodName, args.count, args.types, - false, - reinterpret_cast(&MethodInvoker::invoke), + reinterpret_cast(&MethodInvoker::invoke), sizeof(memberFunction), &memberFunction); return *this; @@ -810,48 +790,29 @@ namespace emscripten { class_& function(const char* methodName, ReturnType (ClassType::*memberFunction)(Args...) const, Policies...) { using namespace internal; - typename WithPolicies::template ArgTypeList args; + typename WithPolicies::template ArgTypeList, Args...> args; _embind_register_class_function( TypeID::get(), methodName, args.count, args.types, - true, - reinterpret_cast(&ConstMethodInvoker::invoke), + reinterpret_cast(&MethodInvoker::invoke), sizeof(memberFunction), &memberFunction); return *this; } - template - class_& function(const char* methodName, ReturnType (*function)(ClassType& ptr, Args...), Policies...) { + template + class_& function(const char* methodName, ReturnType (*function)(ThisType, Args...), Policies...) { using namespace internal; - typename WithPolicies::template ArgTypeList args; + typename WithPolicies::template ArgTypeList args; _embind_register_class_function( TypeID::get(), methodName, args.count, args.types, - false, - reinterpret_cast(&FunctionInvoker::invoke), - sizeof(function), - &function); - return *this; - } - - template - class_& function(const char* methodName, ReturnType (*function)(const ClassType& ptr, Args...), Policies...) { - using namespace internal; - - typename WithPolicies::template ArgTypeList args; - _embind_register_class_function( - TypeID::get(), - methodName, - args.count, - args.types, - true, - reinterpret_cast(&FunctionInvoker::invoke), + reinterpret_cast(&FunctionInvoker::invoke), sizeof(function), &function); return *this; From d0d2471703ec6bd62a9d5557984a74a358c032f6 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Sat, 23 Mar 2013 23:52:27 -0700 Subject: [PATCH 221/258] Fix a bug in external class function definitions and allow specifying functions that take smart pointers for 'this' --- system/include/emscripten/bind.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index 76ae7df62..332c533b8 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -812,7 +812,7 @@ namespace emscripten { methodName, args.count, args.types, - reinterpret_cast(&FunctionInvoker::invoke), + reinterpret_cast(&FunctionInvoker::invoke), sizeof(function), &function); return *this; From 7973873abff495fab9f9193e057a6c6c52871d05 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Mon, 25 Mar 2013 16:39:21 -0700 Subject: [PATCH 222/258] Improve the names of functions registered by embind so they show up in the chrome profiler. --- src/embind/embind.js | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index d5e8659de..c7b1633a0 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -85,6 +85,7 @@ function createNamedFunction(name, body) { return new Function( "body", "return function " + name + "() {\n" + + " \"use strict\";" + " return body.apply(this, arguments);\n" + "};\n" )(body); @@ -306,7 +307,7 @@ function makeInvoker(name, argCount, argTypes, invoker, fn) { if (!FUNCTION_TABLE[fn]) { throwBindingError('function '+name+' is not defined'); } - return function() { + return createNamedFunction(makeLegalFunctionName(name), function() { if (arguments.length !== argCount - 1) { throwBindingError('function ' + name + ' called with ' + arguments.length + ' arguments, expected ' + (argCount - 1)); } @@ -320,7 +321,7 @@ function makeInvoker(name, argCount, argTypes, invoker, fn) { rv = argTypes[0].fromWireType(rv); runDestructors(destructors); return rv; - }; + }); } function __embind_register_function(name, argCount, rawArgTypesAddr, rawInvoker, fn) { @@ -989,7 +990,7 @@ function __embind_register_class_function( }; whenDependentTypesAreResolved([], rawArgTypes, function(argTypes) { - classType.registeredClass.instancePrototype[methodName] = function() { + classType.registeredClass.instancePrototype[methodName] = createNamedFunction(makeLegalFunctionName(humanName), function() { if (arguments.length !== argCount - 2) { throwBindingError(humanName + ' called with ' + arguments.length + ' arguments, expected ' + (argCount-1)); } @@ -1007,7 +1008,7 @@ function __embind_register_class_function( rv = argTypes[0].fromWireType(rv); runDestructors(destructors); return rv; - }; + }); return []; }); return []; @@ -1094,12 +1095,12 @@ function __embind_register_class_property( var char_0 = '0'.charCodeAt(0); var char_9 = '9'.charCodeAt(0); function makeLegalFunctionName(name) { - var rv = name.replace(/[^a-zA-Z0-9_]/g, '$'); - var f = rv.charCodeAt(0); + name = name.replace(/[^a-zA-Z0-9_]/g, '$'); + var f = name.charCodeAt(0); if (f >= char_0 && f <= char_9) { - return '_' + rv; + return '_' + name; } else { - return rv; + return name; } } From dbd1a3bfe253ac1df0200427de10f60dc520cfb1 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Tue, 26 Mar 2013 12:39:57 -0700 Subject: [PATCH 223/258] Allow multiple arguments to emscripten::val::new_() --- src/embind/emval.js | 18 ++++++++++++++++-- system/include/emscripten/val.h | 29 +++++++++++++++++++++++++---- 2 files changed, 41 insertions(+), 6 deletions(-) diff --git a/src/embind/emval.js b/src/embind/emval.js index c60c62e06..eb31777b5 100755 --- a/src/embind/emval.js +++ b/src/embind/emval.js @@ -73,8 +73,22 @@ function __emval_take_value(type, v) { return __emval_register(v); } -function __emval_new(handle) { - return __emval_register(new (_emval_handle_array[handle].value)); +function __emval_new(handle, argCount, argTypes) { + var args = parseParameters( + argCount, + argTypes, + Array.prototype.slice.call(arguments, 3)); + + // implement what amounts to operator new + var constructor = _emval_handle_array[handle].value; + function dummy(){} + dummy.prototype = constructor.prototype; + var obj = new constructor; + var rv = constructor.apply(obj, args); + if (typeof rv === 'object') { + obj = rv; + } + return __emval_register(obj); } // appease jshint (technically this code uses eval) diff --git a/system/include/emscripten/val.h b/system/include/emscripten/val.h index 3952e008d..48f8ef694 100644 --- a/system/include/emscripten/val.h +++ b/system/include/emscripten/val.h @@ -20,7 +20,11 @@ namespace emscripten { EM_VAL _emval_new_cstring(const char*); void _emval_take_value(TYPEID type/*, ...*/); - EM_VAL _emval_new(EM_VAL value); + EM_VAL _emval_new( + EM_VAL value, + unsigned argCount, + internal::TYPEID argTypes[] + /*, ... */); EM_VAL _emval_get_global(const char* name); EM_VAL _emval_get_property(EM_VAL object, EM_VAL key); @@ -122,8 +126,25 @@ namespace emscripten { return val::global("Object")["prototype"]["hasOwnProperty"].call("call", *this, val(key)).as(); } - val new_() const { - return val(internal::_emval_new(handle)); + template + val new_(Args... args) const { + using namespace internal; + + WithPolicies<>::ArgTypeList argList; + // todo: this is awfully similar to operator(), can we + // merge them somehow? + typedef EM_VAL (*TypedNew)( + EM_VAL, + unsigned, + TYPEID argTypes[], + typename BindingType::WireType...); + TypedNew typedNew = reinterpret_cast(&_emval_new); + return val( + typedNew( + handle, + argList.count, + argList.types, + toWireType(args)...)); } template @@ -136,7 +157,7 @@ namespace emscripten { internal::_emval_set_property(handle, val(key).handle, v.handle); } - template + template val operator()(Args... args) { using namespace internal; From 56783851c34932a3e6196c38865905d966666ba0 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Tue, 26 Mar 2013 13:30:01 -0700 Subject: [PATCH 224/258] can look up module properties like HEAP8 from within C++ :) --- src/embind/emval.js | 5 +++++ system/include/emscripten/val.h | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/src/embind/emval.js b/src/embind/emval.js index eb31777b5..7153ae6d9 100755 --- a/src/embind/emval.js +++ b/src/embind/emval.js @@ -99,6 +99,11 @@ function __emval_get_global(name) { return __emval_register(global[name]); } +function __emval_get_module_property(name) { + name = Pointer_stringify(name); + return __emval_register(Module[name]); +} + function __emval_get_property(handle, key) { return __emval_register(_emval_handle_array[handle].value[_emval_handle_array[key].value]); } diff --git a/system/include/emscripten/val.h b/system/include/emscripten/val.h index 48f8ef694..91f775a70 100644 --- a/system/include/emscripten/val.h +++ b/system/include/emscripten/val.h @@ -27,6 +27,7 @@ namespace emscripten { /*, ... */); EM_VAL _emval_get_global(const char* name); + EM_VAL _emval_get_module_property(const char* name); EM_VAL _emval_get_property(EM_VAL object, EM_VAL key); void _emval_set_property(EM_VAL object, EM_VAL key, EM_VAL value); void _emval_as(EM_VAL value, TYPEID returnType); @@ -92,6 +93,10 @@ namespace emscripten { return val(internal::_emval_get_global(name)); } + static val module_property(const char* name) { + return val(internal::_emval_get_module_property(name)); + } + template explicit val(const T& value) { typedef internal::BindingType BT; From 638478a9a9234e7adb14259735432babd31bddba Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Tue, 26 Mar 2013 14:27:57 -0700 Subject: [PATCH 225/258] Typed array objects require use of operator new. You can't just call them directly. --- src/embind/emval.js | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/embind/emval.js b/src/embind/emval.js index 7153ae6d9..7cdac6d93 100755 --- a/src/embind/emval.js +++ b/src/embind/emval.js @@ -73,14 +73,35 @@ function __emval_take_value(type, v) { return __emval_register(v); } +var __newers = {}; // arity -> function + function __emval_new(handle, argCount, argTypes) { var args = parseParameters( argCount, argTypes, Array.prototype.slice.call(arguments, 3)); - // implement what amounts to operator new + // Alas, we are forced to use operator new until WebKit enables + // constructing typed arrays without new. + // In WebKit, Uint8Array(10) throws an error. + // In every other browser, it's identical to new Uint8Array(10). + + var newer = __newers[argCount]; + if (!newer) { + var parameters = new Array(argCount); + for (var i = 0; i < argCount; ++i) { + parameters[i] = 'a' + i; + } + /*jshint evil:true*/ + newer = __newers[argCount] = new Function( + ['c'].concat(parameters), + "return new c(" + parameters.join(',') + ");"); + } + var constructor = _emval_handle_array[handle].value; + var obj = newer.apply(undefined, [constructor].concat(args)); +/* + // implement what amounts to operator new function dummy(){} dummy.prototype = constructor.prototype; var obj = new constructor; @@ -88,6 +109,7 @@ function __emval_new(handle, argCount, argTypes) { if (typeof rv === 'object') { obj = rv; } +*/ return __emval_register(obj); } From 08881f98fdf4688afab03f4b0632f4d1385de969 Mon Sep 17 00:00:00 2001 From: jinsuck Date: Wed, 27 Mar 2013 14:35:59 -0700 Subject: [PATCH 226/258] add a helper method for Northstar unit testing (when emval leaks, we want to see what is left there) --- src/embind/emval.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/embind/emval.js b/src/embind/emval.js index 7cdac6d93..45030b99c 100755 --- a/src/embind/emval.js +++ b/src/embind/emval.js @@ -19,6 +19,16 @@ Module.count_emval_handles = function() { return count; }; +/** @expose */ +Module.get_first_emval = function() { + for (var i = 0; i < _emval_handle_array.length; ++i) { + if (_emval_handle_array[i] !== undefined) { + return _emval_handle_array[i]; + } + } + return null; +}; + // Private C++ API function __emval_register(value) { From 113721e6c6ca4cbde2bb39c4460269424ef3d81d Mon Sep 17 00:00:00 2001 From: Jukka Jylanki Date: Tue, 26 Mar 2013 12:18:13 +0200 Subject: [PATCH 227/258] Raise a TypeError if user tries to push an integer from JS side to C/C++ function exported with embind and the integer (char/short/int/long) is out of bounds of the data type expected by the C++ function. --- src/embind/embind.js | 9 ++++++++- system/include/emscripten/bind.h | 4 +++- system/lib/embind/bind.cpp | 19 ++++++++++--------- 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index c7b1633a0..ccce9f191 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -221,10 +221,14 @@ function __embind_register_bool(rawType, name, trueValue, falseValue) { }); } -function __embind_register_integer(rawType, name) { +// When converting a number from JS to C++ side, the valid range of the number is +// [minRange, maxRange], inclusive. +function __embind_register_integer(primitiveType, name, minRange, maxRange) { name = Pointer_stringify(name); registerType(rawType, { name: name, + minRange: minRange, + maxRange: maxRange, fromWireType: function(value) { return value; }, @@ -232,6 +236,9 @@ function __embind_register_integer(rawType, name) { if (typeof value !== "number") { throw new TypeError('Cannot convert "' + _embind_repr(value) + '" to ' + this.name); } + if (value < minRange || value > maxRange) { + throw new TypeError('Passing a number "' + _embind_repr(value) + '" from JS side to C/C++ side to an argument of type "' + name + '", which is outside the valid range [' + minRange + ', ' + maxRange + ']!'); + } return value | 0; }, }); diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index 332c533b8..b30cf54b2 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -39,7 +39,9 @@ namespace emscripten { void _embind_register_integer( TYPEID integerType, - const char* name); + const char* name, + int minRange, + int maxRange); void _embind_register_float( TYPEID floatType, diff --git a/system/lib/embind/bind.cpp b/system/lib/embind/bind.cpp index a5c878f5f..417d9ffd6 100755 --- a/system/lib/embind/bind.cpp +++ b/system/lib/embind/bind.cpp @@ -5,6 +5,7 @@ #include #include #include +#include using namespace emscripten; @@ -44,15 +45,15 @@ EMSCRIPTEN_BINDINGS(native_and_builtin_types) { _embind_register_bool(TypeID::get(), "bool", true, false); - _embind_register_integer(TypeID::get(), "char"); - _embind_register_integer(TypeID::get(), "signed char"); - _embind_register_integer(TypeID::get(), "unsigned char"); - _embind_register_integer(TypeID::get(), "short"); - _embind_register_integer(TypeID::get(), "unsigned short"); - _embind_register_integer(TypeID::get(), "int"); - _embind_register_integer(TypeID::get(), "unsigned int"); - _embind_register_integer(TypeID::get(), "long"); - _embind_register_integer(TypeID::get(), "unsigned long"); + _embind_register_integer(TypeID::get(), "char", CHAR_MIN, CHAR_MAX); + _embind_register_integer(TypeID::get(), "signed char", SCHAR_MIN, SCHAR_MAX); + _embind_register_integer(TypeID::get(), "unsigned char", 0, UCHAR_MAX); + _embind_register_integer(TypeID::get(), "short", SHRT_MIN, SHRT_MAX); + _embind_register_integer(TypeID::get(), "unsigned short", 0, USHRT_MAX); + _embind_register_integer(TypeID::get(), "int", INT_MIN, INT_MAX); + _embind_register_integer(TypeID::get(), "unsigned int", 0, UINT_MAX); + _embind_register_integer(TypeID::get(), "long", LONG_MIN, LONG_MAX); + _embind_register_integer(TypeID::get(), "unsigned long", 0, ULONG_MAX); _embind_register_float(TypeID::get(), "float"); _embind_register_float(TypeID::get(), "double"); From 0c11a28ccfa3716962c7424fe227c82edc04de16 Mon Sep 17 00:00:00 2001 From: Jukka Jylanki Date: Wed, 27 Mar 2013 08:58:35 +0200 Subject: [PATCH 228/258] Fix passing of UINT_MAX and ULONG_MAX from C++ to JS function. LLVM doesn't have u32 type, so UINT_MAX literal comes out as 'i32 -1' literal to JS, which whould treat it as a negative -1.0 double. --- src/embind/embind.js | 5 ++++- system/include/emscripten/bind.h | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index ccce9f191..1a2cec449 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -225,7 +225,10 @@ function __embind_register_bool(rawType, name, trueValue, falseValue) { // [minRange, maxRange], inclusive. function __embind_register_integer(primitiveType, name, minRange, maxRange) { name = Pointer_stringify(name); - registerType(rawType, { + if (maxRange == -1) { // LLVM doesn't have signed and unsigned 32-bit types, so u32 literals come out as 'i32 -1'. Always treat those as max u32. + maxRange = 4294967295; + } + registerType(primitiveType, { name: name, minRange: minRange, maxRange: maxRange, diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index b30cf54b2..211c67c00 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -40,8 +40,8 @@ namespace emscripten { void _embind_register_integer( TYPEID integerType, const char* name, - int minRange, - int maxRange); + long minRange, + unsigned long maxRange); void _embind_register_float( TYPEID floatType, From c424c0ac7f08fa4ce0e2eac1db1f5c53733bbb14 Mon Sep 17 00:00:00 2001 From: Jukka Jylanki Date: Wed, 27 Mar 2013 09:07:03 +0200 Subject: [PATCH 229/258] Annotate a few places where -O3 level unsafe optimizations could be performed by registering a faster form of toWireType function that omits type checks. toWireType can potentially be a 'hot' function in C++<->JS interop. --- src/embind/embind.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/embind/embind.js b/src/embind/embind.js index 1a2cec449..f52e80ec6 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -236,6 +236,8 @@ function __embind_register_integer(primitiveType, name, minRange, maxRange) { return value; }, toWireType: function(destructors, value) { + // todo: Here we have an opportunity for -O3 level "unsafe" optimizations: we could + // avoid the following two if()s and assume value is of proper type. if (typeof value !== "number") { throw new TypeError('Cannot convert "' + _embind_repr(value) + '" to ' + this.name); } @@ -255,6 +257,8 @@ function __embind_register_float(rawType, name) { return value; }, toWireType: function(destructors, value) { + // todo: Here we have an opportunity for -O3 level "unsafe" optimizations: we could + // avoid the following if() and assume value is of proper type. if (typeof value !== "number") { throw new TypeError('Cannot convert "' + _embind_repr(value) + '" to ' +this.name); } @@ -485,6 +489,8 @@ function __embind_register_struct( }, toWireType: function(destructors, o) { var fields = this.fields; + // todo: Here we have an opportunity for -O3 level "unsafe" optimizations: + // assume all fields are present without checking. for (var fieldName in fields) { if (!(fieldName in o)) { throw new TypeError('Missing field'); From da222ce8d8529d92d4256369bd910e4d5929a008 Mon Sep 17 00:00:00 2001 From: Jukka Jylanki Date: Thu, 28 Mar 2013 11:06:48 +0200 Subject: [PATCH 230/258] Fix equality testing in previous commit --- src/embind/embind.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index f52e80ec6..e0abe6f1d 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -225,7 +225,7 @@ function __embind_register_bool(rawType, name, trueValue, falseValue) { // [minRange, maxRange], inclusive. function __embind_register_integer(primitiveType, name, minRange, maxRange) { name = Pointer_stringify(name); - if (maxRange == -1) { // LLVM doesn't have signed and unsigned 32-bit types, so u32 literals come out as 'i32 -1'. Always treat those as max u32. + if (maxRange === -1) { // LLVM doesn't have signed and unsigned 32-bit types, so u32 literals come out as 'i32 -1'. Always treat those as max u32. maxRange = 4294967295; } registerType(primitiveType, { From 92dee924425cb3ee1b7a266ab7a280f94baf33be Mon Sep 17 00:00:00 2001 From: Jukka Jylanki Date: Fri, 29 Mar 2013 13:42:58 +0200 Subject: [PATCH 231/258] Migrate embind tests from IMVU repository to emscripten repository. --- tests/embind/embind.test.js | 1495 +++++++++++++++++++++++++ tests/embind/embind_test.cpp | 1677 +++++++++++++++++++++++++++-- tests/embind/embind_test.js | 398 ------- tests/embind/imvu_test_adapter.js | 614 +++++++++++ tests/embind/underscore-1.4.2.js | 1200 +++++++++++++++++++++ tests/runner.py | 54 +- 6 files changed, 4913 insertions(+), 525 deletions(-) create mode 100755 tests/embind/embind.test.js delete mode 100644 tests/embind/embind_test.js create mode 100755 tests/embind/imvu_test_adapter.js create mode 100755 tests/embind/underscore-1.4.2.js diff --git a/tests/embind/embind.test.js b/tests/embind/embind.test.js new file mode 100755 index 000000000..94d935f2e --- /dev/null +++ b/tests/embind/embind.test.js @@ -0,0 +1,1495 @@ +module({ + Emscripten: '../../../../build/embind_test.js', +}, function(imports) { + var cm = imports.Emscripten; + + var CheckForLeaks = fixture("check for leaks", function() { + this.setUp(function() { + if (typeof INVOKED_FROM_EMSCRIPTEN_TEST_RUNNER === "undefined") { // TODO: Enable this to work in Emscripten runner as well! + cm._mallocDebug(2); + assert.equal(0, cm.count_emval_handles()); + cm._mallocAssertAllMemoryFree(); + } + }); + this.tearDown(function() { + if (typeof INVOKED_FROM_EMSCRIPTEN_TEST_RUNNER === "undefined") { // TODO: Enable this to work in Emscripten runner as well! + cm._mallocAssertAllMemoryFree(); + assert.equal(0, cm.count_emval_handles()); + } + }); + }); + + var BaseFixture = CheckForLeaks; + + BaseFixture.extend("temp jig", function() { + test("temp test", function() { + }); + }); + + if (typeof INVOKED_FROM_EMSCRIPTEN_TEST_RUNNER === "undefined") { // TODO: Enable this to work in Emscripten runner as well! + + BaseFixture.extend("leak testing", function() { + test("no memory allocated at start of test", function() { + cm._mallocAssertAllMemoryFree(); + }); + test("assert when memory is allocated", function() { + var ptr = cm._malloc(42); + assert.throws(cm._MemoryAllocationError, function() { + cm._mallocAssertAllMemoryFree(); + }); + cm._free(ptr); + }); + test("allocated memory counts down again for free", function() { + var ptr = cm._malloc(42); + cm._free(ptr); + cm._mallocAssertAllMemoryFree(); + }); + test("free without malloc throws MemoryAllocationError", function() { + var ptr = cm._malloc(42); + cm._free(ptr); + assert.throws(cm._MemoryAllocationError, function() { + cm._free(ptr); + }); + }); + }); + + } + + BaseFixture.extend("access to base class members", function() { + test("method name in derived class silently overrides inherited name", function() { + var derived = new cm.Derived(); + assert.equal("Derived", derived.getClassName()); + derived.delete(); + }); + test("can reference base method from derived class", function(){ + var derived = new cm.Derived(); + assert.equal("Base", derived.getClassNameFromBase()); + derived.delete(); + }); + test("can reference base method from doubly derived class", function() { + var derivedTwice = new cm.DerivedTwice(); + assert.equal("Base", derivedTwice.getClassNameFromBase()); + derivedTwice.delete(); + }); + test("can reference base method through unbound classes", function() { + var derivedThrice = new cm.DerivedThrice(); + assert.equal("Base", derivedThrice.getClassNameFromBase()); + derivedThrice.delete(); + }); + test("property name in derived class hides identically named property in base class for set", function() { + var derived = new cm.Derived(); + derived.setMember(7); + + derived.member = 17; + + assert.equal(17, derived.getMember()); + derived.delete(); + }); + test("can reference base property from derived class for get", function(){ + var derived = new cm.Derived(); + derived.setBaseMember(5); + + assert.equal(5, derived.baseMember); + + derived.delete(); + }); + test("can reference property of any base class for get when multiply derived", function(){ + var derived = new cm.MultiplyDerived(); + derived.setBaseMember(11); + + assert.equal(11, derived.baseMember); + + derived.delete(); + }); + test("can reference base property from derived class for set", function(){ + var derived = new cm.Derived(); + + derived.baseMember = 32; + + assert.equal(32, derived.getBaseMember()); + derived.delete(); + }); + test("can reference property of any base for set when multiply derived", function(){ + var derived = new cm.MultiplyDerived(); + derived.setBaseMember(97); + + derived.baseMember = 32; + + assert.equal(32, derived.getBaseMember()); + derived.delete(); + }); + test("can reach around derived property to access base property with same name for get", function() { + var derived = new cm.Derived(); + derived.setMember(12); + derived.delete(); + }); + + test("if deriving from second base adjusts pointer", function() { + var derived = new cm.HasTwoBases; + assert.equal("Base2", derived.getField()); + derived.delete(); + }); + + test("properties adjust pointer", function() { + var derived = new cm.HasTwoBases; + derived.field = "Foo"; + assert.equal("Foo", derived.getField()); + assert.equal("Foo", derived.field); + derived.delete(); + }); + + test("calling method on unrelated class throws error", function() { + var a = new cm.HasTwoBases; + var e = assert.throws(cm.BindingError, function() { + cm.Derived.prototype.setMember.call(a, "foo"); + }); + assert.equal('Derived.setMember incompatible with "this" of type HasTwoBases', e.message); + a.delete(); + }); + + test("calling method with invalid this throws error", function() { + var e = assert.throws(cm.BindingError, function() { + cm.Derived.prototype.setMember.call(undefined, "foo"); + }); + if (typeof INVOKED_FROM_EMSCRIPTEN_TEST_RUNNER === "undefined") { // TODO: Enable this to work in Emscripten runner as well! + // got Error: expected: Derived.setMember with invalid "this": undefined, actual: Derived.setMember incompatible with "this" of type Object + assert.equal('Derived.setMember with invalid "this": undefined', e.message); + } + + var e = assert.throws(cm.BindingError, function() { + cm.Derived.prototype.setMember.call("this", "foo"); + }); + if (typeof INVOKED_FROM_EMSCRIPTEN_TEST_RUNNER === "undefined") { // TODO: Enable this to work in Emscripten runner as well! + // TODO got 'Derived.setMember incompatible with "this" of type Object' + assert.equal('Derived.setMember with invalid "this": this', e.message); + } + + var e = assert.throws(cm.BindingError, function() { + cm.Derived.prototype.setMember.call({}, "foo"); + }); + if (typeof INVOKED_FROM_EMSCRIPTEN_TEST_RUNNER === "undefined") { // TODO: Enable this to work in Emscripten runner as well! + assert.equal('Derived.setMember incompatible with "this" of type Object', e.message); + } + }); + + test("setting and getting property on unrelated class throws error", function() { + var a = new cm.HasTwoBases; + var e = assert.throws(cm.BindingError, function() { + Object.getOwnPropertyDescriptor(cm.HeldBySmartPtr.prototype, 'i').set.call(a, 10); + }); + assert.equal('HeldBySmartPtr.i setter incompatible with "this" of type HasTwoBases', e.message); + + var e = assert.throws(cm.BindingError, function() { + Object.getOwnPropertyDescriptor(cm.HeldBySmartPtr.prototype, 'i').get.call(a); + }); + assert.equal('HeldBySmartPtr.i getter incompatible with "this" of type HasTwoBases', e.message); + + a.delete(); + }); + }); + + BaseFixture.extend("automatic upcasting of parameters passed to C++", function() { + // raw + test("raw pointer argument is upcast to parameter type", function() { + var derived = new cm.Derived(); + var name = cm.embind_test_get_class_name_via_base_ptr(derived); + assert.equal("Base", name); + derived.delete(); + }); + + test("automatic raw pointer upcasting works with multiple inheritance", function() { + var derived = new cm.MultiplyDerived(); + var name = cm.embind_test_get_class_name_via_base_ptr(derived); + assert.equal("Base", name); + derived.delete(); + }); + + test("automatic raw pointer upcasting does not change local pointer", function() { + var derived = new cm.MultiplyDerived(); + cm.embind_test_get_class_name_via_base_ptr(derived); + assert.equal("MultiplyDerived", derived.getClassName()); + derived.delete(); + }); + + test("passing incompatible raw pointer to method throws exception", function() { + var base = new cm.Base(); + assert.throws(cm.BindingError, function() { + cm.embind_test_get_class_name_via_second_base_ptr(base); + }); + base.delete(); + }); + + // raw polymorphic + test("polymorphic raw pointer argument is upcast to parameter type", function() { + var derived = new cm.PolyDerived(); + var name = cm.embind_test_get_class_name_via_polymorphic_base_ptr(derived); + assert.equal("PolyBase", name); + derived.delete(); + }); + + test("automatic polymorphic raw pointer upcasting works with multiple inheritance", function() { + var derived = new cm.PolyMultiplyDerived(); + var name = cm.embind_test_get_class_name_via_polymorphic_base_ptr(derived); + assert.equal("PolyBase", name); + derived.delete(); + }); + + test("passing incompatible raw polymorphic pointer to method throws exception", function() { + var base = new cm.PolyBase(); + assert.throws(cm.BindingError, function() { + cm.embind_test_get_class_name_via_polymorphic_second_base_ptr(base); + }); + base.delete(); + + }); + + // smart + test("can pass smart pointer to raw pointer parameter", function() { + var smartBase = cm.embind_test_return_smart_base_ptr(); + assert.equal("Base", cm.embind_test_get_class_name_via_base_ptr(smartBase)); + smartBase.delete(); + }); + + test("can pass and upcast smart pointer to raw pointer parameter", function() { + var smartDerived = cm.embind_test_return_smart_derived_ptr(); + assert.equal("Base", cm.embind_test_get_class_name_via_base_ptr(smartDerived)); + smartDerived.delete(); + }); + + test("smart pointer argument is upcast to parameter type", function() { + var derived = cm.embind_test_return_smart_derived_ptr(); + assert.instanceof(derived, cm.Derived); + assert.instanceof(derived, cm.Base); + var name = cm.embind_test_get_class_name_via_smart_base_ptr(derived); + assert.equal("Base", name); + derived.delete(); + }); + + test("return smart derived ptr as base", function() { + var derived = cm.embind_test_return_smart_derived_ptr_as_base(); + assert.equal("PolyDerived", cm.embind_test_get_virtual_class_name_via_smart_polymorphic_base_ptr(derived)); + assert.equal("PolyDerived", derived.getClassName()); + derived.delete(); + }); + + test("return smart derived ptr as val", function() { + var derived = cm.embind_test_return_smart_derived_ptr_as_val(); + assert.equal("PolyDerived", cm.embind_test_get_virtual_class_name_via_smart_polymorphic_base_ptr(derived)); + derived.delete(); + }); + + test("automatic smart pointer upcasting works with multiple inheritance", function() { + var derived = cm.embind_test_return_smart_multiply_derived_ptr(); + var name = cm.embind_test_get_class_name_via_smart_base_ptr(derived); + assert.equal("Base", name); + derived.delete(); + }); + + test("automatically upcasted smart pointer parameter shares ownership with original argument", function() { + var derived = cm.embind_test_return_smart_multiply_derived_ptr(); + assert.equal(1, cm.MultiplyDerived.getInstanceCount()); + cm.embind_save_smart_base_pointer(derived); + assert.equal(1, cm.MultiplyDerived.getInstanceCount()); + derived.delete(); + assert.equal(1, cm.MultiplyDerived.getInstanceCount()); + cm.embind_save_smart_base_pointer(null); + assert.equal(0, cm.MultiplyDerived.getInstanceCount()); + }); + + // smart polymorphic + test("smart polymorphic pointer argument is upcast to parameter type", function() { + var derived = cm.embind_test_return_smart_polymorphic_derived_ptr(); + var name = cm.embind_test_get_class_name_via_smart_polymorphic_base_ptr(derived); + assert.equal("PolyBase", name); + derived.delete(); + }); + + test("automatic smart polymorphic pointer upcasting works with multiple inheritance", function() { + var derived = cm.embind_test_return_smart_polymorphic_multiply_derived_ptr(); + var name = cm.embind_test_get_class_name_via_smart_polymorphic_base_ptr(derived); + assert.equal("PolyBase", name); + derived.delete(); + }); + }); + + BaseFixture.extend("automatic downcasting of return values received from C++", function() { + // raw + test("non-polymorphic raw pointers are not downcast and do not break automatic casting mechanism", function() { + var base = cm.embind_test_return_raw_derived_ptr_as_base(); + assert.equal("Base", base.getClassName()); + assert.instanceof(base, cm.Base); + base.delete(); + }); + + // raw polymorphic + test("polymorphic raw pointer return value is downcast to allocated type (if that is bound)", function() { + var derived = cm.embind_test_return_raw_polymorphic_derived_ptr_as_base(); + assert.instanceof(derived, cm.PolyBase); + assert.instanceof(derived, cm.PolyDerived); + assert.equal("PolyDerived", derived.getClassName()); + var siblingDerived = cm.embind_test_return_raw_polymorphic_sibling_derived_ptr_as_base(); + assert.equal("PolySiblingDerived", siblingDerived.getClassName()); + siblingDerived.delete(); + derived.delete(); + }); + + test("polymorphic raw pointer return value is downcast to the most derived bound type", function() { + var derivedThrice = cm.embind_test_return_raw_polymorphic_derived_four_times_not_bound_as_base(); + // if the actual returned type is not bound, then don't assume anything + assert.equal("PolyBase", derivedThrice.getClassName()); + // if we ever fix this, then reverse the assertion + //assert.equal("PolyDerivedThrice", derivedThrice.getClassName()); + derivedThrice.delete(); + }); + + test("polymorphic smart pointer return value is downcast to the most derived type which has an associated smart pointer", function() { + var derived = cm.embind_test_return_poly_derived_twice_without_smart_pointer_as_poly_base(); + // if the actual returned type is not bound, then don't assume anything + assert.equal("PolyBase", derived.getClassName()); + // if we ever fix this, then remove the assertion + //assert.equal("PolyDerived", derived.getClassName()); + derived.delete(); + }); + + test("automatic downcasting works with multiple inheritance", function() { + var base = cm.embind_test_return_raw_polymorphic_multiply_derived_ptr_as_base(); + var secondBase = cm.embind_test_return_raw_polymorphic_multiply_derived_ptr_as_second_base(); + assert.equal("PolyMultiplyDerived", base.getClassName()); + // embind does not support multiple inheritance + //assert.equal("PolyMultiplyDerived", secondBase.getClassName()); + secondBase.delete(); + base.delete(); + }); + + // smart + test("non-polymorphic smart pointers do not break automatic casting mechanism", function() { + }); + + // smart polymorphic + test("automatically downcasting a smart pointer does not change the underlying pointer", function() { + cm.PolyDerived.setPtrDerived(); + assert.equal("PolyBase", cm.PolyDerived.getPtrClassName()); + var derived = cm.PolyDerived.getPtr(); + assert.equal("PolyDerived", derived.getClassName()); + assert.equal("PolyBase", cm.PolyDerived.getPtrClassName()); + derived.delete(); + cm.PolyDerived.releasePtr(); + }); + + test("polymorphic smart pointer return value is actual allocated type (when bound)", function() { + var derived = cm.embind_test_return_smart_polymorphic_derived_ptr_as_base(); + assert.equal("PolyDerived", derived.getClassName()); + + var siblingDerived = cm.embind_test_return_smart_polymorphic_sibling_derived_ptr_as_base(); + assert.equal("PolySiblingDerived", siblingDerived.getClassName()); + + siblingDerived.delete(); + derived.delete(); + }); + }); + + BaseFixture.extend("embind", function() { + test("value creation", function() { + assert.equal(15, cm.emval_test_new_integer()); + assert.equal("Hello everyone", cm.emval_test_new_string()); + assert.equal("Hello everyone", cm.emval_test_get_string_from_val({key: "Hello everyone"})); + + var object = cm.emval_test_new_object(); + assert.equal('bar', object.foo); + assert.equal(1, object.baz); + }); + + test("pass const reference to primitive", function() { + assert.equal(3, cm.const_ref_adder(1, 2)); + }); + + test("passthrough", function() { + var a = {foo: 'bar'}; + var b = cm.emval_test_passthrough(a); + a.bar = 'baz'; + assert.equal('baz', b.bar); + + assert.equal(0, cm.count_emval_handles()); + }); + + test("void return converts to undefined", function() { + assert.equal(undefined, cm.emval_test_return_void()); + }); + + test("booleans can be marshalled", function() { + assert.equal(false, cm.emval_test_not(true)); + assert.equal(true, cm.emval_test_not(false)); + }); + + test("convert double to unsigned", function() { + var rv = cm.emval_test_as_unsigned(1.5); + assert.equal('number', typeof rv); + assert.equal(1, rv); + assert.equal(0, cm.count_emval_handles()); + }); + + test("get length of array", function() { + assert.equal(10, cm.emval_test_get_length([0, 1, 2, 3, 4, 5, 'a', 'b', 'c', 'd'])); + assert.equal(0, cm.count_emval_handles()); + }); + + test("add a bunch of things", function() { + assert.equal(66.0, cm.emval_test_add(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)); + assert.equal(0, cm.count_emval_handles()); + }); + + test("sum array", function() { + assert.equal(66, cm.emval_test_sum([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])); + assert.equal(0, cm.count_emval_handles()); + }); + + test("strings", function() { + assert.equal("foobar", "foo" + "bar"); + assert.equal("foobar", cm.emval_test_take_and_return_std_string("foobar")); + + assert.equal("foobar", cm.emval_test_take_and_return_std_string_const_ref("foobar")); + }); + + test("nuls pass through strings", function() { + assert.equal("foo\0bar", cm.emval_test_take_and_return_std_string("foo\0bar")); + }); + + test("no memory leak when passing strings in by const reference", function() { + cm.emval_test_take_and_return_std_string_const_ref("foobar"); + }); + + test("can create new object", function() { + assert.deepEqual({}, cm.embind_test_new_Object()); + }); + + test("can invoke constructors with arguments", function() { + function constructor(i, s, argument) { + this.i = i; + this.s = s; + this.argument = argument; + } + constructor.prototype.method = function() { + return this.argument; + }; + var x = {}; + var instance = cm.embind_test_new_factory(constructor, x); + assert.equal(10, instance.i); + assert.equal("hello", instance.s); + assert.equal(x, instance.argument); + }); + + test("can return module property objects", function() { + assert.equal(cm.HEAP8, cm.get_module_property("HEAP8")); + }); + + test("can return big class instances", function() { + var c = cm.embind_test_return_big_class_instance(); + assert.equal(11, c.member); + c.delete(); + }); + + test("can return small class instances", function() { + var c = cm.embind_test_return_small_class_instance(); + assert.equal(7, c.member); + c.delete(); + }); + + test("can pass small class instances", function() { + var c = new cm.SmallClass(); + var m = cm.embind_test_accept_small_class_instance(c); + assert.equal(7, m); + c.delete(); + }); + + test("can pass big class instances", function() { + var c = new cm.BigClass(); + var m = cm.embind_test_accept_big_class_instance(c); + assert.equal(11, m); + c.delete(); + }); + + test("can get member classes then call its member functions", function() { + var p = new cm.ParentClass(); + var c = p.getBigClass(); + var m = c.getMember(); + assert.equal(11, m); + c.delete(); + p.delete(); + }); + + test('C++ -> JS primitive type range checks', function() { + // all types should have zero. + assert.equal("0", cm.char_to_string(0)); + assert.equal("0", cm.signed_char_to_string(0)); + assert.equal("0", cm.unsigned_char_to_string(0)); + assert.equal("0", cm.short_to_string(0)); + assert.equal("0", cm.unsigned_short_to_string(0)); + assert.equal("0", cm.int_to_string(0)); + assert.equal("0", cm.unsigned_int_to_string(0)); + assert.equal("0", cm.long_to_string(0)); + assert.equal("0", cm.unsigned_long_to_string(0)); + + // all types should have positive values. + assert.equal("5", cm.char_to_string(5)); + assert.equal("5", cm.signed_char_to_string(5)); + assert.equal("5", cm.unsigned_char_to_string(5)); + assert.equal("5", cm.short_to_string(5)); + assert.equal("5", cm.unsigned_short_to_string(5)); + assert.equal("5", cm.int_to_string(5)); + assert.equal("5", cm.unsigned_int_to_string(5)); + assert.equal("5", cm.long_to_string(5)); + assert.equal("5", cm.unsigned_long_to_string(5)); + + // signed types should have negative values. + assert.equal("-5", cm.char_to_string(-5)); // Assuming char as signed. + assert.equal("-5", cm.signed_char_to_string(-5)); + assert.equal("-5", cm.short_to_string(-5)); + assert.equal("-5", cm.int_to_string(-5)); + assert.equal("-5", cm.long_to_string(-5)); + + // assumptions: char == signed char == 8 bits + // unsigned char == 8 bits + // short == 16 bits + // int == long == 32 bits + + // all types should have their max positive values. + assert.equal("127", cm.char_to_string(127)); + assert.equal("127", cm.signed_char_to_string(127)); + assert.equal("255", cm.unsigned_char_to_string(255)); + assert.equal("32767", cm.short_to_string(32767)); + assert.equal("65535", cm.unsigned_short_to_string(65535)); + assert.equal("2147483647", cm.int_to_string(2147483647)); + assert.equal("4294967295", cm.unsigned_int_to_string(4294967295)); + assert.equal("2147483647", cm.long_to_string(2147483647)); + assert.equal("4294967295", cm.unsigned_long_to_string(4294967295)); + + // signed types should have their min negative values. + assert.equal("-128", cm.char_to_string(-128)); + assert.equal("-128", cm.signed_char_to_string(-128)); + assert.equal("-32768", cm.short_to_string(-32768)); + assert.equal("-2147483648", cm.int_to_string(-2147483648)); + assert.equal("-2147483648", cm.long_to_string(-2147483648)); + + // passing out of range values should fail. + assert.throws(TypeError, function() { cm.char_to_string(-129); }); + assert.throws(TypeError, function() { cm.char_to_string(128); }); + assert.throws(TypeError, function() { cm.signed_char_to_string(-129); }); + assert.throws(TypeError, function() { cm.signed_char_to_string(128); }); + assert.throws(TypeError, function() { cm.unsigned_char_to_string(-1); }); + assert.throws(TypeError, function() { cm.unsigned_char_to_string(256); }); + assert.throws(TypeError, function() { cm.short_to_string(-32769); }); + assert.throws(TypeError, function() { cm.short_to_string(32768); }); + assert.throws(TypeError, function() { cm.unsigned_short_to_string(-1); }); + assert.throws(TypeError, function() { cm.unsigned_short_to_string(65536); }); + assert.throws(TypeError, function() { cm.int_to_string(-2147483649); }); + assert.throws(TypeError, function() { cm.int_to_string(2147483648); }); + assert.throws(TypeError, function() { cm.unsigned_int_to_string(-1); }); + assert.throws(TypeError, function() { cm.unsigned_int_to_string(4294967296); }); + assert.throws(TypeError, function() { cm.long_to_string(-2147483649); }); + assert.throws(TypeError, function() { cm.long_to_string(2147483648); }); + assert.throws(TypeError, function() { cm.unsigned_long_to_string(-1); }); + assert.throws(TypeError, function() { cm.unsigned_long_to_string(4294967296); }); + + }); + +/* + test("can get templated member classes then call its member functions", function() { + var p = new cm.ContainsTemplatedMemberClass(); + var c = p.getTestTemplate(); + var m = c.getMember(1); + assert.equal(87, m); + c.delete(); + p.delete(); + }); +*/ + }); + + BaseFixture.extend("vector", function() { + test("std::vector returns as an native object", function() { + var vec = cm.emval_test_return_vector(); + + assert.equal(3, vec.size()); + assert.equal(10, vec.get(0)); + assert.equal(20, vec.get(1)); + assert.equal(30, vec.get(2)); + vec.delete(); + }); + + test("out of bounds std::vector access returns undefined", function() { + var vec = cm.emval_test_return_vector(); + + assert.throws(TypeError, function() { vec.get(-1); }); + assert.equal(undefined, vec.get(4)); + + vec.delete(); + }); + + test("std::vector> can be passed back", function() { + var vec = cm.emval_test_return_shared_ptr_vector(); + + assert.equal(2, vec.size()); + var str0 = vec.get(0); + var str1 = vec.get(1); + + assert.equal('string #1', str0.get()); + assert.equal('string #2', str1.get()); + str0.delete(); + str1.delete(); + + vec.delete(); + }); + + test("objects can be pushed back", function() { + var vectorHolder = new cm.VectorHolder(); + var vec = vectorHolder.get(); + assert.equal(2, vec.size()); + + var str = new cm.StringHolder('abc'); + vec.push_back(str); + str.delete(); + assert.equal(3, vec.size()); + var str = vec.get(2); + assert.equal('abc', str.get()); + + str.delete(); + vec.delete(); + vectorHolder.delete(); + }); + + test("can get elements with array operator", function(){ + var vec = cm.emval_test_return_vector(); + assert.equal(10, vec.get(0)); + vec.delete(); + }); + + test("can set elements with array operator", function() { + var vec = cm.emval_test_return_vector(); + assert.equal(10, vec.get(0)); + vec.set(2, 60); + assert.equal(60, vec.get(2)); + vec.delete(); + }); + + test("can set and get objects", function() { + var vec = cm.emval_test_return_shared_ptr_vector(); + var str = vec.get(0); + assert.equal('string #1', str.get()); + str.delete(); + vec.delete(); + }); + }); + + BaseFixture.extend("map", function() { + test("std::map returns as native object", function() { + var map = cm.embind_test_get_string_int_map(); + + assert.equal(2, map.size()); + assert.equal(1, map.get("one")); + assert.equal(2, map.get("two")); + + map.delete(); + }); + + test("std::map can set keys and values", function() { + var map = cm.embind_test_get_string_int_map(); + + assert.equal(2, map.size()); + + map.set("three", 3); + + assert.equal(3, map.size()); + assert.equal(3, map.get("three")); + + map.set("three", 4); + + assert.equal(3, map.size()); + assert.equal(4, map.get("three")); + + map.delete(); + }); + }); + + BaseFixture.extend("functors", function() { + test("can get and call function ptrs", function() { + var ptr = cm.emval_test_get_function_ptr(); + assert.equal("foobar", ptr.opcall("foobar")); + ptr.delete(); + }); + + test("can pass functor to C++", function() { + var ptr = cm.emval_test_get_function_ptr(); + assert.equal("asdf", cm.emval_test_take_and_call_functor(ptr)); + ptr.delete(); + }); + + test("can clone handles", function() { + var a = cm.emval_test_get_function_ptr(); + assert.equal(1, a.$$.count.value); + var b = a.clone(); + assert.equal(2, a.$$.count.value); + assert.equal(2, b.$$.count.value); + a.delete(); + + assert.throws(cm.BindingError, function() { + a.delete(); + }); + b.delete(); + }); + }); + + BaseFixture.extend("classes", function() { + test("class instance", function() { + var a = {foo: 'bar'}; + assert.equal(0, cm.count_emval_handles()); + var c = new cm.ValHolder(a); + assert.equal(1, cm.count_emval_handles()); + assert.equal('bar', c.getVal().foo); + assert.equal(1, cm.count_emval_handles()); + + c.setVal('1234'); + assert.equal('1234', c.getVal()); + + c.delete(); + assert.equal(0, cm.count_emval_handles()); + }); + + test("class instance $$ property is non-enumerable", function() { + var c = new cm.ValHolder(undefined); + assert.deepEqual([], Object.keys(c)); + var d = c.clone(); + c.delete(); + + assert.deepEqual([], Object.keys(d)); + d.delete(); + }); + + test("class methods", function() { + assert.equal(10, cm.ValHolder.some_class_method(10)); + + var b = cm.ValHolder.makeValHolder("foo"); + assert.equal("foo", b.getVal()); + b.delete(); + }); + + test("can't call methods on deleted class instances", function() { + var c = new cm.ValHolder(undefined); + c.delete(); + assert.throws(cm.BindingError, function() { + c.getVal(); + }); + assert.throws(cm.BindingError, function() { + c.delete(); + }); + }); + + test("calling constructor without new raises BindingError", function() { + var e = assert.throws(cm.BindingError, function() { + cm.ValHolder(undefined); + }); + assert.equal("Use 'new' to construct ValHolder", e.message); + }); + + test("can return class instances by value", function() { + var c = cm.emval_test_return_ValHolder(); + assert.deepEqual({}, c.getVal()); + c.delete(); + }); + + test("can pass class instances to functions by reference", function() { + var a = {a:1}; + var c = new cm.ValHolder(a); + cm.emval_test_set_ValHolder_to_empty_object(c); + assert.deepEqual({}, c.getVal()); + c.delete(); + }); + + test("can pass smart pointer by reference", function() { + var base = cm.embind_test_return_smart_base_ptr(); + var name = cm.embind_test_get_class_name_via_reference_to_smart_base_ptr(base); + assert.equal("Base", name); + base.delete(); + }); + + test("can pass smart pointer by value", function() { + var base = cm.embind_test_return_smart_base_ptr(); + var name = cm.embind_test_get_class_name_via_smart_base_ptr(base); + assert.equal("Base", name); + base.delete(); + }); + + // todo: fix this + // This test does not work because we make no provision for argument values + // having been changed after returning from a C++ routine invocation. In + // this specific case, the original pointee of the smart pointer was + // freed and replaced by a new one, but the ptr in our local handle + // was never updated after returning from the call. + test("can modify smart pointers passed by reference", function() { +// var base = cm.embind_test_return_smart_base_ptr(); +// cm.embind_modify_smart_pointer_passed_by_reference(base); +// assert.equal("Changed", base.getClassName()); +// base.delete(); + }); + + test("can not modify smart pointers passed by value", function() { + var base = cm.embind_test_return_smart_base_ptr(); + cm.embind_attempt_to_modify_smart_pointer_when_passed_by_value(base); + assert.equal("Base", base.getClassName()); + base.delete(); + }); + + test("const return value", function() { + var c = new cm.ValHolder("foo"); + assert.equal("foo", c.getConstVal()); + c.delete(); + }); + + test("return object by const ref", function() { + var c = new cm.ValHolder("foo"); + assert.equal("foo", c.getValConstRef()); + c.delete(); + }); + + test("instanceof", function() { + var c = new cm.ValHolder("foo"); + assert.instanceof(c, cm.ValHolder); + c.delete(); + }); + + test("can access struct fields", function() { + var c = new cm.CustomStruct(); + assert.equal(10, c.field); + assert.equal(10, c.getField()); + c.delete(); + }); + + test("can set struct fields", function() { + var c = new cm.CustomStruct(); + c.field = 15; + assert.equal(15, c.field); + c.delete(); + }); + + test("assignment returns value", function() { + var c = new cm.CustomStruct(); + assert.equal(15, c.field = 15); + c.delete(); + }); + + test("assigning string to integer raises TypeError", function() { + var c = new cm.CustomStruct(); + + var e = assert.throws(TypeError, function() { + c.field = "hi"; + }); + assert.equal('Cannot convert "hi" to int', e.message); + + var e = assert.throws(TypeError, function() { + c.field = {foo:'bar'}; + }); + assert.equal('Cannot convert "[object Object]" to int', e.message); + + c.delete(); + }); + + test("can return tuples by value", function() { + var c = cm.emval_test_return_TupleVector(); + assert.deepEqual([1, 2, 3], 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() { + var a = new cm.ValHolder({}); + assert.equal(1, cm.count_emval_handles()); + var b = a.clone(); + a.delete(); + + assert.equal(1, cm.count_emval_handles()); + + assert.throws(cm.BindingError, function() { + a.delete(); + }); + b.delete(); + + assert.equal(0, cm.count_emval_handles()); + }); + + test("A shared pointer set/get point to the same underlying pointer", function() { + var a = new cm.SharedPtrHolder(); + var b = a.get(); + + a.set(b); + var c = a.get(); + + assert.equal(b.$$.ptr, c.$$.ptr); + b.delete(); + c.delete(); + a.delete(); + }); + + test("can return shared ptrs from instance methods", function() { + var a = new cm.SharedPtrHolder(); + + // returns the shared_ptr. + var b = a.get(); + + assert.equal("a string", b.get()); + b.delete(); + a.delete(); + }); + + test("smart ptrs clone correctly", function() { + assert.equal(0, cm.count_emval_handles()); + + var a = cm.emval_test_return_shared_ptr(); + + var b = a.clone(); + a.delete(); + + assert.equal(1, cm.count_emval_handles()); + + assert.throws(cm.BindingError, function() { + a.delete(); + }); + b.delete(); + + assert.equal(0, cm.count_emval_handles()); + }); + + test("can't clone if already deleted", function() { + var a = new cm.ValHolder({}); + a.delete(); + assert.throws(cm.BindingError, function() { + a.clone(); + }); + }); + + test("virtual calls work correctly", function() { + var derived = cm.embind_test_return_raw_polymorphic_derived_ptr_as_base(); + assert.equal("PolyDerived", derived.virtualGetClassName()); + derived.delete(); + }); + + test("virtual calls work correctly on smart ptrs", function() { + var derived = cm.embind_test_return_smart_polymorphic_derived_ptr_as_base(); + assert.equal("PolyDerived", derived.virtualGetClassName()); + derived.delete(); + }); + + test("Empty smart ptr is null", function() { + var a = cm.emval_test_return_empty_shared_ptr(); + assert.equal(null, a); + }); + + test("string cannot be given as smart pointer argument", function() { + assert.throws(cm.BindingError, function() { + cm.emval_test_is_shared_ptr_null("hello world"); + }); + }); + + test("number cannot be given as smart pointer argument", function() { + assert.throws(cm.BindingError, function() { + cm.emval_test_is_shared_ptr_null(105); + }); + }); + + test("raw pointer cannot be given as smart pointer argument", function() { + var p = new cm.ValHolder({}); + assert.throws(cm.BindingError, function() { cm.emval_test_is_shared_ptr_null(p); }); + p.delete(); + }); + + test("null is passed as empty smart pointer", function() { + assert.true(cm.emval_test_is_shared_ptr_null(null)); + }); + + test("Deleting already deleted smart ptrs fails", function() { + var a = cm.emval_test_return_shared_ptr(); + a.delete(); + assert.throws(cm.BindingError, function() { + a.delete(); + }); + }); + + test("StringHolder", function() { + var a = new cm.StringHolder("foobar"); + assert.equal("foobar", a.get()); + + a.set("barfoo"); + assert.equal("barfoo", a.get()); + + assert.equal("barfoo", a.get_const_ref()); + + a.delete(); + }); + + test("can call methods on unique ptr", function() { + var result = cm.emval_test_return_unique_ptr(); + + result.setVal('1234'); + assert.equal('1234', result.getVal()); + result.delete(); + }); + + test("can call methods on shared ptr", function(){ + var result = cm.emval_test_return_shared_ptr(); + result.setVal('1234'); + + assert.equal('1234', result.getVal()); + result.delete(); + }); + + test("Non functors throw exception", function() { + var a = {foo: 'bar'}; + var c = new cm.ValHolder(a); + assert.throws(TypeError, function() { + c(); + }); + c.delete(); + }); + + test("non-member methods", function() { + var a = {foo: 'bar'}; + var c = new cm.ValHolder(a); + c.setEmpty(); // non-member method + assert.deepEqual({}, c.getValNonMember()); + c.delete(); + }); + + test("instantiating class without constructor gives error", function() { + var e = assert.throws(cm.BindingError, function() { + cm.AbstractClass(); + }); + assert.equal("Use 'new' to construct AbstractClass", e.message); + + var e = assert.throws(cm.BindingError, function() { + new cm.AbstractClass(); + }); + assert.equal("AbstractClass has no accessible constructor", e.message); + }); + + test("can construct class with external constructor", function() { + var e = new cm.HasExternalConstructor("foo"); + assert.instanceof(e, cm.HasExternalConstructor); + assert.equal("foo", e.getString()); + e.delete(); + }); + }); + + BaseFixture.extend("const", function() { + test("calling non-const method with const handle is error", function() { + var vh = cm.ValHolder.makeConst({}); + var e = assert.throws(cm.BindingError, function() { + vh.setVal({}); + }); + assert.equal('Cannot convert argument of type ValHolder const* to parameter type ValHolder*', e.message); + vh.delete(); + }); + + test("passing const pointer to non-const pointer is error", function() { + var vh = new cm.ValHolder.makeConst({}); + var e = assert.throws(cm.BindingError, function() { + cm.ValHolder.set_via_raw_pointer(vh, {}); + }); + assert.equal('Cannot convert argument of type ValHolder const* to parameter type ValHolder*', e.message); + vh.delete(); + }); + }); + + BaseFixture.extend("smart pointers", function() { + test("constructor can return smart pointer", function() { + var e = new cm.HeldBySmartPtr(10, "foo"); + assert.instanceof(e, cm.HeldBySmartPtr); + assert.equal(10, e.i); + assert.equal("foo", e.s); + var f = cm.takesHeldBySmartPtr(e); + f.delete(); + e.delete(); + }); + + test("cannot pass incorrect smart pointer type", function() { + var e = cm.emval_test_return_shared_ptr(); + assert.throws(cm.BindingError, function() { + cm.takesHeldBySmartPtr(e); + }); + e.delete(); + }); + + test("smart pointer object has no object keys", function() { + var e = new cm.HeldBySmartPtr(10, "foo"); + assert.deepEqual([], Object.keys(e)); + + var f = e.clone(); + e.delete(); + + assert.deepEqual([], Object.keys(f)); + f.delete(); + }); + + test("smart pointer object has correct constructor name", function() { + var e = new cm.HeldBySmartPtr(10, "foo"); + assert.equal('HeldBySmartPtr', e.constructor.name); + e.delete(); + }); + + test("constructor can return smart pointer", function() { + var e = new cm.HeldBySmartPtr(10, "foo"); + assert.instanceof(e, cm.HeldBySmartPtr); + assert.equal(10, e.i); + assert.equal("foo", e.s); + var f = cm.takesHeldBySmartPtr(e); + assert.instanceof(f, cm.HeldBySmartPtr); + f.delete(); + e.delete(); + }); + + test("custom smart pointer", function() { + var e = new cm.HeldByCustomSmartPtr(20, "bar"); + assert.instanceof(e, cm.HeldByCustomSmartPtr); + assert.equal(20, e.i); + assert.equal("bar", e.s); + e.delete(); + }); + + test("custom smart pointer passed through wiretype", function() { + var e = new cm.HeldByCustomSmartPtr(20, "bar"); + var f = cm.passThroughCustomSmartPtr(e); + e.delete(); + + assert.instanceof(f, cm.HeldByCustomSmartPtr); + assert.equal(20, f.i); + assert.equal("bar", f.s); + + f.delete(); + }); + + test("cannot give null to by-value argument", function() { + var e = assert.throws(cm.BindingError, function() { + cm.takesHeldBySmartPtr(null); + }); + assert.equal('null is not a valid HeldBySmartPtr', e.message); + }); + + test("raw pointer can take and give null", function() { + assert.equal(null, cm.passThroughRawPtr(null)); + }); + + test("custom smart pointer can take and give null", function() { + assert.equal(null, cm.passThroughCustomSmartPtr(null)); + }); + + test("cannot pass shared_ptr to CustomSmartPtr", function() { + var o = cm.HeldByCustomSmartPtr.createSharedPtr(10, "foo"); + var e = assert.throws(cm.BindingError, function() { + cm.passThroughCustomSmartPtr(o); + }); + assert.equal('Cannot convert argument of type NSt3__110shared_ptrI20HeldByCustomSmartPtrEE to parameter type 14CustomSmartPtrI20HeldByCustomSmartPtrE', e.message); + o.delete(); + }); + + test("custom smart pointers can be passed to shared_ptr parameter", function() { + var e = cm.HeldBySmartPtr.newCustomPtr(10, "abc"); + assert.equal(10, e.i); + assert.equal("abc", e.s); + + cm.takesHeldBySmartPtrSharedPtr(e).delete(); + e.delete(); + }); + + test("can call non-member functions as methods", function() { + var e = new cm.HeldBySmartPtr(20, "bar"); + var f = e.returnThis(); + e.delete(); + assert.equal(20, f.i); + assert.equal("bar", f.s); + f.delete(); + }); + }); + + BaseFixture.extend("enumerations", function() { + test("can compare enumeration values", function() { + assert.equal(cm.Enum.ONE, cm.Enum.ONE); + assert.notEqual(cm.Enum.ONE, cm.Enum.TWO); + }); + + if (typeof INVOKED_FROM_EMSCRIPTEN_TEST_RUNNER === "undefined") { // TODO: Enable this to work in Emscripten runner as well! + test("repr includes enum value", function() { + assert.equal('<#Enum_ONE {}>', IMVU.repr(cm.Enum.ONE)); + assert.equal('<#Enum_TWO {}>', IMVU.repr(cm.Enum.TWO)); + }); + } + + test("instanceof", function() { + assert.instanceof(cm.Enum.ONE, cm.Enum); + }); + + test("can pass and return enumeration values to functions", function() { + assert.equal(cm.Enum.TWO, cm.emval_test_take_and_return_Enum(cm.Enum.TWO)); + }); + }); + + BaseFixture.extend("C++11 enum class", function() { + test("can compare enumeration values", function() { + assert.equal(cm.EnumClass.ONE, cm.EnumClass.ONE); + assert.notEqual(cm.EnumClass.ONE, cm.EnumClass.TWO); + }); + + if (typeof INVOKED_FROM_EMSCRIPTEN_TEST_RUNNER === "undefined") { // TODO: Enable this to work in Emscripten runner as well! + test("repr includes enum value", function() { + assert.equal('<#EnumClass_ONE {}>', IMVU.repr(cm.EnumClass.ONE)); + assert.equal('<#EnumClass_TWO {}>', IMVU.repr(cm.EnumClass.TWO)); + }); + } + + test("instanceof", function() { + assert.instanceof(cm.EnumClass.ONE, cm.EnumClass); + }); + + test("can pass and return enumeration values to functions", function() { + assert.equal(cm.EnumClass.TWO, cm.emval_test_take_and_return_EnumClass(cm.EnumClass.TWO)); + }); + }); + + BaseFixture.extend("emval call tests", function() { + test("can call functions from C++", function() { + var called = false; + cm.emval_test_call_function(function(i, f, tv, sv) { + called = true; + assert.equal(10, i); + assert.equal(1.5, f); + assert.deepEqual([1.25, 2.5, 3.75], 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); + }); + }); + + BaseFixture.extend("extending built-in classes", function() { + // cm.ValHolder.prototype.patched = 10; // this sets instanceCounts.patched inside of Deletable module !?! + + test("can access patched value on new instances", function() { + cm.ValHolder.prototype.patched = 10; + var c = new cm.ValHolder(undefined); + assert.equal(10, c.patched); + c.delete(); + cm.ValHolder.prototype.patched = undefined; + }); + + test("can access patched value on returned instances", function() { + cm.ValHolder.prototype.patched = 10; + var c = cm.emval_test_return_ValHolder(); + assert.equal(10, c.patched); + c.delete(); + cm.ValHolder.prototype.patched = undefined; + }); + }); + + BaseFixture.extend("raw pointers", function() { + test("can pass raw pointers into functions if explicitly allowed", function() { + var vh = new cm.ValHolder({}); + cm.ValHolder.set_via_raw_pointer(vh, 10); + assert.equal(10, cm.ValHolder.get_via_raw_pointer(vh)); + vh.delete(); + }); + + test("can return raw pointers from functions if explicitly allowed", function() { + var p = cm.embind_test_return_raw_base_ptr(); + assert.equal("Base", p.getClassName()); + p.delete(); + }); + + test("can pass multiple raw pointers to functions", function() { + var target = new cm.ValHolder(undefined); + var source = new cm.ValHolder("hi"); + cm.ValHolder.transfer_via_raw_pointer(target, source); + assert.equal("hi", target.getVal()); + target.delete(); + source.delete(); + }); + }); + + BaseFixture.extend("JavaScript interface", function() { + this.setUp(function() { + this.testobj = { + "method1": function() { return 111; }, + "method2": function() { return 222; } + }; + }); + + test("pass js object to c++ and call its method", function() { + var obj = new cm.JSInterfaceHolder(this.testobj); + assert.equal(111, obj.callMethod("method1")); + assert.equal(222, obj.callMethod("method2")); + assert.equal(111, obj.callMethodUsingSharedPtr("method1")); + assert.equal(222, obj.callMethodUsingSharedPtr("method2")); + obj.delete(); + }); + }); + + BaseFixture.extend("abstract methods", function() { + test("can call abstract methods", function() { + var obj = cm.getAbstractClass(); + assert.equal("from concrete", obj.abstractMethod()); + obj.delete(); + }); + + test("can implement abstract methods in JavaScript", function() { + var expected = "my JS string"; + function MyImplementation() { + this.rv = expected; + } + MyImplementation.prototype.abstractMethod = function() { + return this.rv; + }; + + var impl = cm.AbstractClass.implement(new MyImplementation); + assert.equal(expected, impl.abstractMethod()); + assert.equal(expected, cm.callAbstractMethod(impl)); + impl.delete(); + }); + }); + + BaseFixture.extend("registration order", function() { + test("registration of tuple elements out of order leaves them in order", function() { + var ot = cm.getOrderedTuple(); + assert.instanceof(ot[0], cm.FirstElement); + assert.instanceof(ot[1], cm.SecondElement); + ot[0].delete(); + ot[1].delete(); + }); + + test("registration of struct elements out of order", function() { + var os = cm.getOrderedStruct(); + assert.instanceof(os.first, cm.FirstElement); + assert.instanceof(os.second, cm.SecondElement); + os.first.delete(); + os.second.delete(); + }); + }); + + if (typeof INVOKED_FROM_EMSCRIPTEN_TEST_RUNNER === "undefined") { // TODO: Enable this to work in Emscripten runner as well! + + BaseFixture.extend("unbound types", function() { + function assertMessage(fn, message) { + var e = assert.throws(cm.UnboundTypeError, fn); + assert.equal(message, e.message); + } + + test("calling function with unbound types produces error", function() { + assertMessage( + function() { + cm.getUnboundClass(); + }, + 'Cannot call getUnboundClass due to unbound types: UnboundClass'); + }); + + test("unbound base class produces error", function() { + assertMessage( + function() { + cm.getHasUnboundBase(); + }, + 'Cannot call getHasUnboundBase due to unbound types: UnboundClass'); + }); + + test("construct of class with unbound base", function() { + assertMessage( + function() { + new cm.HasUnboundBase; + }, 'Cannot construct HasUnboundBase due to unbound types: UnboundClass'); + }); + + test("unbound constructor argument", function() { + assertMessage( + function() { + new cm.HasConstructorUsingUnboundArgument; + }, + 'Cannot construct HasConstructorUsingUnboundArgument due to unbound types: UnboundClass'); + }); + + test("unbound constructor argument of class with unbound base", function() { + assertMessage( + function() { + new cm.HasConstructorUsingUnboundArgumentAndUnboundBase; + }, + 'Cannot construct HasConstructorUsingUnboundArgumentAndUnboundBase due to unbound types: SecondUnboundClass'); + }); + + test('class function with unbound argument', function() { + var x = new cm.BoundClass; + assertMessage( + function() { + x.method(); + }, 'Cannot call BoundClass.method due to unbound types: UnboundClass'); + x.delete(); + }); + + test('class class function with unbound argument', function() { + assertMessage( + function() { + cm.BoundClass.classfunction(); + }, 'Cannot call BoundClass.classfunction due to unbound types: UnboundClass'); + }); + + test('class property of unbound type', function() { + var x = new cm.BoundClass; + var y; + assertMessage( + function() { + y = x.property; + }, 'Cannot access BoundClass.property due to unbound types: UnboundClass'); + assertMessage( + function() { + x.property = 10; + }, 'Cannot access BoundClass.property due to unbound types: UnboundClass'); + x.delete(); + }); + + // todo: tuple elements + // todo: tuple element accessors + // todo: struct fields + }); + + } + + BaseFixture.extend("noncopyable", function() { + test('can call method on noncopyable object', function() { + var x = new cm.Noncopyable; + assert.equal('foo', x.method()); + x.delete(); + }); + }); + + BaseFixture.extend("function names", function() { + assert.equal('ValHolder', cm.ValHolder.name); + assert.equal('ValHolder$setVal', cm.ValHolder.prototype.setVal.name); + assert.equal('ValHolder$makeConst', cm.ValHolder.makeConst.name); + }); +}); + +// If running as part of the emscripten test runner suite, and not as part of the IMVU suite, +// we launch the test execution from here. IMVU suite uses its own dedicated mechanism instead of this. +if (typeof run_all_tests !== "undefined") + run_all_tests(); diff --git a/tests/embind/embind_test.cpp b/tests/embind/embind_test.cpp index 485cdfcd8..e9feccfef 100644 --- a/tests/embind/embind_test.cpp +++ b/tests/embind/embind_test.cpp @@ -1,5 +1,6 @@ #include #include +#include #include using namespace emscripten; @@ -27,6 +28,10 @@ val emval_test_new_string() { return val("Hello everyone"); } +std::string emval_test_get_string_from_val(val v) { + return v["key"].as(); +} + val emval_test_new_object() { val rv(val::object()); rv.set("foo", val("bar")); @@ -54,18 +59,22 @@ unsigned emval_test_as_unsigned(val v) { } unsigned emval_test_get_length(val v) { - return v.get("length").as(); + return v["length"].as(); } double emval_test_add(char c, signed char sc, unsigned char uc, signed short ss, unsigned short us, signed int si, unsigned int ui, signed long sl, unsigned long ul, float f, double d) { return c + sc + uc + ss + us + si + ui + sl + ul + f + d; } +float const_ref_adder(const int& i, const float& f) { + return i + f; +} + unsigned emval_test_sum(val v) { - unsigned length = v.get("length").as(); + unsigned length = v["length"].as(); double rv = 0; for (unsigned i = 0; i < length; ++i) { - rv += v.get(i).as(); + rv += v[i].as(); } return rv; } @@ -82,30 +91,70 @@ std::string emval_test_take_and_return_std_string_const_ref(const std::string& s return str; } +std::function emval_test_get_function_ptr() { + return emval_test_take_and_return_std_string; +} + +std::string emval_test_take_and_call_functor(std::function func) { + return func("asdf"); +} + class ValHolder { public: ValHolder(val v) - : v(v) + : v_(v) {} val getVal() const { - return v; + return v_; + } + + val getValNonConst() { + return v_; + } + + const val getConstVal() const { + return v_; + } + + const val& getValConstRef() const { + return v_; } void setVal(val v) { - this->v = v; - } - - int returnIntPlusFive( int x ) { - return x + 5; + this->v_ = v; } static int some_class_method(int i) { return i; } + static const ValHolder* makeConst(val v) { + return new ValHolder(v); + } + + static ValHolder makeValHolder(val v) { + return ValHolder(v); + } + + static void set_via_raw_pointer(ValHolder* vh, val v) { + vh->setVal(v); + } + + static val get_via_raw_pointer(const ValHolder* vh) { + return vh->getVal(); + } + + static void transfer_via_raw_pointer(ValHolder* target, const ValHolder* source) { + target->setVal(source->getVal()); + } + + static val getValNonMember(const ValHolder& target) { + return target.getVal(); + } + private: - val v; + val v_; }; ValHolder emval_test_return_ValHolder() { @@ -119,18 +168,556 @@ void emval_test_set_ValHolder_to_empty_object(ValHolder& vh) { class StringHolder { public: StringHolder(const std::string& s) - : str(s) + : str_(s) {} void set(const std::string& s) { - str = s; + str_ = s; } + std::string get() const { - return str; + return str_; + } + + std::string& get_ref() { + return str_; + } + + const std::string& get_const_ref() const { + return str_; } private: - std::string str; + std::string str_; +}; + +class SharedPtrHolder { +public: + SharedPtrHolder() + : ptr_(new StringHolder("a string")) + {} + + std::shared_ptr get() const { + return ptr_; + } + + void set(std::shared_ptr p) { + ptr_ = p; + } +private: + std::shared_ptr ptr_; +}; + +class VectorHolder { +public: + VectorHolder() { + v_.push_back(StringHolder("string #1")); + v_.push_back(StringHolder("string #2")); + } + + std::vector get() const { + return v_; + } + + void set(std::vector vec) { + v_ = vec; + } + +private: + std::vector v_; +}; + +class SmallClass { +public: + SmallClass(): member(7) {}; + int member; +}; + +class BigClass { +public: + BigClass(): member(11) {}; + int member; + int otherMember; + int yetAnotherMember; + + int getMember() { + return member; + } +}; + +class ParentClass { +public: + ParentClass(): bigClass() {}; + + BigClass bigClass; + + const BigClass& getBigClass() { + return bigClass; + }; +}; + +template +class TemplateClass { +public: + TemplateClass(T a, T b, T c) { + members[0] = a; + members[1] = b; + members[2] = c; + }; + + const T getMember(int n) { + return members[n]; + } + +protected: + T members[3]; +}; + +class ContainsTemplatedMemberClass { +public: + ContainsTemplatedMemberClass(): testTemplate(86, 87, 88) {}; + + TemplateClass testTemplate; + + const TemplateClass& getTestTemplate() { + return testTemplate; + }; +}; + +// Begin Inheritance Hierarchy Class Definitions + +class Base { +public: + Base(): name("Base"), + member(0), + baseMember(0) + {} + + std::string getClassName() const { + return name; + } + std::string getClassNameFromBase() const { + return name; + } + std::string getClassNameNotAvailableInDerivedClasses() { + // but wait -- if you act now we will throw in a SECOND base class method ABSOLUTELY FREE!! + return name; + } + void setMember(int value) { + member = value; + } + int getMember() { + return member; + } + void setBaseMember(int value) { + baseMember = value; + } + int getBaseMember() { + return baseMember; + } + std::string name; + int member; + int baseMember; +}; + +class SecondBase { +public: + SecondBase() + : name("SecondBase"), + member(0), + secondBaseMember(0) + {} + + std::string getClassName() const { + return name; + } + std::string getClassNameNotAvailableInDerivedClasses() { + return name; + } + std::string getClassNameFromSecondBase() const { + return name; + } + void setMember(int value) { + member = value; + } + int getMember() { + return member; + } + void setSecondBaseMember(int value) { + secondBaseMember = value; + } + int getSecondBaseMember() { + return secondBaseMember; + } + std::string name; + int member; + int secondBaseMember; +}; + +class Derived : public Base{ +public: + Derived() + : Base() + , member(0) + , name_("Derived") + {} + + std::string getClassName() const { + return name_; + } + void setMember(int value) { + member = value; + } + int getMember() { + return member; + } + int member; +private: + std::string name_; +}; + +class DerivedHolder { +public: + DerivedHolder() { + derived_.reset(); + } + void newDerived() { + deleteDerived(); + derived_ = std::shared_ptr(new Derived()); + } + void deleteDerived() { + derived_.reset(); + } + std::shared_ptr getDerived() { + return derived_; + } + std::string getDerivedClassName() { + return derived_->getClassName(); + } +private: + std::shared_ptr derived_; +}; + +class SiblingDerived : public Base { +public: + SiblingDerived() + : Base(), + name_("SiblingDerived") + {} + + std::string getClassName() const { + return name_; + } + +private: + std::string name_; +}; + +class MultiplyDerived : public Base, public SecondBase { +public: + MultiplyDerived() + : Base(), SecondBase(), + name_("MultiplyDerived") + { instanceCount_ ++; } + + ~MultiplyDerived() + { instanceCount_ --; } + + std::string getClassName() const { + return name_; + } + + static int getInstanceCount() { + return instanceCount_; + } +private: + std::string name_; + static int instanceCount_; +}; +int MultiplyDerived::instanceCount_ = 0; + +class DerivedTwice : public Derived { +public: + DerivedTwice() + : Derived(), + name_("DerivedTwice") + {} + + std::string getClassName() const { + return name_; + } +private: + std::string name_; +}; + +class DerivedTwiceNotBound : public Derived { +public: + DerivedTwiceNotBound() + : Derived(), + name_("DerivedTwiceNotBound") + {} + + std::string getClassName() const { + return name_; + } +private: + std::string name_; +}; + +class DerivedThrice: public DerivedTwiceNotBound { +public: + DerivedThrice() + : DerivedTwiceNotBound(), + name_("DerivedThrice") + {} + + std::string getClassName() const { + return name_; + } +private: + std::string name_; +}; + +class DerivedFourTimesNotBound: public DerivedThrice { +public: + DerivedFourTimesNotBound() + : DerivedThrice(), + name_("DerivedFourTimesNotBound") + {} + + std::string getClassName() const { + return name_; + } +private: + std::string name_; +}; + +class PolyBase { +public: + PolyBase(const std::string& s) + : str_(s), + name_("PolyBase") + {} + + PolyBase(): name_("PolyBase") {} + + virtual ~PolyBase() {} + + virtual std::string virtualGetClassName() const { + return name_; + } + + std::string getClassName() const { + return name_; + } + +private: + std::string str_; + std::string name_; +}; + +class PolySecondBase { +public: + PolySecondBase(): name_("PolySecondBase") + {} + + virtual ~PolySecondBase() {} + + std::string getClassName() const { + return name_; + } +private: + std::string name_; +}; + +class PolyDerived : public PolyBase{ +public: + PolyDerived() + : PolyBase("PolyDerived"), + name_("PolyDerived") + {} + + std::string virtualGetClassName() const { + return name_; + } + + std::string getClassName() const { + return name_; + } + + static void setPtrDerived() { + ptr_ = std::shared_ptr(new PolyDerived()); + } + + static void releasePtr() { + ptr_.reset(); + } + + static std::string getPtrClassName() { + return ptr_->getClassName(); + } + + static std::shared_ptr getPtr() { + return ptr_; + } + +private: + std::string name_; + static std::shared_ptr ptr_; +}; +std::shared_ptr PolyDerived::ptr_; + +class PolySiblingDerived : public PolyBase { +public: + PolySiblingDerived() + : PolyBase(), + name_("PolySiblingDerived") + {} + + std::string getClassName() const { + return name_; + } +private: + std::string name_; +}; + +class PolyMultiplyDerived : public PolyBase, public PolySecondBase { +public: + PolyMultiplyDerived() + : PolyBase(), PolySecondBase(), + name_("PolyMultiplyDerived") + {} + + std::string getClassName() const { + return name_; + } +private: + std::string name_; +}; + +class PolyDerivedTwiceWithoutSmartPointer: public PolyDerived { +public: + PolyDerivedTwiceWithoutSmartPointer() + : PolyDerived(), + name_("PolyDerivedTwiceWithoutSmartPointer") + {} + + std::string getClassName() const { + return name_; + } +private: + std::string name_; +}; + +class PolyDerivedTwiceNotBound : public PolyDerived { +public: + PolyDerivedTwiceNotBound() + : PolyDerived(), + name_("PolyDerivedTwiceNotBound") + {} + + std::string getClassName() const { + return name_; + } +private: + std::string name_; +}; + +class PolyDerivedThrice: public PolyDerivedTwiceNotBound { +public: + PolyDerivedThrice() + : PolyDerivedTwiceNotBound(), + name_("PolyDerivedThrice") + {} + + std::string getClassName() const { + return name_; + } +private: + std::string name_; +}; + +class PolyDerivedFourTimesNotBound: public PolyDerivedThrice { +public: + PolyDerivedFourTimesNotBound() + : PolyDerivedThrice(), + name_("PolyDerivedFourTimesNotBound") + {} + + std::string getClassName() const { + return name_; + } +private: + std::string name_; +}; + +// todo: does it need to be polymorphic? +// todo: virtual diamond pattern +class PolyDiamondBase { +public: + PolyDiamondBase(): + name_("PolyBase") + {} + ~PolyDiamondBase() {} + + std::string getClassName() const { + return name_; + } +private: + std::string name_; +}; + +class PolyDiamondDerived: public PolyDiamondBase { +public: + PolyDiamondDerived() + : PolyDiamondBase(), + name_("PolyDiamondDerived") + {} + + std::string getClassName() const { + return name_; + } +private: + std::string name_; +}; + +class PolyDiamondSiblingDerived: public PolyDiamondBase { +public: + PolyDiamondSiblingDerived() + : PolyDiamondBase(), + name_("PolyDiamondSiblingDerived") + {} + + std::string getClassName() const { + return name_; + } +private: + std::string name_; +}; + +class PolyDiamondMultiplyDerived: public PolyDiamondDerived, public PolyDiamondSiblingDerived { +public: + PolyDiamondMultiplyDerived() + : PolyDiamondDerived(), PolyDiamondSiblingDerived(), + name_("PolyDiamondMultiplyDerived") + {} + + std::string getClassName() const { + return name_; + } +private: + std::string name_; +}; + +// End Inheritance Hierarchy Class Definitions + +std::map embind_test_get_string_int_map() { + std::map m; + + m["one"] = 1; + m["two"] = 2; + + return m; }; struct TupleVector { @@ -187,6 +774,11 @@ struct CustomStruct { CustomStruct() : field(10) {} + + const int& getField() const { + return field; + } + int field; }; @@ -210,52 +802,521 @@ 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); } -void optional_test_copy() { - using emscripten::internal::optional; - - optional foo = 22; - optional bar(foo); - - return bool(bar); +std::unique_ptr emval_test_return_unique_ptr() { + return std::unique_ptr(new ValHolder(val::object())); } -void optional_test_copy2() { - using emscripten::internal::optional; - - optional foo; - optional bar(foo); - - return bool(bar); +std::shared_ptr emval_test_return_shared_ptr() { + return std::shared_ptr(new ValHolder(val::object())); } -EMSCRIPTEN_BINDINGS(([]() { +std::shared_ptr emval_test_return_empty_shared_ptr() { + return std::shared_ptr(); +} + +bool emval_test_is_shared_ptr_null(std::shared_ptr p) { + return !p; +} + +static SmallClass smallClass; +static BigClass bigClass; + +SmallClass embind_test_return_small_class_instance() { + return smallClass; +} + +BigClass embind_test_return_big_class_instance() { + return bigClass; +} + +int embind_test_accept_small_class_instance(SmallClass c) { + return c.member; +} + +int embind_test_accept_big_class_instance(BigClass c) { + return c.member; +} + +// Begin Inheritance Hierarchy Test Wrappers + +Base* embind_test_return_raw_base_ptr() { + return new Base(); +} + +Base* embind_test_return_raw_derived_ptr_as_base() { + return new Derived(); +} + +Base* embind_test_return_raw_sibling_derived_ptr_as_base() { + return new SiblingDerived(); +} + +PolyBase* embind_test_return_raw_polymorphic_derived_ptr_as_base() { + return new PolyDerived(); +} + +PolyBase* embind_test_return_raw_polymorphic_sibling_derived_ptr_as_base() { + return new PolySiblingDerived(); +} + +PolyBase* embind_test_return_raw_polymorphic_multiply_derived_ptr_as_base() { + return new PolyMultiplyDerived(); +} + +PolySecondBase* embind_test_return_raw_polymorphic_multiply_derived_ptr_as_second_base() { + return new PolyMultiplyDerived(); +} + +PolyBase* embind_test_return_raw_polymorphic_derived_four_times_not_bound_as_base() { + return new PolyDerivedFourTimesNotBound(); +} + +std::shared_ptr embind_test_return_smart_base_ptr() { + return std::shared_ptr(new Base()); +} + +std::shared_ptr embind_test_return_smart_polymorphic_base_ptr() { + return std::shared_ptr(new PolyBase("PolyBase")); +} + +std::shared_ptr embind_test_return_smart_derived_ptr() { + return std::shared_ptr(new Derived()); +} + +std::shared_ptr embind_test_return_smart_sibling_derived_ptr() { + return std::shared_ptr(new SiblingDerived()); +} + +std::shared_ptr embind_test_return_smart_multiply_derived_ptr() { + return std::shared_ptr(new MultiplyDerived()); +} + +std::shared_ptr embind_test_return_smart_derived_thrice_ptr() { + return std::shared_ptr(new DerivedThrice()); +} + +std::shared_ptr embind_test_return_smart_polymorphic_derived_ptr() { + return std::shared_ptr(new PolyDerived()); +} + +std::shared_ptr embind_test_return_smart_polymorphic_sibling_derived_ptr() { + return std::shared_ptr(new PolySiblingDerived()); +} + +std::shared_ptr embind_test_return_smart_polymorphic_multiply_derived_ptr() { + return std::shared_ptr(new PolyMultiplyDerived()); +} + +std::shared_ptr embind_test_return_poly_derived_twice_without_smart_pointer_as_poly_base() { + return std::shared_ptr(new PolyDerivedTwiceWithoutSmartPointer()); +} + +std::shared_ptr embind_test_return_smart_poly_derived_thrice_ptr() { + return std::shared_ptr(new PolyDerivedThrice()); +} + +std::shared_ptr embind_test_return_smart_derived_ptr_as_base() { + return std::shared_ptr(new PolyDerived()); +} + +val embind_test_return_smart_derived_ptr_as_val() { + return val(std::shared_ptr(new PolyDerived())); +} + +std::shared_ptr embind_test_return_smart_polymorphic_derived_ptr_as_base() { + return std::shared_ptr(new PolyDerived()); +} + +std::shared_ptr embind_test_return_smart_polymorphic_sibling_derived_ptr_as_base() { + return std::shared_ptr(new PolySiblingDerived()); +} + +std::string embind_test_get_class_name_via_base_ptr(Base *p) { + return p->getClassName(); +} + +std::string embind_test_get_class_name_via_second_base_ptr(SecondBase *p) { + return p->getClassName(); +} + +std::string embind_test_get_class_name_via_polymorphic_base_ptr(PolyBase *p) { + return p->getClassName(); +} + +std::string embind_test_get_class_name_via_polymorphic_second_base_ptr(PolySecondBase *p) { + return p->getClassName(); +} + +std::string embind_test_get_class_name_via_smart_base_ptr(std::shared_ptr p) { + return p->getClassName(); +} + +std::string embind_test_get_class_name_via_reference_to_smart_base_ptr(std::shared_ptr& p) { + return p->getClassName(); +} + +std::string embind_test_get_class_name_via_smart_second_base_ptr(std::shared_ptr p) { + return p->getClassName(); +} + +std::string embind_test_get_class_name_via_smart_polymorphic_base_ptr(std::shared_ptr p) { + return p->getClassName(); +} + +std::string embind_test_get_virtual_class_name_via_smart_polymorphic_base_ptr(std::shared_ptr p) { + return p->virtualGetClassName(); +} + +std::string embind_test_get_class_name_via_smart_polymorphic_second_base_ptr(std::shared_ptr p) { + return p->getClassName(); +} + +void embind_modify_smart_pointer_passed_by_reference(std::shared_ptr& p) { + p = std::shared_ptr(new Base()); + p->name = "Changed"; +} + +void embind_attempt_to_modify_smart_pointer_when_passed_by_value(std::shared_ptr p) { + p = std::shared_ptr(new Base()); + p->name = "Changed"; +} + +static std::shared_ptr savedBasePointer; + +void embind_save_smart_base_pointer(std::shared_ptr p) { + savedBasePointer = p; +} + +// End Inheritance Hierarchy Test Wrappers + +std::vector emval_test_return_vector() { + int myints[] = { 10, 20, 30 }; + return std::vector(myints, myints + sizeof(myints) / sizeof(int)); +} + +std::vector > emval_test_return_vector_of_vectors() { + int myints1[] = { 10, 20, 30 }; + int myints2[] = { 40, 50, 60 }; + std::vector vec1(myints1, myints1 + sizeof(myints1) / sizeof(int)); + std::vector vec2(myints2, myints2 + sizeof(myints2) / sizeof(int)); + std::vector>vec3; + vec3.emplace_back(vec1); + vec3.emplace_back(vec2); + return vec3; +} + +std::vector> emval_test_return_shared_ptr_vector() { + std::vector> sharedStrVector; + sharedStrVector.push_back(std::shared_ptr(new StringHolder("string #1"))); + sharedStrVector.push_back(std::shared_ptr(new StringHolder("string #2"))); + + return sharedStrVector; +} + +class JSInterfaceHolder { +public: + JSInterfaceHolder(JSInterface &jsobj) : jsobj_(jsobj) { + ptr_ = JSInterface::cloneToSharedPtr(jsobj_); + } + + int callMethod(std::string method) { return jsobj_.call(method.c_str()); } + int callMethodUsingSharedPtr(std::string method) { return ptr_->call(method.c_str()); } + +private: + JSInterface jsobj_; + std::shared_ptr ptr_; +}; + +void test_string_with_vec(const std::string& p1, std::vector& v1) { + // THIS DOES NOT WORK -- need to get as val and then call vecFromJSArray + printf("%s\n", p1.c_str()); +} + +val embind_test_new_Object() { + return val::global("Object").new_(); +} + +val embind_test_new_factory(val factory, val argument) { + return factory.new_(10, std::string("hello"), argument); +} + +class AbstractClass { +public: + virtual ~AbstractClass() {} + virtual std::string abstractMethod() const = 0; +}; + +class AbstractClassWrapper : public wrapper { +public: + EMSCRIPTEN_WRAPPER(AbstractClassWrapper); + + std::string abstractMethod() const { + return call("abstractMethod"); + } +}; + +class ConcreteClass : public AbstractClass { + std::string abstractMethod() const { + return "from concrete"; + } +}; + +std::shared_ptr getAbstractClass() { + return std::make_shared(); +} + +std::string callAbstractMethod(AbstractClass& ac) { + return ac.abstractMethod(); +} + +class HasExternalConstructor { +public: + HasExternalConstructor(const std::string& str) + : m(str) + {} + + std::string getString() const { + return m; + } + + std::string m; +}; + +HasExternalConstructor* createHasExternalConstructor(const std::string& str) { + return new HasExternalConstructor(str); +} + +template +class CustomSmartPtr { +public: + CustomSmartPtr() + : CustomSmartPtr(nullptr) + { + std::fill(d_, d_ + N_, Valid); + } + + explicit CustomSmartPtr(T* t) + : ptr_(t) + { + std::fill(d_, d_ + N_, Valid); + } + + CustomSmartPtr(const CustomSmartPtr& other) + : ptr_(other.ptr_) + { + other.verify(); + std::fill(d_, d_ + N_, Valid); + if (ptr_) { + ++(ptr_->refcount); + } + } + + ~CustomSmartPtr() { + verify(); + std::fill(d_, d_ + N_, Deleted); + + if (ptr_ && --ptr_->refcount == 0) { + delete ptr_; + } + } + + T* get_raw() const { + return ptr_; + } + +private: + void verify() const { + for (size_t i = 0; i < N_; ++i) { + if (d_[i] != Valid) { + abort(); + } + } + } + + enum { + Valid = 255, + Deleted = 127, + }; + static constexpr size_t N_ = 1000000; + unsigned char d_[N_]; + T* ptr_; + + CustomSmartPtr& operator=(const CustomSmartPtr&) = delete; +}; + +class HeldBySmartPtr { +public: + HeldBySmartPtr(int i, const std::string& s) + : i(i) + , s(s) + {} + + static CustomSmartPtr newCustomPtr(int i, const std::string& s) { + return CustomSmartPtr(new HeldBySmartPtr(i, s)); + } + + int refcount = 1; + int i; + std::string s; +}; + +HeldBySmartPtr takesHeldBySmartPtr(HeldBySmartPtr p) { + return p; +} +std::shared_ptr takesHeldBySmartPtrSharedPtr(std::shared_ptr p) { + return p; +} + +namespace emscripten { + template + struct smart_ptr_trait> { + typedef T element_type; + + static sharing_policy get_sharing_policy() { + return sharing_policy::NONE; + } + + static T* get(const CustomSmartPtr& p) { + return p.get_raw(); + } + + static CustomSmartPtr share(const CustomSmartPtr& r, T* ptr) { + ++ptr->refcount; // implement an adopt API? + return CustomSmartPtr(ptr); + } + }; +} + +typedef CustomSmartPtr HeldByCustomSmartPtrPtr; + +class HeldByCustomSmartPtr { +public: + HeldByCustomSmartPtr(int i, const std::string& s) + : i(i) + , s(s) + {} + + static HeldByCustomSmartPtrPtr create(int i, const std::string& s) { + return HeldByCustomSmartPtrPtr(new HeldByCustomSmartPtr(i, s)); + } + + static std::shared_ptr createSharedPtr(int i, const std::string& s) { + return std::make_shared(i, s); + }; + + int refcount = 1; + int i; + std::string s; +}; + +HeldByCustomSmartPtr* passThroughRawPtr(HeldByCustomSmartPtr* p) { + return p; +} +HeldByCustomSmartPtrPtr passThroughCustomSmartPtr(HeldByCustomSmartPtrPtr p) { + return p; +} + +struct Base1 { +public: + Base1(): field1("Base1") {} + std::string field1; + + std::string getField() const { + return field1; + } +}; + +struct Base2 { +public: + Base2(): field2("Base2") {} + std::string field2; + + std::string getField() const { + return field2; + } +}; + +struct HasTwoBases : public Base1, public Base2 { +}; + +val get_module_property(const std::string& s) { + return val::module_property(s.c_str()); +} + +std::string char_to_string(char ch) { + char str[256]; + sprintf(str, "%d", (int)ch); + return str; +} + +std::string signed_char_to_string(signed char ch) { + char str[256]; + sprintf(str, "%hhd", ch); + return str; +} + +std::string unsigned_char_to_string(unsigned char ch) { + char str[256]; + sprintf(str, "%hhu", ch); + return str; +} + +std::string short_to_string(short val) { + char str[256]; + sprintf(str, "%hd", val); + return str; +} + +std::string unsigned_short_to_string(unsigned short val) { + char str[256]; + sprintf(str, "%hu", val); + return str; +} + +std::string int_to_string(int val) { + char str[256]; + sprintf(str, "%d", val); + return str; +} + +std::string unsigned_int_to_string(unsigned int val) { + char str[256]; + sprintf(str, "%u", val); + return str; +} + +std::string long_to_string(long val) { + char str[256]; + sprintf(str, "%ld", val); + return str; +} + +std::string unsigned_long_to_string(unsigned long val) { + char str[256]; + sprintf(str, "%lu", val); + return str; +} + +EMSCRIPTEN_BINDINGS(tests) { + register_js_interface(); + + register_vector("IntegerVector"); + register_vector("CharVector"); + register_vector("VectorUnsigned"); + register_vector("VectorUnsignedChar"); + register_vector("StringVector"); + register_vector("EmValVector"); + register_vector("FloatVector"); + register_vector>("IntegerVectorVector"); + function("mallinfo", &emval_test_mallinfo); - function("emval_test_new_integer", &emval_test_new_integer); function("emval_test_new_string", &emval_test_new_string); + function("emval_test_get_string_from_val", &emval_test_get_string_from_val); function("emval_test_new_object", &emval_test_new_object); function("emval_test_passthrough_unsigned", &emval_test_passthrough_unsigned); function("emval_test_passthrough", &emval_test_passthrough); @@ -265,6 +1326,7 @@ EMSCRIPTEN_BINDINGS(([]() { function("emval_test_as_unsigned", &emval_test_as_unsigned); function("emval_test_get_length", &emval_test_get_length); function("emval_test_add", &emval_test_add); + function("const_ref_adder", &const_ref_adder); function("emval_test_sum", &emval_test_sum); //function("emval_test_take_and_return_const_char_star", &emval_test_take_and_return_const_char_star); @@ -305,24 +1367,289 @@ EMSCRIPTEN_BINDINGS(([]() { function("emval_test_take_and_return_TupleInStruct", &emval_test_take_and_return_TupleInStruct); class_("ValHolder") + .smart_ptr>() .constructor() - .method("getVal", &ValHolder::getVal) - .method("setVal", &ValHolder::setVal) - .method("returnIntPlusFive", &ValHolder::returnIntPlusFive) - .classmethod("some_class_method", &ValHolder::some_class_method) + .function("getVal", &ValHolder::getVal) + .function("getValNonConst", &ValHolder::getValNonConst) + .function("getConstVal", &ValHolder::getConstVal) + .function("getValConstRef", &ValHolder::getValConstRef) + .function("setVal", &ValHolder::setVal) + .class_function("makeConst", &ValHolder::makeConst, allow_raw_pointer()) + .class_function("makeValHolder", &ValHolder::makeValHolder) + .class_function("some_class_method", &ValHolder::some_class_method) + .class_function("set_via_raw_pointer", + &ValHolder::set_via_raw_pointer, + allow_raw_pointer>()) + .class_function("get_via_raw_pointer", + &ValHolder::get_via_raw_pointer, + allow_raw_pointer>()) + .class_function("transfer_via_raw_pointer", + &ValHolder::transfer_via_raw_pointer, + allow_raw_pointers()) + + // non-member method + .function("setEmpty", &emval_test_set_ValHolder_to_empty_object) + .function("getValNonMember", &ValHolder::getValNonMember) ; + function("emval_test_return_ValHolder", &emval_test_return_ValHolder); function("emval_test_set_ValHolder_to_empty_object", &emval_test_set_ValHolder_to_empty_object); + class_>("StringFunctorString") + .constructor<>() + .calloperator("opcall") + ; + + function("emval_test_get_function_ptr", &emval_test_get_function_ptr); + function("emval_test_take_and_call_functor", &emval_test_take_and_call_functor); + class_("StringHolder") + .smart_ptr>() .constructor() - .method("set", &StringHolder::set) - .method("get", &StringHolder::get) + .function("set", &StringHolder::set) + .function("get", &StringHolder::get) + .function("get_const_ref", &StringHolder::get_const_ref) + ; + + class_("SharedPtrHolder") + .constructor<>() + .function("get", &SharedPtrHolder::get) + .function("set", &SharedPtrHolder::set) + ; + + class_("SmallClass") + .constructor<>() + .property("member", &SmallClass::member) + ; + + class_("BigClass") + .constructor<>() + .property("member", &BigClass::member) + .property("otherMember", &BigClass::otherMember) + .property("yetAnotherMember", &BigClass::yetAnotherMember) + .function("getMember", &BigClass::getMember) + ; + + class_("ParentClass") + .constructor<>() + .function("getBigClass", &ParentClass::getBigClass) + ; + + class_>("IntTemplateClass") + .constructor() + .function("getMember", &TemplateClass::getMember) + ; + + class_("ContainsTemplatedMemberClass") + .constructor<>() + .function("getTestTemplate", &ContainsTemplatedMemberClass::getTestTemplate) + ; + + // register Derived before Base as a test that it's possible to + // register base classes afterwards + class_>("Derived") + .smart_ptr>() + .constructor<>() + .function("getClassName", &Derived::getClassName) + .function("getMember", &Derived::getMember) + .function("setMember", &Derived::setMember) + .property("member", &Derived::member) + ; + + class_("Base") + .smart_ptr>() + .constructor<>() + .function("getClassName", &Base::getClassName) + .function("getClassNameFromBase", &Base::getClassNameFromBase) + .function("getClassNameNotAvailableInDerivedClasses", &Base::getClassNameNotAvailableInDerivedClasses) + .function("getMember", &Base::getMember) + .function("setMember", &Base::setMember) + .function("getBaseMember", &Base::getBaseMember) + .function("setBaseMember", &Base::setBaseMember) + .property("member", &Base::member) + .property("baseMember", &Base::baseMember) + ; + + class_("SecondBase") + .smart_ptr>() + .constructor<>() + .function("getClassName", &SecondBase::getClassName) + .function("getClassNameFromSecondBase", &SecondBase::getClassNameFromSecondBase) + .function("getClassNameNotAvailableInDerivedClasses", &SecondBase::getClassNameNotAvailableInDerivedClasses) + .function("getMember", &SecondBase::getMember) + .function("setMember", &SecondBase::setMember) + .function("getSecondBaseMember", &SecondBase::getSecondBaseMember) + .function("setSecondBaseMember", &SecondBase::setSecondBaseMember) + .property("member", &SecondBase::member) + .property("secondBaseMember", &SecondBase::secondBaseMember) + ; + + + class_("DerivedHolder") + .constructor<>() + .function("newDerived", &DerivedHolder::newDerived) + .function("deleteDerived", &DerivedHolder::deleteDerived) + .function("getDerived", &DerivedHolder::getDerived) + .function("getDerivedClassName", &DerivedHolder::getDerivedClassName) + ; + + class_("SiblingDerived") + .smart_ptr>() + .constructor<>() + .function("getClassName", &SiblingDerived::getClassName) + ; + + class_>("MultiplyDerived") + .smart_ptr>() + .constructor<>() + .function("getClassName", &MultiplyDerived::getClassName) + .class_function("getInstanceCount", &MultiplyDerived::getInstanceCount) + ; + + class_ >("DerivedTwice") + .constructor<>() + .function("getClassName", &DerivedTwice::getClassName) + ; + + class_ >("DerivedThrice") + .smart_ptr>() + .constructor<>() + .function("getClassName", &DerivedThrice::getClassName) + ; + + class_("PolyBase") + .smart_ptr>() + .constructor<>() + .function("virtualGetClassName", &PolyBase::virtualGetClassName) + .function("getClassName", &PolyBase::getClassName) + ; + + class_("PolySecondBase") + .smart_ptr>() + .constructor<>() + .function("getClassName", &PolySecondBase::getClassName) + ; + + class_>("PolyDerived") + .smart_ptr>() + .constructor<>() + .function("virtualGetClassName", &PolyDerived::virtualGetClassName) + .function("getClassName", &PolyDerived::getClassName) + .class_function("setPtrDerived", &PolyDerived::setPtrDerived) + .class_function("releasePtr", &PolyDerived::releasePtr) + .class_function("getPtrClassName", &PolyDerived::getPtrClassName) + .class_function("getPtr", &PolyDerived::getPtr) + ; +// static void setPtrDerived() { +// ptr = std::shared_ptr(new PolyDerived()); +// } +// +// static std::string getPtrClassName() { +// return ptr->getClassName(); +// } +// +// static std::shared_ptr getPtr() { +// return ptr; +// } + + class_>("PolySiblingDerived") + .smart_ptr>() + .constructor<>() + .function("getClassName", &PolySiblingDerived::getClassName) + ; + + class_>("PolyMultiplyDerived") + .smart_ptr>() + .constructor<>() + .function("getClassName", &PolyMultiplyDerived::getClassName) + ; + + class_>("PolyDerivedThrice") + .smart_ptr>() + .constructor<>() + .function("getClassName", &PolyDerivedThrice::getClassName) + ; + + class_("PolyDiamondBase") + .smart_ptr>() + .constructor<>() + .function("getClassName", &PolyDiamondBase::getClassName) + ; + + class_("PolyDiamondDerived") + .smart_ptr>() + .constructor<>() + .function("getClassName", &PolyDiamondDerived::getClassName) + ; + + class_("PolyDiamondSiblingDerived") + .smart_ptr>() + .constructor<>() + .function("getClassName", &PolyDiamondSiblingDerived::getClassName) + ; + + class_("PolyDiamondMultiplyDerived") + .smart_ptr>() + .constructor<>() + .function("getClassName", &PolyDiamondMultiplyDerived::getClassName) + ; + + function("embind_test_return_small_class_instance", &embind_test_return_small_class_instance); + function("embind_test_return_big_class_instance", &embind_test_return_big_class_instance); + function("embind_test_accept_small_class_instance", &embind_test_accept_small_class_instance); + function("embind_test_accept_big_class_instance", &embind_test_accept_big_class_instance); + + function("embind_test_return_raw_base_ptr", embind_test_return_raw_base_ptr, allow_raw_pointer()); + function("embind_test_return_raw_derived_ptr_as_base", embind_test_return_raw_derived_ptr_as_base, allow_raw_pointer()); + function("embind_test_return_raw_sibling_derived_ptr_as_base", embind_test_return_raw_sibling_derived_ptr_as_base, allow_raw_pointer()); + function("embind_test_return_raw_polymorphic_derived_ptr_as_base", embind_test_return_raw_polymorphic_derived_ptr_as_base, allow_raw_pointer()); + function("embind_test_return_raw_polymorphic_sibling_derived_ptr_as_base", embind_test_return_raw_polymorphic_sibling_derived_ptr_as_base, allow_raw_pointer()); + function("embind_test_return_raw_polymorphic_multiply_derived_ptr_as_base", embind_test_return_raw_polymorphic_multiply_derived_ptr_as_base, allow_raw_pointer()); + function("embind_test_return_raw_polymorphic_multiply_derived_ptr_as_second_base", embind_test_return_raw_polymorphic_multiply_derived_ptr_as_second_base, allow_raw_pointer()); + function("embind_test_return_raw_polymorphic_derived_four_times_not_bound_as_base", embind_test_return_raw_polymorphic_derived_four_times_not_bound_as_base, allow_raw_pointer()); + function("embind_test_return_smart_derived_ptr", embind_test_return_smart_derived_ptr); + function("embind_test_return_smart_sibling_derived_ptr", embind_test_return_smart_sibling_derived_ptr); + function("embind_test_return_smart_multiply_derived_ptr", embind_test_return_smart_multiply_derived_ptr); + function("embind_test_return_smart_derived_thrice_ptr", embind_test_return_smart_derived_thrice_ptr); + function("embind_test_return_smart_base_ptr", embind_test_return_smart_base_ptr); + function("embind_test_return_smart_polymorphic_base_ptr", embind_test_return_smart_polymorphic_base_ptr); + function("embind_test_return_smart_polymorphic_derived_ptr", embind_test_return_smart_polymorphic_derived_ptr); + function("embind_test_return_smart_polymorphic_sibling_derived_ptr", embind_test_return_smart_polymorphic_sibling_derived_ptr); + function("embind_test_return_smart_polymorphic_multiply_derived_ptr", embind_test_return_smart_polymorphic_multiply_derived_ptr); + function("embind_test_return_poly_derived_twice_without_smart_pointer_as_poly_base", embind_test_return_poly_derived_twice_without_smart_pointer_as_poly_base); + function("embind_test_return_smart_poly_derived_thrice_ptr", embind_test_return_smart_poly_derived_thrice_ptr); + function("embind_test_return_smart_derived_ptr_as_base", embind_test_return_smart_derived_ptr_as_base); + function("embind_test_return_smart_derived_ptr_as_val", embind_test_return_smart_derived_ptr_as_val); + function("embind_test_return_smart_polymorphic_derived_ptr_as_base", embind_test_return_smart_polymorphic_derived_ptr_as_base); + function("embind_test_return_smart_polymorphic_sibling_derived_ptr_as_base", embind_test_return_smart_polymorphic_sibling_derived_ptr_as_base); + function("embind_test_get_class_name_via_base_ptr", embind_test_get_class_name_via_base_ptr, allow_raw_pointer>()); + function("embind_test_get_class_name_via_second_base_ptr", embind_test_get_class_name_via_second_base_ptr, allow_raw_pointer>()); + function("embind_test_get_class_name_via_polymorphic_base_ptr", embind_test_get_class_name_via_polymorphic_base_ptr, allow_raw_pointer>()); + function("embind_test_get_class_name_via_polymorphic_second_base_ptr", embind_test_get_class_name_via_polymorphic_second_base_ptr, allow_raw_pointer>()); + // todo: allow_raw_pointer should fail earlier if argument is not a pointer + function("embind_test_get_class_name_via_smart_base_ptr", embind_test_get_class_name_via_smart_base_ptr); + function("embind_test_get_class_name_via_reference_to_smart_base_ptr", embind_test_get_class_name_via_reference_to_smart_base_ptr); + function("embind_test_get_class_name_via_smart_second_base_ptr", embind_test_get_class_name_via_smart_second_base_ptr); + function("embind_test_get_class_name_via_smart_polymorphic_base_ptr", embind_test_get_class_name_via_smart_polymorphic_base_ptr); + function("embind_test_get_virtual_class_name_via_smart_polymorphic_base_ptr", embind_test_get_virtual_class_name_via_smart_polymorphic_base_ptr); + function("embind_test_get_class_name_via_smart_polymorphic_second_base_ptr", embind_test_get_class_name_via_smart_polymorphic_second_base_ptr); + function("embind_modify_smart_pointer_passed_by_reference", embind_modify_smart_pointer_passed_by_reference); + function("embind_attempt_to_modify_smart_pointer_when_passed_by_value", embind_attempt_to_modify_smart_pointer_when_passed_by_value); + function("embind_save_smart_base_pointer", embind_save_smart_base_pointer); + + class_("Base2") + .function("getField", &Base2::getField) + .property("field", &Base2::field2) + ; + + class_>("HasTwoBases") + .constructor() ; class_("CustomStruct") .constructor<>() - .field("field", &CustomStruct::field) + .property("field", &CustomStruct::field) + .function("getField", &CustomStruct::getField) ; enum_("Enum") @@ -337,25 +1664,221 @@ EMSCRIPTEN_BINDINGS(([]() { ; function("emval_test_take_and_return_EnumClass", &emval_test_take_and_return_EnumClass); - class InterfaceWrapper : public wrapper { - int method() { - return call("method"); - } - TupleInStruct method2(const TupleInStruct& arg1, float arg2) { - return call("method2", arg1, arg2); - } - void method3() { - return call("method3"); - } - }; - interface("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); - function('optional_test_copy', &optional_test_copy); - function('optional_test_copy2', &optional_test_copy2); -})); + function("emval_test_return_unique_ptr", &emval_test_return_unique_ptr); + + function("emval_test_return_shared_ptr", &emval_test_return_shared_ptr); + function("emval_test_return_empty_shared_ptr", &emval_test_return_empty_shared_ptr); + function("emval_test_is_shared_ptr_null", &emval_test_is_shared_ptr_null); + + function("emval_test_return_vector", &emval_test_return_vector); + function("emval_test_return_vector_of_vectors", &emval_test_return_vector_of_vectors); + + register_vector>("SharedPtrVector"); + function("emval_test_return_shared_ptr_vector", &emval_test_return_shared_ptr_vector); + + function("get_module_property", &get_module_property); + + register_vector("StringHolderVector"); + class_("VectorHolder") + .constructor<>() + .function("get", &VectorHolder::get) + .function("set", &VectorHolder::set) + ; + + function("test_string_with_vec", &test_string_with_vec); + + register_map("StringIntMap"); + function("embind_test_get_string_int_map", embind_test_get_string_int_map); + + class_("JSInterfaceHolder") + .constructor() + .function("callMethod", &JSInterfaceHolder::callMethod) + .function("callMethodUsingSharedPtr", &JSInterfaceHolder::callMethodUsingSharedPtr) + ; + + function("embind_test_new_Object", &embind_test_new_Object); + function("embind_test_new_factory", &embind_test_new_factory); + + class_("AbstractClass") + .smart_ptr>() + .allow_subclass() + .function("abstractMethod", &AbstractClass::abstractMethod) + ; + + function("getAbstractClass", &getAbstractClass); + function("callAbstractMethod", &callAbstractMethod); + + class_("HasExternalConstructor") + .constructor(&createHasExternalConstructor) + .function("getString", &HasExternalConstructor::getString) + ; + + auto HeldBySmartPtr_class = class_("HeldBySmartPtr"); + HeldBySmartPtr_class + .smart_ptr>() + .smart_ptr_constructor(&std::make_shared) + .class_function("newCustomPtr", HeldBySmartPtr::newCustomPtr) + .function("returnThis", &takesHeldBySmartPtrSharedPtr) + .property("i", &HeldBySmartPtr::i) + .property("s", &HeldBySmartPtr::s) + ; + function("takesHeldBySmartPtr", &takesHeldBySmartPtr); + function("takesHeldBySmartPtrSharedPtr", &takesHeldBySmartPtrSharedPtr); + + class_("HeldByCustomSmartPtr") + .smart_ptr>() + .smart_ptr_constructor(&HeldByCustomSmartPtr::create) + .class_function("createSharedPtr", &HeldByCustomSmartPtr::createSharedPtr) + .property("i", &HeldByCustomSmartPtr::i) + .property("s", &HeldByCustomSmartPtr::s) + ; + + function("passThroughRawPtr", &passThroughRawPtr, allow_raw_pointers()); + function("passThroughCustomSmartPtr", &passThroughCustomSmartPtr); + + function("char_to_string", &char_to_string); + function("signed_char_to_string", &signed_char_to_string); + function("unsigned_char_to_string", &unsigned_char_to_string); + function("short_to_string", &short_to_string); + function("unsigned_short_to_string", &unsigned_short_to_string); + function("int_to_string", &int_to_string); + function("unsigned_int_to_string", &unsigned_int_to_string); + function("long_to_string", &long_to_string); + function("unsigned_long_to_string", &unsigned_long_to_string); +} + +// tests for out-of-order registration + +class SecondElement { +}; + +class FirstElement { +}; + +struct OrderedTuple { + FirstElement first; + SecondElement second; +}; + +struct OrderedStruct { + FirstElement first; + SecondElement second; +}; + +OrderedTuple getOrderedTuple() { + return OrderedTuple(); +} + +OrderedStruct getOrderedStruct() { + return OrderedStruct(); +} + +EMSCRIPTEN_BINDINGS(order) { + value_tuple("OrderedTuple") + .element(&OrderedTuple::first) + .element(&OrderedTuple::second) + ; + + value_struct("OrderedStruct") + .field("first", &OrderedStruct::first) + .field("second", &OrderedStruct::second) + ; + + class_("SecondElement") + ; + + class_("FirstElement") + ; + + function("getOrderedTuple", &getOrderedTuple); + function("getOrderedStruct", &getOrderedStruct); +} + +// tests for unbound types + +template +T passThrough(T t) { + return t; +} + +struct UnboundClass { +}; + +struct HasUnboundBase : public UnboundClass { + static void noop() { + } +}; + +HasUnboundBase getHasUnboundBase(HasUnboundBase f) { + return f; +} + +struct HasConstructorUsingUnboundArgument { + HasConstructorUsingUnboundArgument(UnboundClass) { + } +}; + +struct SecondUnboundClass { +}; + +struct HasConstructorUsingUnboundArgumentAndUnboundBase : public SecondUnboundClass { + HasConstructorUsingUnboundArgumentAndUnboundBase(UnboundClass) { + } +}; + +struct BoundClass { + UnboundClass method(UnboundClass t) { + return t; + } + + static UnboundClass classfunction(UnboundClass t) { + return t; + } + + UnboundClass property; +}; + +EMSCRIPTEN_BINDINGS(incomplete) { + function("getUnboundClass", &passThrough); + + class_>("HasUnboundBase") + .class_function("noop", &HasUnboundBase::noop) + ; + function("getHasUnboundBase", &passThrough); + + class_("HasConstructorUsingUnboundArgument") + .constructor() + ; + + class_>("HasConstructorUsingUnboundArgumentAndUnboundBase") + .constructor() + ; + + class_("BoundClass") + .constructor<>() + .function("method", &BoundClass::method) + .class_function("classfunction", &BoundClass::classfunction) + .property("property", &BoundClass::property) + ; +} + +class Noncopyable { + Noncopyable(const Noncopyable&) = delete; + Noncopyable& operator=(const Noncopyable&) = delete; + +public: + Noncopyable() {} + + std::string method() const { + return "foo"; + } +}; + +EMSCRIPTEN_BINDINGS(noncopyable) { + class_("Noncopyable") + .constructor<>() + .function("method", &Noncopyable::method) + ; +} diff --git a/tests/embind/embind_test.js b/tests/embind/embind_test.js deleted file mode 100644 index 9ba6a48f5..000000000 --- a/tests/embind/embind_test.js +++ /dev/null @@ -1,398 +0,0 @@ -//=== testing glue - -function module(ignore, func) { - func({ Emscripten: Module }); -} - -function fixture(name, info) { - Module.print('fixture: ' + name); - for (var test in info) { - var f = info[test]; - if (typeof f != 'function') continue; - Module.print('--test: ' + test); - // TODO: Base fixture! - f(); - } -} - -assert.true = assert; - -assert.equal = function(x, y) { - assert(x == y); -} - -assert.notEqual = function(x, y) { - assert(x != y); -} - -assert.throws = function(exc, func) { - var ret; - try { - func(); - } catch(e) { - ret = e; - } - assert(ret); // TODO: check exc vs e - return ret; -} - -assert.instanceof = function(inst, clazz) { - assert(inst instanceof clazz); -} - -assert.deepEqual = function(x, y) { - assert(JSON.stringify(x) == JSON.stringify(y)); -} - -//=== - -module({ - Emscripten: '../build/Emscripten.js' -}, function(imports) { - var cm = imports.Emscripten; - - var checkForLeaks = { - setUp: function() { - this.originalBlockCount = cm.mallinfo().uordblks; - }, - tearDown: function() { - assert.equal(this.originalBlockCount, cm.mallinfo().uordblks); - }, - }; - - fixture("embind", { - baseFixture: checkForLeaks, - - "test value creation": function() { - assert.equal(15, cm.emval_test_new_integer()); - assert.equal("Hello everyone", cm.emval_test_new_string()); - - var object = cm.emval_test_new_object(); - assert.equal('bar', object.foo); - assert.equal(1, object.baz); - }, - - "test passthrough": function() { - var a = {foo: 'bar'}; - var b = cm.emval_test_passthrough(a); - a.bar = 'baz'; - assert.equal('baz', b.bar); - - assert.equal(0, cm.count_emval_handles()); - }, - - "test void return converts to undefined": function() { - assert.equal(undefined, cm.emval_test_return_void()); - }, - - "test booleans can be marshalled": function() { - assert.equal(false, cm.emval_test_not(true)); - assert.equal(true, cm.emval_test_not(false)); - }, - - "test convert double to unsigned": function() { - var rv = cm.emval_test_as_unsigned(1.5); - assert.equal('number', typeof rv); - assert.equal(1, rv); - assert.equal(0, cm.count_emval_handles()); - }, - - "test get length of array": function() { - assert.equal(10, cm.emval_test_get_length([0, 1, 2, 3, 4, 5, 'a', 'b', 'c', 'd'])); - assert.equal(0, cm.count_emval_handles()); - }, - - "test add a bunch of things": function() { - assert.equal(66.0, cm.emval_test_add(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)); - assert.equal(0, cm.count_emval_handles()); - }, - - "test sum array": function() { - assert.equal(66, cm.emval_test_sum([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])); - assert.equal(0, cm.count_emval_handles()); - }, - - "test strings": function() { - assert.equal("foobar", "foo" + "bar"); - assert.equal("foobar", cm.emval_test_take_and_return_std_string("foobar")); - - assert.equal("foobar", cm.emval_test_take_and_return_std_string_const_ref("foobar")); - }, - - "test no memory leak when passing strings in by const reference": function() { - var original = cm.mallinfo().uordblks; - cm.emval_test_take_and_return_std_string_const_ref("foobar"); - assert.equal(original, cm.mallinfo().uordblks); - }, - }); - - fixture("classes", { - baseFixture: checkForLeaks, - - "test class instance": function() { - var a = {foo: 'bar'}; - var c = new cm.ValHolder(a); - assert.equal('bar', c.getVal().foo); - - c.setVal('1234'); - assert.equal('1234', c.getVal()); - - assert.equal(1239, c.returnIntPlusFive(1234)); - - c.delete(); - assert.equal(0, cm.count_emval_handles()); - }, - - "test class methods": function() { - assert.equal(10, cm.ValHolder.some_class_method(10)); - }, - - "test can't call methods on deleted class instances": function() { - var c = new cm.ValHolder(undefined); - c.delete(); - assert.throws(cm.BindingError, function() { - c.getVal(); - }); - assert.throws(cm.BindingError, function() { - c.delete(); - }); - }, - - "test isinstance": function() { - var c = new cm.ValHolder(undefined); - assert.instanceof(c, cm.ValHolder); - c.delete(); - }, - - "test can return class instances by value": function() { - var c = cm.emval_test_return_ValHolder(); - assert.deepEqual({}, c.getVal()); - c.delete(); - }, - - "test can pass class instances to functions by reference": function() { - var a = {a:1}; - var c = new cm.ValHolder(a); - cm.emval_test_set_ValHolder_to_empty_object(c); - assert.deepEqual({}, c.getVal()); - c.delete(); - }, - - "test can access struct fields": function() { - var c = new cm.CustomStruct(); - assert.equal(10, c.field); - c.delete(); - }, - - "test can set struct fields": function() { - var c = new cm.CustomStruct(); - c.field = 15; - assert.equal(15, c.field); - c.delete(); - }, - - "test assignment returns value": function() { - var c = new cm.CustomStruct(); - assert.equal(15, c.field = 15); - c.delete(); - }, - - "test assigning string to integer raises TypeError": function() { - var c = new cm.CustomStruct(); - - var e = assert.throws(TypeError, function() { - c.field = "hi"; - }); - assert.equal('Cannot convert "hi" to int', e.message); - - var e = assert.throws(TypeError, function() { - c.field = {foo:'bar'}; - }); - assert.equal('Cannot convert "[object Object]" to int', e.message); - - c.delete(); - }, - - "test can return tuples by value": function() { - var c = cm.emval_test_return_TupleVector(); - assert.deepEqual([1, 2, 3], c); - }, - - "test tuples can contain tuples": function() { - var c = cm.emval_test_return_TupleVectorTuple(); - assert.deepEqual([[1, 2, 3]], c); - }, - - "test can pass tuples by value": function() { - var c = cm.emval_test_take_and_return_TupleVector([4, 5, 6]); - assert.deepEqual([4, 5, 6], c); - }, - - "test can return structs by value": function() { - var c = cm.emval_test_return_StructVector(); - assert.deepEqual({x: 1, y: 2, z: 3}, c); - }, - - "test can pass structs by value": function() { - var c = cm.emval_test_take_and_return_StructVector({x: 4, y: 5, z: 6}); - assert.deepEqual({x: 4, y: 5, z: 6}, c); - }, - - "test can pass and return tuples in structs": function() { - var d = cm.emval_test_take_and_return_TupleInStruct({field: [1, 2, 3]}); - assert.deepEqual({field: [1, 2, 3]}, d); - }, - - "test can clone handles": function() { - assert.equal(0, cm.count_emval_handles()); - - var a = new cm.ValHolder({}); - var b = a.clone(); - a.delete(); - - assert.equal(1, cm.count_emval_handles()); - - assert.throws(cm.BindingError, function() { - a.delete(); - }); - b.delete(); - - assert.equal(0, cm.count_emval_handles()); - }, - - "test can't clone if already deleted": function() { - var a = new cm.ValHolder({}); - a.delete(); - assert.throws(cm.BindingError, function() { - a.clone(); - }); - }, - - "test moving handles is a clone+delete": function() { - var a = new cm.ValHolder({}); - var b = a.move(); - assert.throws(cm.BindingError, function() { - a.delete(); - }); - assert.equal(1, cm.count_emval_handles()); - b.delete(); - assert.equal(0, cm.count_emval_handles()); - }, - - "test StringHolder": function() { - var a = new cm.StringHolder("foobar"); - assert.equal("foobar", a.get()); - - a.set("barfoo"); - assert.equal("barfoo", a.get()); - a.delete(); - }, - }); - - fixture("embind enumerations", { - baseFixture: checkForLeaks, - - "test can compare enumeration values": function() { - assert.equal(cm.Enum.ONE, cm.Enum.ONE); - assert.notEqual(cm.Enum.ONE, cm.Enum.TWO); - }, - - "test repr includes enum value": function() { - return; // XXX IMVU? - assert.equal('<#Enum_ONE {}>', IMVU.repr(cm.Enum.ONE)); - assert.equal('<#Enum_TWO {}>', IMVU.repr(cm.Enum.TWO)); - }, - - "test instanceof": function() { - assert.instanceof(cm.Enum.ONE, cm.Enum); - }, - - "test can pass and return enumeration values to functions": function() { - assert.equal(cm.Enum.TWO, cm.emval_test_take_and_return_Enum(cm.Enum.TWO)); - }, - }); - - fixture("C++11 enum class", { - baseFixture: checkForLeaks, - - "test can compare enumeration values": function() { - assert.equal(cm.EnumClass.ONE, cm.EnumClass.ONE); - assert.notEqual(cm.EnumClass.ONE, cm.EnumClass.TWO); - }, - - "test repr includes enum value": function() { - return; // XXX IMVU? - assert.equal('<#EnumClass_ONE {}>', IMVU.repr(cm.EnumClass.ONE)); - assert.equal('<#EnumClass_TWO {}>', IMVU.repr(cm.EnumClass.TWO)); - }, - - "test instanceof": function() { - assert.instanceof(cm.EnumClass.ONE, cm.EnumClass); - }, - - "test can pass and return enumeration values to functions": function() { - assert.equal(cm.EnumClass.TWO, cm.emval_test_take_and_return_EnumClass(cm.EnumClass.TWO)); - }, - }); - - fixture("emval call tests", { - "test can call functions from C++": function() { - var called = false; - cm.emval_test_call_function(function(i, f, tv, sv) { - called = true; - assert.equal(10, i); - assert.equal(1.5, f); - assert.deepEqual([1.25, 2.5, 3.75], tv); - assert.deepEqual({x: 1.25, y: 2.5, z: 3.75}, sv); - }, 10, 1.5, [1.25, 2.5, 3.75], {x: 1.25, y: 2.5, z: 3.75}); - assert.true(called); - }, - }); - - fixture("interfaces", { - baseFixture: checkForLeaks, - - "test can wrap JS object in native interface": function() { - var foo = { - calls: [], - method: function() { - this.calls.push('called'); - return 10; - } - }; - - assert.equal(10, cm.emval_test_call_method(foo)); - assert.deepEqual(['called'], foo.calls); - }, - - "test can pass arguments and return complicated values": function() { - var calls = []; - var foo = { - method2: function(arg1, arg2) { - calls.push([arg1, arg2]); - return arg1; - } - }; - - var result = cm.emval_test_call_method2(foo, {field: [1, 2, 3]}, 7); - assert.deepEqual({field: [1, 2, 3]}, result); - assert.deepEqual([[{field: [1, 2, 3]}, 7]], calls); - }, - - "test can call interface methods that return nothing": function() { - var calls = []; - var foo = { - method3: function() { - calls.push('called'); - } - }; - cm.emval_test_call_method3(foo); - assert.deepEqual(['called'], calls); - }, - }); - - test('emscripten::internal::optional', function () { - assert.true(cm.optional_test_copy()); - assert.false(cm.optional_test_copy2()); - }); -}); diff --git a/tests/embind/imvu_test_adapter.js b/tests/embind/imvu_test_adapter.js new file mode 100755 index 000000000..03405d084 --- /dev/null +++ b/tests/embind/imvu_test_adapter.js @@ -0,0 +1,614 @@ +/* The embind test suite (embind.test.js) is configured to be runnable in two different testing engines: + - The Emscripten python test runner (open-source in emscripten repository) and + - The IMVU test runner (closed-source in IMVU repository) + + Embind (and its tests) were originally developed in IMVU repository, which is the reason for two testing architectures. + This adapter file is used when the embind tests are run as part of the Emscripten test runner, to provide the necessary glue code to adapt the tests to Emscripten runner. + + To run the Embind tests using the Emscripten test runner, invoke 'python tests/runner.py other.test_embind' in the Emscripten root directory. +*/ + +//=== testing glue + +function module(ignore, func) { + func({ Emscripten: Module }); +} + +/*global IMVU:true, TEST_MAX_OUTPUT_SIZE*/ +//(function() { +// "use strict"; + + // { beforeTest: function, + // afterTest: function } + var superFixtures = []; + + function registerSuperFixture(superFixture) { + superFixtures.push(superFixture); + } + + // { fixture: Fixture instance, + // name: string, + // body: function() } + var allTests = []; + + function test(name, fn) { + if (arguments.length !== 2) { + throw new TypeError("test requires 2 arguments"); + } + + if (undefined !== activeFixture && activeFixture.abstract) { + activeFixture.abstractTests.push({ + name: name, + body: fn }); + } else { + var fixtureName = (undefined !== activeFixture)? activeFixture.name + ': ' : ''; + allTests.push({ + name: fixtureName + name, + body: fn, + fixture: activeFixture }); + } + } + + function runTest(test, continuation) { + try { + var afterTests = []; + + for (var i = 0; i < superFixtures.length; ++i) { + var superFixture = superFixtures[i]; + + var superScope = {}; + superFixture.beforeTest.call(superScope); + afterTests.push(superFixture.afterTest.bind(superScope)); + } + + var testScope = test.fixture ? + Object.create(test.fixture.scope) : + {}; + + var runSetUp = function(fixtureObject) { + if (undefined === fixtureObject) { + return; + } + runSetUp(fixtureObject.parent); + fixtureObject.setUp.call(testScope); + afterTests.push(fixtureObject.tearDown.bind(testScope)); + }; + runSetUp(test.fixture); + + test.body.call(testScope); + while (afterTests.length) { + afterTests.pop()(); + } + return false; + } catch (e) { + console.error('error:', e); + return {stack: e.stack, e: e}; + } + } + + function run_all(reporter) { + for (var i = 0; i < allTests.length; ++i) { + var test = allTests[i]; + reporter({ + type: 'test-start', + name: test.name + }); + + var failed = runTest(test); + if (failed) { + reporter({ + type: 'test-complete', + name: test.name, + verdict: 'FAIL', + stack: failed.stack, + e: failed.e + }); + return false; + } else { + reporter({ + type: 'test-complete', + name: test.name, + verdict: 'PASS' + }); + } + } + + reporter({ + type: 'all-tests-complete' + }); + + allTests = []; + return true; + } + + var activeFixture; + + function Fixture(parent, name, definition, abstract_) { + if (!(definition instanceof Function)) { + throw new TypeError("fixture's 2nd argument must be a function"); + } + + this.name = name; + this.parent = parent; + this.abstract = abstract_; + if (this.abstract) { + // { name: string, + // body: function } + this.abstractTests = []; + } + + if (this.parent !== undefined) { + this.parent.addAbstractTests(this); + } + + this.scope = (this.parent === undefined ? {} : Object.create(this.parent.scope)); + this.scope.setUp = function(setUp) { + this.setUp = setUp; + }.bind(this); + this.scope.tearDown = function(tearDown) { + this.tearDown = tearDown; + }.bind(this); + + if (undefined !== activeFixture) { + throw new TypeError("Cannot define a fixture within another fixture"); + } + + activeFixture = this; + try { + definition.call(this.scope); + } + finally { + activeFixture = undefined; + } + } + Fixture.prototype.setUp = function defaultSetUp() { + }; + Fixture.prototype.tearDown = function defaultTearDown() { + }; + Fixture.prototype.addAbstractTests = function(concreteFixture) { + if (this.abstract) { + for (var i = 0; i < this.abstractTests.length; ++i) { + var test = this.abstractTests[i]; + allTests.push({ + name: concreteFixture.name + ': ' + test.name, + body: test.body, + fixture: concreteFixture}); + } + } + if (this.parent) { + this.parent.addAbstractTests(concreteFixture); + } + }; + + Fixture.prototype.extend = function(fixtureName, definition) { + return new Fixture(this, fixtureName, definition, false); + }; + + function fixture(fixtureName, definition) { + return new Fixture(undefined, fixtureName, definition, false); + } + fixture.abstract = function(fixtureName, definition) { + return new Fixture(undefined, fixtureName, definition, true); + }; + + var AssertionError = Error; + + function fail(exception, info) { + exception.info = info; + throw exception; + } + + var formatTestValue = function(v) { + return v.toString(); + /* + var s = IMVU.repr(v, TEST_MAX_OUTPUT_SIZE + 1); + if (s.length <= TEST_MAX_OUTPUT_SIZE) { + return s; + } + return s.substring(0, TEST_MAX_OUTPUT_SIZE) + '...'; + */ + }; + +// var assert = { + + //////////////////////////////////////////////////////////////////////////////// + // GENERAL STATUS + + assert.fail = function(info) { + info = info || "assert.fail()"; + fail(new AssertionError(info)); + }, + + //////////////////////////////////////////////////////////////////////////////// + // BOOLEAN TESTS + + assert['true'] = function(value) { + if (!value) { + fail(new AssertionError("expected truthy, actual " + formatTestValue(value)), + {Value: value}); + } + }, + + assert['false'] = function(value) { + if (value) { + fail(new AssertionError("expected falsy, actual " + formatTestValue(value)), + {Value: value}); + } + }, + + //////////////////////////////////////////////////////////////////////////////// + // SCALAR COMPARISON + + assert.equal = function(expected, actual) { + if (expected !== actual) { + fail(new AssertionError('expected: ' + formatTestValue(expected) + ', actual: ' + formatTestValue(actual)), + {Expected: expected, Actual: actual}); + } + }, + + assert.notEqual = function(expected, actual) { + if (expected === actual) { + fail(new AssertionError('actual was equal to: ' + formatTestValue(expected))); + } + }, + + assert.greater = function(lhs, rhs) { + if (lhs <= rhs) { + fail(new AssertionError(formatTestValue(lhs) + ' not greater than ' + formatTestValue(rhs))); + } + }, + + assert.less = function(lhs, rhs) { + if (lhs >= rhs) { + fail(new AssertionError(formatTestValue(lhs) + ' not less than ' + formatTestValue(rhs))); + } + }, + + assert.greaterOrEqual = function(lhs, rhs) { + if (lhs < rhs) { + fail(new AssertionError(formatTestValue(lhs) + ' not greater than or equal to ' + formatTestValue(rhs))); + } + }, + + assert.lessOrEqual = function(lhs, rhs) { + if (lhs > rhs) { + fail(new AssertionError(formatTestValue(lhs) + ' not less than or equal to ' + formatTestValue(rhs))); + } + }, + + //////////////////////////////////////////////////////////////////////////////// + // DEEP COMPARISON + + assert.deepEqual = function(expected, actual) { + if (!_.isEqual(expected, actual)) { + fail(new AssertionError('expected: ' + formatTestValue(expected) + ', actual: ' + formatTestValue(actual)), + {Expected: expected, Actual: actual}); + } + }, + + assert.notDeepEqual = function(expected, actual) { + if (_.isEqual(expected, actual)) { + fail(new AssertionError('expected: ' + formatTestValue(expected) + ' and actual: ' + formatTestValue(actual) + ' were equal')); + } + }, + + //////////////////////////////////////////////////////////////////////////////// + // FLOATING POINT + + assert.nearEqual = function( expected, actual, tolerance ) { + if( tolerance === undefined ) { + tolerance = 0.0; + } + if( expected instanceof Array && actual instanceof Array ) { + assert.equal(expected.length, actual.length); + for( var i=0; i tolerance ) { + fail( new AssertionError('expected: ' + formatTestValue(expected) + ', actual: ' + formatTestValue(actual) + + ', tolerance: ' + formatTestValue(tolerance) + ', diff: ' + formatTestValue(actual-expected) ), + { Expected:expected, Actual:actual, Tolerance:tolerance } ); + } + }, + + //////////////////////////////////////////////////////////////////////////////// + // STRING + + assert.inString = function(expected, string){ + if (-1 === string.indexOf(expected)){ + fail(new AssertionError('expected: ' + formatTestValue(expected) + ' not in string: ' + formatTestValue(string)), + {Expected: expected, 'String': string}); + } + }, + + assert.notInString = function(expected, string){ + if (-1 !== string.indexOf(expected)){ + fail(new AssertionError('unexpected: ' + formatTestValue(expected) + ' in string: ' + formatTestValue(string)), + {Expected: expected, 'String': string}); + } + }, + + assert.matches = function(re, string) { + if (!re.test(string)) { + fail(new AssertionError('regexp ' + re + ' does not match: ' + string)); + } + }, + + //////////////////////////////////////////////////////////////////////////////// + // ARRAY + + assert.inArray = function(expected, array) { + var found = false; + _.each(array, function(element){ + if (_.isEqual(expected, element)){ + found = true; + } + }); + if (!found){ + fail(new AssertionError('expected: ' + formatTestValue(expected) + ' not found in array: ' + formatTestValue(array)), + {Expected: expected, 'Array': array}); + } + }, + + assert.notInArray = function(expected, array) { + var found = false; + _.each(array, function(element){ + if (_.isEqual(expected, element)){ + found = true; + } + }); + if (found){ + fail(new AssertionError('unexpected: ' + formatTestValue(expected) + ' found in array: ' + formatTestValue(array)), + {Expected: expected, 'Array': array}); + } + }, + + //////////////////////////////////////////////////////////////////////////////// + // OBJECTS + + assert.hasKey = function (key, object) { + if (!(key in object)) { + fail(new AssertionError('Key ' + formatTestValue(key) + ' is not in object: ' + formatTestValue(object))); + } + }, + + assert.notHasKey = function (key, object) { + if (key in object) { + fail(new AssertionError('Unexpected key ' + formatTestValue(key) + ' is found in object: ' + formatTestValue(object))); + } + }, + + //////////////////////////////////////////////////////////////////////////////// + // EXCEPTIONS + + assert['throws'] = function(exception, fn) { + try { + fn(); + } catch (e) { + if (e instanceof exception) { + return e; + } + fail(new AssertionError('Expected to throw "' + exception.name + '", actually threw: ' + formatTestValue(e) + ': ' + e.message), + {Expected: exception, Actual: e}); + } + throw new AssertionError('did not throw'); + }, + + //////////////////////////////////////////////////////////////////////////////// + // TYPE + + assert['instanceof'] = function(actual, type) { + if(!(actual instanceof type)) { + fail(new AssertionError(formatTestValue(actual) + ' not instance of ' + formatTestValue(type)), + {Type: type, Actual: actual}); + } + }, + + //////////////////////////////////////////////////////////////////////////////// + // DOM ASSERTIONS + + // TODO: lift into separate file? + assert.dom = { + present: function(domElement){ + if (!$(domElement).length) { + fail(new AssertionError(decipherDomElement(domElement) + ' should be present')); + } + }, + + notPresent: function(selector){ + assert.equal(0, $(selector).length); + }, + + hasTag: function(tag, domElement) { + var elementTag = $(domElement)[0].tagName.toLowerCase(); + if (elementTag !== tag.toLowerCase()) { + fail(new AssertionError(decipherDomElement(domElement) + ' expected to have tag name ' + formatTestValue(tag) + ', was ' + formatTestValue(elementTag) + ' instead')); + } + }, + + hasClass: function(className, domElement) { + if (!$(domElement).hasClass(className)){ + fail(new AssertionError(decipherDomElement(domElement) + ' expected to have class '+ formatTestValue(className) + ', has ' + formatTestValue($(domElement).attr('class')) + ' instead')); + } + }, + + notHasClass: function(className, domElement) { + assert.dom.present(domElement); // if domElement is empty, .hasClass will always return false + if ($(domElement).hasClass(className)){ + fail(new AssertionError(decipherDomElement(domElement) + ' expected NOT to have class '+ formatTestValue(className))); + } + }, + + hasAttribute: function(attributeName, selector) { + assert['true']($(selector).is('[' + attributeName + ']')); + }, + + notHasAttribute: function(attributeName, selector) { + assert.dom.present(selector); + assert['false']($(selector).is('[' + attributeName + ']')); + }, + + attr: function(value, attributeName, selector) { + assert.equal(value, $(selector).attr(attributeName)); + }, + + attributeValues: function (values, selector) { + var $el = $(selector); + _(values).each(function (val, key) { + assert.equal(val, $el.attr(key)); + }); + }, + + text: function(expected, selector) { + assert.equal(expected, $(selector).text()); + }, + + value: function(expected, selector) { + assert.equal(expected, $(selector).val()); + }, + + count: function(elementCount, selector) { + assert.equal(elementCount, $(selector).length); + }, + + visible: function(domElement) { + if (!$(domElement).is(':visible')) { + fail(new AssertionError(decipherDomElement(domElement) + ' expected to be visible')); + } + }, + + notVisible: function(domElement) { + assert.dom.present(domElement); + if ($(domElement).is(':visible')) { + fail(new AssertionError(decipherDomElement(domElement) + ' expected to be NOT visible')); + } + }, + + disabled: function(domElement) { + if (!$(domElement).is(':disabled')) { + fail(new AssertionError(decipherDomElement(domElement) + ' expected to be disabled')); + } + }, + + enabled: function(domElement) { + if (!$(domElement).is(':enabled')) { + fail(new AssertionError(decipherDomElement(domElement) + ' expected to be enabled')); + } + }, + + focused: function(selector) { + var expected = $(selector)[0]; + var actual = document.activeElement; + if (expected !== actual) { + throw new AssertionError(actual.outerHTML + ' has focus. expected: ' + expected.outerHTML); + } + }, + + notFocused: function(selector) { + var expected = $(selector)[0]; + var actual = document.activeElement; + if (expected === actual) { + throw new AssertionError(expected.outerHTML + ' expected not to have focus.'); + } + }, + + html: function(expected, selector) { + assert.equal(expected, $(selector).html()); + }, + + css: function(expected, propertyName, selector) { + assert.equal(expected, $(selector).css(propertyName)); + }, + + empty: function(selectorOrJQueryObject) { + var el = selectorOrJQueryObject; + assert.dom.present(el); + if (!$(el).is(':empty')) { + fail(new AssertionError(decipherDomElement(el) + ' expected to be empty')); + } + }, + + notEmpty: function(selectorOrJQueryObject) { + var el = selectorOrJQueryObject; + assert.dom.present(el); + if ($(el).is(':empty')) { + fail(new AssertionError(decipherDomElement(el) + ' expected NOT to be empty')); + } + } + } +// }; + + function decipherDomElement(selectorOrJQueryObject) { + if (typeof selectorOrJQueryObject === 'string') { + return 'Selector ' + formatTestValue(selectorOrJQueryObject); + } else if (typeof selectorOrJQueryObject === 'object') { + return "'" + selectorOrJQueryObject[0] + "'"; + } + } + + var g = 'undefined' === typeof window ? global : window; + + // synonyms + assert.equals = assert.equal; + assert.notEquals = assert.notEqual; + assert['null'] = assert.equal.bind(null, null); + assert.notNull = assert.notEqual.bind(null, null); + assert['undefined'] = assert.equal.bind(null, undefined); + assert.notUndefined = assert.notEqual.bind(null, undefined); + + // ES3 synonyms + assert.false_ = assert['false']; + assert.true_ = assert['true']; + + g.registerSuperFixture = registerSuperFixture; + g.test = test; + g.run_all = run_all; + g.fixture = fixture; +// g.repr = IMVU.repr; + g.AssertionError = AssertionError; + g.assert = assert; + g.test = test; + g.TEST_MAX_OUTPUT_SIZE = 1024; + + g.setTimeout = function(fn, time) { + if (time === 1 || time === 0){ + fn(); + return 0; + } + throw new AssertionError("Don't call setTimeout in tests. Use fakes."); + }; + + g.setInterval = function() { + throw new AssertionError("Don't call setInterval in tests. Use fakes."); + }; + + if (typeof process !== 'undefined') { + process.nextTick = function() { + throw new AssertionError("Don't call process.nextTick in tests. Use fakes."); + }; + } + + Math.random = function() { + throw new AssertionError("Don't call Math.random in tests. Use fakes."); + }; + + g.requestAnimationFrame = function() { + throw new AssertionError("Don't call requestAnimationFrame in tests. Use fakes."); + }; +//})(); + +// Emscripten runner starts all tests from this function. +// IMVU runner uses a separate runner & reporting mechanism. +function run_all_tests() { + function report_to_stdout(msg) { + if (msg.type == "test-complete") + console.log(msg.name + ": " + msg.verdict); + } + run_all(report_to_stdout); +} + +// Signal the embind test suite that it is being run from the Emscripten python test runner and not the +// IMVU test runner. +var INVOKED_FROM_EMSCRIPTEN_TEST_RUNNER = 1; diff --git a/tests/embind/underscore-1.4.2.js b/tests/embind/underscore-1.4.2.js new file mode 100755 index 000000000..dd2740957 --- /dev/null +++ b/tests/embind/underscore-1.4.2.js @@ -0,0 +1,1200 @@ +// Underscore.js 1.4.2 +// http://underscorejs.org +// (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc. +// Underscore may be freely distributed under the MIT license. + +//(function() { + + // Baseline setup + // -------------- + + // Establish the root object, `window` in the browser, or `global` on the server. + var root = this; + + // Save the previous value of the `_` variable. + var previousUnderscore = root._; + + // Establish the object that gets returned to break out of a loop iteration. + var breaker = {}; + + // Save bytes in the minified (but not gzipped) version: + var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype; + + // Create quick reference variables for speed access to core prototypes. + var push = ArrayProto.push, + slice = ArrayProto.slice, + concat = ArrayProto.concat, + unshift = ArrayProto.unshift, + toString = ObjProto.toString, + hasOwnProperty = ObjProto.hasOwnProperty; + + // All **ECMAScript 5** native function implementations that we hope to use + // are declared here. + var + nativeForEach = ArrayProto.forEach, + nativeMap = ArrayProto.map, + nativeReduce = ArrayProto.reduce, + nativeReduceRight = ArrayProto.reduceRight, + nativeFilter = ArrayProto.filter, + nativeEvery = ArrayProto.every, + nativeSome = ArrayProto.some, + nativeIndexOf = ArrayProto.indexOf, + nativeLastIndexOf = ArrayProto.lastIndexOf, + nativeIsArray = Array.isArray, + nativeKeys = Object.keys, + nativeBind = FuncProto.bind; + + // Create a safe reference to the Underscore object for use below. + var _ = function(obj) { + if (obj instanceof _) return obj; + if (!(this instanceof _)) return new _(obj); + this._wrapped = obj; + }; + + // Export the Underscore object for **Node.js**, with + // backwards-compatibility for the old `require()` API. If we're in + // the browser, add `_` as a global object via a string identifier, + // for Closure Compiler "advanced" mode. + if (typeof exports !== 'undefined') { + if (typeof module !== 'undefined' && module.exports) { + exports = module.exports = _; + } + exports._ = _; + } else { + root['_'] = _; + } + + // Current version. + _.VERSION = '1.4.2'; + + // Collection Functions + // -------------------- + + // The cornerstone, an `each` implementation, aka `forEach`. + // Handles objects with the built-in `forEach`, arrays, and raw objects. + // Delegates to **ECMAScript 5**'s native `forEach` if available. + var each = _.each = _.forEach = function(obj, iterator, context) { + if (obj == null) return; + if (nativeForEach && obj.forEach === nativeForEach) { + obj.forEach(iterator, context); + } else if (obj.length === +obj.length) { + for (var i = 0, l = obj.length; i < l; i++) { + if (iterator.call(context, obj[i], i, obj) === breaker) return; + } + } else { + for (var key in obj) { + if (_.has(obj, key)) { + if (iterator.call(context, obj[key], key, obj) === breaker) return; + } + } + } + }; + + // Return the results of applying the iterator to each element. + // Delegates to **ECMAScript 5**'s native `map` if available. + _.map = _.collect = function(obj, iterator, context) { + var results = []; + if (obj == null) return results; + if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context); + each(obj, function(value, index, list) { + results[results.length] = iterator.call(context, value, index, list); + }); + return results; + }; + + // **Reduce** builds up a single result from a list of values, aka `inject`, + // or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available. + _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) { + var initial = arguments.length > 2; + if (obj == null) obj = []; + if (nativeReduce && obj.reduce === nativeReduce) { + if (context) iterator = _.bind(iterator, context); + return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator); + } + each(obj, function(value, index, list) { + if (!initial) { + memo = value; + initial = true; + } else { + memo = iterator.call(context, memo, value, index, list); + } + }); + if (!initial) throw new TypeError('Reduce of empty array with no initial value'); + return memo; + }; + + // The right-associative version of reduce, also known as `foldr`. + // Delegates to **ECMAScript 5**'s native `reduceRight` if available. + _.reduceRight = _.foldr = function(obj, iterator, memo, context) { + var initial = arguments.length > 2; + if (obj == null) obj = []; + if (nativeReduceRight && obj.reduceRight === nativeReduceRight) { + if (context) iterator = _.bind(iterator, context); + return arguments.length > 2 ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator); + } + var length = obj.length; + if (length !== +length) { + var keys = _.keys(obj); + length = keys.length; + } + each(obj, function(value, index, list) { + index = keys ? keys[--length] : --length; + if (!initial) { + memo = obj[index]; + initial = true; + } else { + memo = iterator.call(context, memo, obj[index], index, list); + } + }); + if (!initial) throw new TypeError('Reduce of empty array with no initial value'); + return memo; + }; + + // Return the first value which passes a truth test. Aliased as `detect`. + _.find = _.detect = function(obj, iterator, context) { + var result; + any(obj, function(value, index, list) { + if (iterator.call(context, value, index, list)) { + result = value; + return true; + } + }); + return result; + }; + + // Return all the elements that pass a truth test. + // Delegates to **ECMAScript 5**'s native `filter` if available. + // Aliased as `select`. + _.filter = _.select = function(obj, iterator, context) { + var results = []; + if (obj == null) return results; + if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context); + each(obj, function(value, index, list) { + if (iterator.call(context, value, index, list)) results[results.length] = value; + }); + return results; + }; + + // Return all the elements for which a truth test fails. + _.reject = function(obj, iterator, context) { + var results = []; + if (obj == null) return results; + each(obj, function(value, index, list) { + if (!iterator.call(context, value, index, list)) results[results.length] = value; + }); + return results; + }; + + // Determine whether all of the elements match a truth test. + // Delegates to **ECMAScript 5**'s native `every` if available. + // Aliased as `all`. + _.every = _.all = function(obj, iterator, context) { + iterator || (iterator = _.identity); + var result = true; + if (obj == null) return result; + if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context); + each(obj, function(value, index, list) { + if (!(result = result && iterator.call(context, value, index, list))) return breaker; + }); + return !!result; + }; + + // Determine if at least one element in the object matches a truth test. + // Delegates to **ECMAScript 5**'s native `some` if available. + // Aliased as `any`. + var any = _.some = _.any = function(obj, iterator, context) { + iterator || (iterator = _.identity); + var result = false; + if (obj == null) return result; + if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context); + each(obj, function(value, index, list) { + if (result || (result = iterator.call(context, value, index, list))) return breaker; + }); + return !!result; + }; + + // Determine if the array or object contains a given value (using `===`). + // Aliased as `include`. + _.contains = _.include = function(obj, target) { + var found = false; + if (obj == null) return found; + if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1; + found = any(obj, function(value) { + return value === target; + }); + return found; + }; + + // Invoke a method (with arguments) on every item in a collection. + _.invoke = function(obj, method) { + var args = slice.call(arguments, 2); + return _.map(obj, function(value) { + return (_.isFunction(method) ? method : value[method]).apply(value, args); + }); + }; + + // Convenience version of a common use case of `map`: fetching a property. + _.pluck = function(obj, key) { + return _.map(obj, function(value){ return value[key]; }); + }; + + // Convenience version of a common use case of `filter`: selecting only objects + // with specific `key:value` pairs. + _.where = function(obj, attrs) { + if (_.isEmpty(attrs)) return []; + return _.filter(obj, function(value) { + for (var key in attrs) { + if (attrs[key] !== value[key]) return false; + } + return true; + }); + }; + + // Return the maximum element or (element-based computation). + // Can't optimize arrays of integers longer than 65,535 elements. + // See: https://bugs.webkit.org/show_bug.cgi?id=80797 + _.max = function(obj, iterator, context) { + if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) { + return Math.max.apply(Math, obj); + } + if (!iterator && _.isEmpty(obj)) return -Infinity; + var result = {computed : -Infinity}; + each(obj, function(value, index, list) { + var computed = iterator ? iterator.call(context, value, index, list) : value; + computed >= result.computed && (result = {value : value, computed : computed}); + }); + return result.value; + }; + + // Return the minimum element (or element-based computation). + _.min = function(obj, iterator, context) { + if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) { + return Math.min.apply(Math, obj); + } + if (!iterator && _.isEmpty(obj)) return Infinity; + var result = {computed : Infinity}; + each(obj, function(value, index, list) { + var computed = iterator ? iterator.call(context, value, index, list) : value; + computed < result.computed && (result = {value : value, computed : computed}); + }); + return result.value; + }; + + // Shuffle an array. + _.shuffle = function(obj) { + var rand; + var index = 0; + var shuffled = []; + each(obj, function(value) { + rand = _.random(index++); + shuffled[index - 1] = shuffled[rand]; + shuffled[rand] = value; + }); + return shuffled; + }; + + // An internal function to generate lookup iterators. + var lookupIterator = function(value) { + return _.isFunction(value) ? value : function(obj){ return obj[value]; }; + }; + + // Sort the object's values by a criterion produced by an iterator. + _.sortBy = function(obj, value, context) { + var iterator = lookupIterator(value); + return _.pluck(_.map(obj, function(value, index, list) { + return { + value : value, + index : index, + criteria : iterator.call(context, value, index, list) + }; + }).sort(function(left, right) { + var a = left.criteria; + var b = right.criteria; + if (a !== b) { + if (a > b || a === void 0) return 1; + if (a < b || b === void 0) return -1; + } + return left.index < right.index ? -1 : 1; + }), 'value'); + }; + + // An internal function used for aggregate "group by" operations. + var group = function(obj, value, context, behavior) { + var result = {}; + var iterator = lookupIterator(value); + each(obj, function(value, index) { + var key = iterator.call(context, value, index, obj); + behavior(result, key, value); + }); + return result; + }; + + // Groups the object's values by a criterion. Pass either a string attribute + // to group by, or a function that returns the criterion. + _.groupBy = function(obj, value, context) { + return group(obj, value, context, function(result, key, value) { + (_.has(result, key) ? result[key] : (result[key] = [])).push(value); + }); + }; + + // Counts instances of an object that group by a certain criterion. Pass + // either a string attribute to count by, or a function that returns the + // criterion. + _.countBy = function(obj, value, context) { + return group(obj, value, context, function(result, key, value) { + if (!_.has(result, key)) result[key] = 0; + result[key]++; + }); + }; + + // Use a comparator function to figure out the smallest index at which + // an object should be inserted so as to maintain order. Uses binary search. + _.sortedIndex = function(array, obj, iterator, context) { + iterator = iterator == null ? _.identity : lookupIterator(iterator); + var value = iterator.call(context, obj); + var low = 0, high = array.length; + while (low < high) { + var mid = (low + high) >>> 1; + iterator.call(context, array[mid]) < value ? low = mid + 1 : high = mid; + } + return low; + }; + + // Safely convert anything iterable into a real, live array. + _.toArray = function(obj) { + if (!obj) return []; + if (obj.length === +obj.length) return slice.call(obj); + return _.values(obj); + }; + + // Return the number of elements in an object. + _.size = function(obj) { + return (obj.length === +obj.length) ? obj.length : _.keys(obj).length; + }; + + // Array Functions + // --------------- + + // Get the first element of an array. Passing **n** will return the first N + // values in the array. Aliased as `head` and `take`. The **guard** check + // allows it to work with `_.map`. + _.first = _.head = _.take = function(array, n, guard) { + return (n != null) && !guard ? slice.call(array, 0, n) : array[0]; + }; + + // Returns everything but the last entry of the array. Especially useful on + // the arguments object. Passing **n** will return all the values in + // the array, excluding the last N. The **guard** check allows it to work with + // `_.map`. + _.initial = function(array, n, guard) { + return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n)); + }; + + // Get the last element of an array. Passing **n** will return the last N + // values in the array. The **guard** check allows it to work with `_.map`. + _.last = function(array, n, guard) { + if ((n != null) && !guard) { + return slice.call(array, Math.max(array.length - n, 0)); + } else { + return array[array.length - 1]; + } + }; + + // Returns everything but the first entry of the array. Aliased as `tail` and `drop`. + // Especially useful on the arguments object. Passing an **n** will return + // the rest N values in the array. The **guard** + // check allows it to work with `_.map`. + _.rest = _.tail = _.drop = function(array, n, guard) { + return slice.call(array, (n == null) || guard ? 1 : n); + }; + + // Trim out all falsy values from an array. + _.compact = function(array) { + return _.filter(array, function(value){ return !!value; }); + }; + + // Internal implementation of a recursive `flatten` function. + var flatten = function(input, shallow, output) { + each(input, function(value) { + if (_.isArray(value)) { + shallow ? push.apply(output, value) : flatten(value, shallow, output); + } else { + output.push(value); + } + }); + return output; + }; + + // Return a completely flattened version of an array. + _.flatten = function(array, shallow) { + return flatten(array, shallow, []); + }; + + // Return a version of the array that does not contain the specified value(s). + _.without = function(array) { + return _.difference(array, slice.call(arguments, 1)); + }; + + // Produce a duplicate-free version of the array. If the array has already + // been sorted, you have the option of using a faster algorithm. + // Aliased as `unique`. + _.uniq = _.unique = function(array, isSorted, iterator, context) { + var initial = iterator ? _.map(array, iterator, context) : array; + var results = []; + var seen = []; + each(initial, function(value, index) { + if (isSorted ? (!index || seen[seen.length - 1] !== value) : !_.contains(seen, value)) { + seen.push(value); + results.push(array[index]); + } + }); + return results; + }; + + // Produce an array that contains the union: each distinct element from all of + // the passed-in arrays. + _.union = function() { + return _.uniq(concat.apply(ArrayProto, arguments)); + }; + + // Produce an array that contains every item shared between all the + // passed-in arrays. + _.intersection = function(array) { + var rest = slice.call(arguments, 1); + return _.filter(_.uniq(array), function(item) { + return _.every(rest, function(other) { + return _.indexOf(other, item) >= 0; + }); + }); + }; + + // Take the difference between one array and a number of other arrays. + // Only the elements present in just the first array will remain. + _.difference = function(array) { + var rest = concat.apply(ArrayProto, slice.call(arguments, 1)); + return _.filter(array, function(value){ return !_.contains(rest, value); }); + }; + + // Zip together multiple lists into a single array -- elements that share + // an index go together. + _.zip = function() { + var args = slice.call(arguments); + var length = _.max(_.pluck(args, 'length')); + var results = new Array(length); + for (var i = 0; i < length; i++) { + results[i] = _.pluck(args, "" + i); + } + return results; + }; + + // Converts lists into objects. Pass either a single array of `[key, value]` + // pairs, or two parallel arrays of the same length -- one of keys, and one of + // the corresponding values. + _.object = function(list, values) { + var result = {}; + for (var i = 0, l = list.length; i < l; i++) { + if (values) { + result[list[i]] = values[i]; + } else { + result[list[i][0]] = list[i][1]; + } + } + return result; + }; + + // If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**), + // we need this function. Return the position of the first occurrence of an + // item in an array, or -1 if the item is not included in the array. + // Delegates to **ECMAScript 5**'s native `indexOf` if available. + // If the array is large and already in sort order, pass `true` + // for **isSorted** to use binary search. + _.indexOf = function(array, item, isSorted) { + if (array == null) return -1; + var i = 0, l = array.length; + if (isSorted) { + if (typeof isSorted == 'number') { + i = (isSorted < 0 ? Math.max(0, l + isSorted) : isSorted); + } else { + i = _.sortedIndex(array, item); + return array[i] === item ? i : -1; + } + } + if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item, isSorted); + for (; i < l; i++) if (array[i] === item) return i; + return -1; + }; + + // Delegates to **ECMAScript 5**'s native `lastIndexOf` if available. + _.lastIndexOf = function(array, item, from) { + if (array == null) return -1; + var hasIndex = from != null; + if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) { + return hasIndex ? array.lastIndexOf(item, from) : array.lastIndexOf(item); + } + var i = (hasIndex ? from : array.length); + while (i--) if (array[i] === item) return i; + return -1; + }; + + // Generate an integer Array containing an arithmetic progression. A port of + // the native Python `range()` function. See + // [the Python documentation](http://docs.python.org/library/functions.html#range). + _.range = function(start, stop, step) { + if (arguments.length <= 1) { + stop = start || 0; + start = 0; + } + step = arguments[2] || 1; + + var len = Math.max(Math.ceil((stop - start) / step), 0); + var idx = 0; + var range = new Array(len); + + while(idx < len) { + range[idx++] = start; + start += step; + } + + return range; + }; + + // Function (ahem) Functions + // ------------------ + + // Reusable constructor function for prototype setting. + var ctor = function(){}; + + // Create a function bound to a given object (assigning `this`, and arguments, + // optionally). Binding with arguments is also known as `curry`. + // Delegates to **ECMAScript 5**'s native `Function.bind` if available. + // We check for `func.bind` first, to fail fast when `func` is undefined. + _.bind = function bind(func, context) { + var bound, args; + if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1)); + if (!_.isFunction(func)) throw new TypeError; + args = slice.call(arguments, 2); + return bound = function() { + if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments))); + ctor.prototype = func.prototype; + var self = new ctor; + var result = func.apply(self, args.concat(slice.call(arguments))); + if (Object(result) === result) return result; + return self; + }; + }; + + // Bind all of an object's methods to that object. Useful for ensuring that + // all callbacks defined on an object belong to it. + _.bindAll = function(obj) { + var funcs = slice.call(arguments, 1); + if (funcs.length == 0) funcs = _.functions(obj); + each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); }); + return obj; + }; + + // Memoize an expensive function by storing its results. + _.memoize = function(func, hasher) { + var memo = {}; + hasher || (hasher = _.identity); + return function() { + var key = hasher.apply(this, arguments); + return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments)); + }; + }; + + // Delays a function for the given number of milliseconds, and then calls + // it with the arguments supplied. + _.delay = function(func, wait) { + var args = slice.call(arguments, 2); + return setTimeout(function(){ return func.apply(null, args); }, wait); + }; + + // Defers a function, scheduling it to run after the current call stack has + // cleared. + _.defer = function(func) { + return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1))); + }; + + // Returns a function, that, when invoked, will only be triggered at most once + // during a given window of time. + _.throttle = function(func, wait) { + var context, args, timeout, throttling, more, result; + var whenDone = _.debounce(function(){ more = throttling = false; }, wait); + return function() { + context = this; args = arguments; + var later = function() { + timeout = null; + if (more) { + result = func.apply(context, args); + } + whenDone(); + }; + if (!timeout) timeout = setTimeout(later, wait); + if (throttling) { + more = true; + } else { + throttling = true; + result = func.apply(context, args); + } + whenDone(); + return result; + }; + }; + + // Returns a function, that, as long as it continues to be invoked, will not + // be triggered. The function will be called after it stops being called for + // N milliseconds. If `immediate` is passed, trigger the function on the + // leading edge, instead of the trailing. + _.debounce = function(func, wait, immediate) { + var timeout, result; + return function() { + var context = this, args = arguments; + var later = function() { + timeout = null; + if (!immediate) result = func.apply(context, args); + }; + var callNow = immediate && !timeout; + clearTimeout(timeout); + timeout = setTimeout(later, wait); + if (callNow) result = func.apply(context, args); + return result; + }; + }; + + // Returns a function that will be executed at most one time, no matter how + // often you call it. Useful for lazy initialization. + _.once = function(func) { + var ran = false, memo; + return function() { + if (ran) return memo; + ran = true; + memo = func.apply(this, arguments); + func = null; + return memo; + }; + }; + + // Returns the first function passed as an argument to the second, + // allowing you to adjust arguments, run code before and after, and + // conditionally execute the original function. + _.wrap = function(func, wrapper) { + return function() { + var args = [func]; + push.apply(args, arguments); + return wrapper.apply(this, args); + }; + }; + + // Returns a function that is the composition of a list of functions, each + // consuming the return value of the function that follows. + _.compose = function() { + var funcs = arguments; + return function() { + var args = arguments; + for (var i = funcs.length - 1; i >= 0; i--) { + args = [funcs[i].apply(this, args)]; + } + return args[0]; + }; + }; + + // Returns a function that will only be executed after being called N times. + _.after = function(times, func) { + if (times <= 0) return func(); + return function() { + if (--times < 1) { + return func.apply(this, arguments); + } + }; + }; + + // Object Functions + // ---------------- + + // Retrieve the names of an object's properties. + // Delegates to **ECMAScript 5**'s native `Object.keys` + _.keys = nativeKeys || function(obj) { + if (obj !== Object(obj)) throw new TypeError('Invalid object'); + var keys = []; + for (var key in obj) if (_.has(obj, key)) keys[keys.length] = key; + return keys; + }; + + // Retrieve the values of an object's properties. + _.values = function(obj) { + var values = []; + for (var key in obj) if (_.has(obj, key)) values.push(obj[key]); + return values; + }; + + // Convert an object into a list of `[key, value]` pairs. + _.pairs = function(obj) { + var pairs = []; + for (var key in obj) if (_.has(obj, key)) pairs.push([key, obj[key]]); + return pairs; + }; + + // Invert the keys and values of an object. The values must be serializable. + _.invert = function(obj) { + var result = {}; + for (var key in obj) if (_.has(obj, key)) result[obj[key]] = key; + return result; + }; + + // Return a sorted list of the function names available on the object. + // Aliased as `methods` + _.functions = _.methods = function(obj) { + var names = []; + for (var key in obj) { + if (_.isFunction(obj[key])) names.push(key); + } + return names.sort(); + }; + + // Extend a given object with all the properties in passed-in object(s). + _.extend = function(obj) { + each(slice.call(arguments, 1), function(source) { + for (var prop in source) { + obj[prop] = source[prop]; + } + }); + return obj; + }; + + // Return a copy of the object only containing the whitelisted properties. + _.pick = function(obj) { + var copy = {}; + var keys = concat.apply(ArrayProto, slice.call(arguments, 1)); + each(keys, function(key) { + if (key in obj) copy[key] = obj[key]; + }); + return copy; + }; + + // Return a copy of the object without the blacklisted properties. + _.omit = function(obj) { + var copy = {}; + var keys = concat.apply(ArrayProto, slice.call(arguments, 1)); + for (var key in obj) { + if (!_.contains(keys, key)) copy[key] = obj[key]; + } + return copy; + }; + + // Fill in a given object with default properties. + _.defaults = function(obj) { + each(slice.call(arguments, 1), function(source) { + for (var prop in source) { + if (obj[prop] == null) obj[prop] = source[prop]; + } + }); + return obj; + }; + + // Create a (shallow-cloned) duplicate of an object. + _.clone = function(obj) { + if (!_.isObject(obj)) return obj; + return _.isArray(obj) ? obj.slice() : _.extend({}, obj); + }; + + // Invokes interceptor with the obj, and then returns obj. + // The primary purpose of this method is to "tap into" a method chain, in + // order to perform operations on intermediate results within the chain. + _.tap = function(obj, interceptor) { + interceptor(obj); + return obj; + }; + + // Internal recursive comparison function for `isEqual`. + var eq = function(a, b, aStack, bStack) { + // Identical objects are equal. `0 === -0`, but they aren't identical. + // See the Harmony `egal` proposal: http://wiki.ecmascript.org/doku.php?id=harmony:egal. + if (a === b) return a !== 0 || 1 / a == 1 / b; + // A strict comparison is necessary because `null == undefined`. + if (a == null || b == null) return a === b; + // Unwrap any wrapped objects. + if (a instanceof _) a = a._wrapped; + if (b instanceof _) b = b._wrapped; + // Compare `[[Class]]` names. + var className = toString.call(a); + if (className != toString.call(b)) return false; + switch (className) { + // Strings, numbers, dates, and booleans are compared by value. + case '[object String]': + // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is + // equivalent to `new String("5")`. + return a == String(b); + case '[object Number]': + // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for + // other numeric values. + return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b); + case '[object Date]': + case '[object Boolean]': + // Coerce dates and booleans to numeric primitive values. Dates are compared by their + // millisecond representations. Note that invalid dates with millisecond representations + // of `NaN` are not equivalent. + return +a == +b; + // RegExps are compared by their source patterns and flags. + case '[object RegExp]': + return a.source == b.source && + a.global == b.global && + a.multiline == b.multiline && + a.ignoreCase == b.ignoreCase; + } + if (typeof a != 'object' || typeof b != 'object') return false; + // Assume equality for cyclic structures. The algorithm for detecting cyclic + // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`. + var length = aStack.length; + while (length--) { + // Linear search. Performance is inversely proportional to the number of + // unique nested structures. + if (aStack[length] == a) return bStack[length] == b; + } + // Add the first object to the stack of traversed objects. + aStack.push(a); + bStack.push(b); + var size = 0, result = true; + // Recursively compare objects and arrays. + if (className == '[object Array]') { + // Compare array lengths to determine if a deep comparison is necessary. + size = a.length; + result = size == b.length; + if (result) { + // Deep compare the contents, ignoring non-numeric properties. + while (size--) { + if (!(result = eq(a[size], b[size], aStack, bStack))) break; + } + } + } else { + // Objects with different constructors are not equivalent, but `Object`s + // from different frames are. + var aCtor = a.constructor, bCtor = b.constructor; + if (aCtor !== bCtor && !(_.isFunction(aCtor) && (aCtor instanceof aCtor) && + _.isFunction(bCtor) && (bCtor instanceof bCtor))) { + return false; + } + // Deep compare objects. + for (var key in a) { + if (_.has(a, key)) { + // Count the expected number of properties. + size++; + // Deep compare each member. + if (!(result = _.has(b, key) && eq(a[key], b[key], aStack, bStack))) break; + } + } + // Ensure that both objects contain the same number of properties. + if (result) { + for (key in b) { + if (_.has(b, key) && !(size--)) break; + } + result = !size; + } + } + // Remove the first object from the stack of traversed objects. + aStack.pop(); + bStack.pop(); + return result; + }; + + // Perform a deep comparison to check if two objects are equal. + _.isEqual = function(a, b) { + return eq(a, b, [], []); + }; + + // Is a given array, string, or object empty? + // An "empty" object has no enumerable own-properties. + _.isEmpty = function(obj) { + if (obj == null) return true; + if (_.isArray(obj) || _.isString(obj)) return obj.length === 0; + for (var key in obj) if (_.has(obj, key)) return false; + return true; + }; + + // Is a given value a DOM element? + _.isElement = function(obj) { + return !!(obj && obj.nodeType === 1); + }; + + // Is a given value an array? + // Delegates to ECMA5's native Array.isArray + _.isArray = nativeIsArray || function(obj) { + return toString.call(obj) == '[object Array]'; + }; + + // Is a given variable an object? + _.isObject = function(obj) { + return obj === Object(obj); + }; + + // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp. + each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function(name) { + _['is' + name] = function(obj) { + return toString.call(obj) == '[object ' + name + ']'; + }; + }); + + // Define a fallback version of the method in browsers (ahem, IE), where + // there isn't any inspectable "Arguments" type. + if (!_.isArguments(arguments)) { + _.isArguments = function(obj) { + return !!(obj && _.has(obj, 'callee')); + }; + } + + // Optimize `isFunction` if appropriate. + if (typeof (/./) !== 'function') { + _.isFunction = function(obj) { + return typeof obj === 'function'; + }; + } + + // Is a given object a finite number? + _.isFinite = function(obj) { + return _.isNumber(obj) && isFinite(obj); + }; + + // Is the given value `NaN`? (NaN is the only number which does not equal itself). + _.isNaN = function(obj) { + return _.isNumber(obj) && obj != +obj; + }; + + // Is a given value a boolean? + _.isBoolean = function(obj) { + return obj === true || obj === false || toString.call(obj) == '[object Boolean]'; + }; + + // Is a given value equal to null? + _.isNull = function(obj) { + return obj === null; + }; + + // Is a given variable undefined? + _.isUndefined = function(obj) { + return obj === void 0; + }; + + // Shortcut function for checking if an object has a given property directly + // on itself (in other words, not on a prototype). + _.has = function(obj, key) { + return hasOwnProperty.call(obj, key); + }; + + // Utility Functions + // ----------------- + + // Run Underscore.js in *noConflict* mode, returning the `_` variable to its + // previous owner. Returns a reference to the Underscore object. + _.noConflict = function() { + root._ = previousUnderscore; + return this; + }; + + // Keep the identity function around for default iterators. + _.identity = function(value) { + return value; + }; + + // Run a function **n** times. + _.times = function(n, iterator, context) { + for (var i = 0; i < n; i++) iterator.call(context, i); + }; + + // Return a random integer between min and max (inclusive). + _.random = function(min, max) { + if (max == null) { + max = min; + min = 0; + } + return min + (0 | Math.random() * (max - min + 1)); + }; + + // List of HTML entities for escaping. + var entityMap = { + escape: { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''', + '/': '/' + } + }; + entityMap.unescape = _.invert(entityMap.escape); + + // Regexes containing the keys and values listed immediately above. + var entityRegexes = { + escape: new RegExp('[' + _.keys(entityMap.escape).join('') + ']', 'g'), + unescape: new RegExp('(' + _.keys(entityMap.unescape).join('|') + ')', 'g') + }; + + // Functions for escaping and unescaping strings to/from HTML interpolation. + _.each(['escape', 'unescape'], function(method) { + _[method] = function(string) { + if (string == null) return ''; + return ('' + string).replace(entityRegexes[method], function(match) { + return entityMap[method][match]; + }); + }; + }); + + // If the value of the named property is a function then invoke it; + // otherwise, return it. + _.result = function(object, property) { + if (object == null) return null; + var value = object[property]; + return _.isFunction(value) ? value.call(object) : value; + }; + + // Add your own custom functions to the Underscore object. + _.mixin = function(obj) { + each(_.functions(obj), function(name){ + var func = _[name] = obj[name]; + _.prototype[name] = function() { + var args = [this._wrapped]; + push.apply(args, arguments); + return result.call(this, func.apply(_, args)); + }; + }); + }; + + // Generate a unique integer id (unique within the entire client session). + // Useful for temporary DOM ids. + var idCounter = 0; + _.uniqueId = function(prefix) { + var id = idCounter++; + return prefix ? prefix + id : id; + }; + + // By default, Underscore uses ERB-style template delimiters, change the + // following template settings to use alternative delimiters. + _.templateSettings = { + evaluate : /<%([\s\S]+?)%>/g, + interpolate : /<%=([\s\S]+?)%>/g, + escape : /<%-([\s\S]+?)%>/g + }; + + // When customizing `templateSettings`, if you don't want to define an + // interpolation, evaluation or escaping regex, we need one that is + // guaranteed not to match. + var noMatch = /(.)^/; + + // Certain characters need to be escaped so that they can be put into a + // string literal. + var escapes = { + "'": "'", + '\\': '\\', + '\r': 'r', + '\n': 'n', + '\t': 't', + '\u2028': 'u2028', + '\u2029': 'u2029' + }; + + var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g; + + // JavaScript micro-templating, similar to John Resig's implementation. + // Underscore templating handles arbitrary delimiters, preserves whitespace, + // and correctly escapes quotes within interpolated code. + _.template = function(text, data, settings) { + settings = _.defaults({}, settings, _.templateSettings); + + // Combine delimiters into one regular expression via alternation. + var matcher = new RegExp([ + (settings.escape || noMatch).source, + (settings.interpolate || noMatch).source, + (settings.evaluate || noMatch).source + ].join('|') + '|$', 'g'); + + // Compile the template source, escaping string literals appropriately. + var index = 0; + var source = "__p+='"; + text.replace(matcher, function(match, escape, interpolate, evaluate, offset) { + source += text.slice(index, offset) + .replace(escaper, function(match) { return '\\' + escapes[match]; }); + source += + escape ? "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'" : + interpolate ? "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'" : + evaluate ? "';\n" + evaluate + "\n__p+='" : ''; + index = offset + match.length; + }); + source += "';\n"; + + // If a variable is not specified, place data values in local scope. + if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n'; + + source = "var __t,__p='',__j=Array.prototype.join," + + "print=function(){__p+=__j.call(arguments,'');};\n" + + source + "return __p;\n"; + + try { + var render = new Function(settings.variable || 'obj', '_', source); + } catch (e) { + e.source = source; + throw e; + } + + if (data) return render(data, _); + var template = function(data) { + return render.call(this, data, _); + }; + + // Provide the compiled function source as a convenience for precompilation. + template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}'; + + return template; + }; + + // Add a "chain" function, which will delegate to the wrapper. + _.chain = function(obj) { + return _(obj).chain(); + }; + + // OOP + // --------------- + // If Underscore is called as a function, it returns a wrapped object that + // can be used OO-style. This wrapper holds altered versions of all the + // underscore functions. Wrapped objects may be chained. + + // Helper function to continue chaining intermediate results. + var result = function(obj) { + return this._chain ? _(obj).chain() : obj; + }; + + // Add all of the Underscore functions to the wrapper object. + _.mixin(_); + + // Add all mutator Array functions to the wrapper. + each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) { + var method = ArrayProto[name]; + _.prototype[name] = function() { + var obj = this._wrapped; + method.apply(obj, arguments); + if ((name == 'shift' || name == 'splice') && obj.length === 0) delete obj[0]; + return result.call(this, obj); + }; + }); + + // Add all accessor Array functions to the wrapper. + each(['concat', 'join', 'slice'], function(name) { + var method = ArrayProto[name]; + _.prototype[name] = function() { + return result.call(this, method.apply(this._wrapped, arguments)); + }; + }); + + _.extend(_.prototype, { + + // Start chaining a wrapped Underscore object. + chain: function() { + this._chain = true; + return this; + }, + + // Extracts the result from a wrapped and chained object. + value: function() { + return this._wrapped; + } + + }); + +//}).call(this); diff --git a/tests/runner.py b/tests/runner.py index fd9f78426..fa984f051 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -10241,58 +10241,12 @@ f.close() print args, fail self.clear() try_delete(self.in_dir('a.out.js')) - Popen([PYTHON, EMCC, path_from_root('tests', 'embind', 'embind_test.cpp'), '--post-js', path_from_root('tests', 'embind', 'embind_test.js')] + args, stderr=PIPE if fail else None).communicate() + Popen([PYTHON, EMCC, path_from_root('tests', 'embind', 'embind_test.cpp'), '--post-js', path_from_root('tests', 'embind', 'underscore-1.4.2.js'), '--post-js', path_from_root('tests', 'embind', 'imvu_test_adapter.js'), '--post-js', path_from_root('tests', 'embind', 'embind.test.js')] + args, stderr=PIPE if fail else None).communicate() assert os.path.exists(self.in_dir('a.out.js')) == (not fail) if not fail: - output = run_js(self.in_dir('a.out.js')) - self.assertContained('''fixture: embind ---test: test value creation ---test: test passthrough ---test: test void return converts to undefined ---test: test booleans can be marshalled ---test: test convert double to unsigned ---test: test get length of array ---test: test add a bunch of things ---test: test sum array ---test: test strings ---test: test no memory leak when passing strings in by const reference -fixture: classes ---test: test class instance ---test: test class methods ---test: test can't call methods on deleted class instances ---test: test isinstance ---test: test can return class instances by value ---test: test can pass class instances to functions by reference ---test: test can access struct fields ---test: test can set struct fields ---test: test assignment returns value ---test: test assigning string to integer raises TypeError ---test: test can return tuples by value ---test: test tuples can contain tuples ---test: test can pass tuples by value ---test: test can return structs by value ---test: test can pass structs by value ---test: test can pass and return tuples in structs ---test: test can clone handles ---test: test can't clone if already deleted ---test: test moving handles is a clone+delete ---test: test StringHolder -fixture: embind enumerations ---test: test can compare enumeration values ---test: test repr includes enum value ---test: test instanceof ---test: test can pass and return enumeration values to functions -fixture: C++11 enum class ---test: test can compare enumeration values ---test: test repr includes enum value ---test: test instanceof ---test: test can pass and return enumeration values to functions -fixture: emval call tests ---test: test can call functions from C++ -fixture: interfaces ---test: test can wrap JS object in native interface ---test: test can pass arguments and return complicated values ---test: test can call interface methods that return nothing''', output) + output = run_js(self.in_dir('a.out.js'), stdout=PIPE, stderr=PIPE, full_output=True) + print >> sys.stderr, output + assert "FAIL" not in output def test_llvm_nativizer(self): try: From 8b1b35d8e34496a81967b2d08e2f7900111bba5e Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Mon, 1 Apr 2013 15:21:08 -0700 Subject: [PATCH 232/258] Move context generation/copying out of JavaScript and into C++, where we'll be able to do some cool things in the future. --- src/embind/embind.js | 53 ++++------ system/include/emscripten/bind.h | 167 +++++++++++++++---------------- 2 files changed, 98 insertions(+), 122 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index e0abe6f1d..f850c73f2 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -386,26 +386,15 @@ function __embind_register_tuple(rawType, name, rawConstructor, rawDestructor) { }); } -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( rawTupleType, rawType, getter, setter, - memberPointerSize, - memberPointer + context ) { getter = FUNCTION_TABLE[getter]; setter = FUNCTION_TABLE[setter]; - memberPointer = copyMemberPointer(memberPointer, memberPointerSize); var tupleType = requireRegisteredType(rawTupleType, 'tuple'); var index = tupleType.elements.length; @@ -416,11 +405,11 @@ function __embind_register_tuple_element( type = type[0]; tupleType.elements[index] = { read: function(ptr) { - return type.fromWireType(getter(ptr, memberPointer)); + return type.fromWireType(getter(context, ptr)); }, write: function(ptr, o) { var destructors = []; - setter(ptr, memberPointer, type.toWireType(destructors, o)); + setter(context, ptr, type.toWireType(destructors, o)); runDestructors(destructors); } }; @@ -432,29 +421,27 @@ function __embind_register_tuple_element_accessor( rawTupleType, rawElementType, rawStaticGetter, - getterSize, - getter, + getterContext, rawStaticSetter, - setterSize, - setter + setterContext ) { var tupleType = requireRegisteredType(rawTupleType, 'tuple'); rawStaticGetter = FUNCTION_TABLE[rawStaticGetter]; - getter = copyMemberPointer(getter, getterSize); rawStaticSetter = FUNCTION_TABLE[rawStaticSetter]; - setter = copyMemberPointer(setter, setterSize); whenDependentTypesAreResolved([], [rawElementType], function(elementType) { elementType = elementType[0]; tupleType.elements.push({ read: function(ptr) { - return elementType.fromWireType(rawStaticGetter(ptr, HEAP32[getter >> 2])); + return elementType.fromWireType(rawStaticGetter( + getterContext, + ptr)); }, write: function(ptr, o) { var destructors = []; rawStaticSetter( + setterContext, ptr, - HEAP32[setter >> 2], elementType.toWireType(destructors, o)); runDestructors(destructors); } @@ -512,24 +499,22 @@ function __embind_register_struct_field( rawFieldType, rawGetter, rawSetter, - memberPointerSize, - memberPointer + context ) { var structType = requireRegisteredType(rawStructType, 'struct'); fieldName = Pointer_stringify(fieldName); rawGetter = FUNCTION_TABLE[rawGetter]; rawSetter = FUNCTION_TABLE[rawSetter]; - memberPointer = copyMemberPointer(memberPointer, memberPointerSize); // TODO: test incomplete registration of value structs whenDependentTypesAreResolved([], [rawFieldType], function(fieldType) { fieldType = fieldType[0]; structType.fields[fieldName] = { read: function(ptr) { - return fieldType.fromWireType(rawGetter(ptr, memberPointer)); + return fieldType.fromWireType(rawGetter(context, ptr)); }, write: function(ptr, o) { var destructors = []; - rawSetter(ptr, memberPointer, fieldType.toWireType(destructors, o)); + rawSetter(context, ptr, fieldType.toWireType(destructors, o)); runDestructors(destructors); } }; @@ -989,13 +974,11 @@ function __embind_register_class_function( argCount, rawArgTypesAddr, // [ReturnType, ThisType, Args...] rawInvoker, - memberFunctionSize, - memberFunction + context ) { var rawArgTypes = heap32VectorToArray(argCount, rawArgTypesAddr); methodName = Pointer_stringify(methodName); rawInvoker = FUNCTION_TABLE[rawInvoker]; - memberFunction = copyMemberPointer(memberFunction, memberFunctionSize); whenDependentTypesAreResolved([], [rawClassType], function(classType) { classType = classType[0]; @@ -1015,7 +998,7 @@ function __embind_register_class_function( var destructors = []; var args = new Array(argCount + 1); - args[0] = memberFunction; + args[0] = context; args[1] = argTypes[1].toWireType(destructors, this); for (var i = 2; i < argCount; ++i) { args[i] = argTypes[i].toWireType(destructors, arguments[i - 2]); @@ -1064,13 +1047,11 @@ function __embind_register_class_property( rawFieldType, getter, setter, - memberPointerSize, - memberPointer + context ) { fieldName = Pointer_stringify(fieldName); getter = FUNCTION_TABLE[getter]; setter = FUNCTION_TABLE[setter]; - memberPointer = copyMemberPointer(memberPointer, memberPointerSize); whenDependentTypesAreResolved([], [rawClassType], function(classType) { classType = classType[0]; var humanName = classType.name + '.' + fieldName; @@ -1091,12 +1072,12 @@ function __embind_register_class_property( Object.defineProperty(classType.registeredClass.instancePrototype, fieldName, { get: function() { var ptr = validateThis(this, classType, humanName + ' getter'); - return fieldType.fromWireType(getter(ptr, memberPointer)); + return fieldType.fromWireType(getter(context, ptr)); }, set: function(v) { var ptr = validateThis(this, classType, humanName + ' setter'); var destructors = []; - setter(ptr, memberPointer, fieldType.toWireType(destructors, v)); + setter(context, ptr, fieldType.toWireType(destructors, v)); runDestructors(destructors); }, enumerable: true diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index 211c67c00..947f0b307 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -73,18 +73,15 @@ namespace emscripten { TYPEID elementType, GenericFunction getter, GenericFunction setter, - size_t memberPointerSize, - void* memberPointer); + void* context); void _embind_register_tuple_element_accessor( TYPEID tupleType, TYPEID elementType, GenericFunction staticGetter, - size_t getterSize, - void* getter, + void* getterContext, GenericFunction staticSetter, - size_t setterSize, - void* setter); + void* setterContext); void _embind_register_struct( TYPEID structType, @@ -98,8 +95,7 @@ namespace emscripten { TYPEID fieldType, GenericFunction getter, GenericFunction setter, - size_t memberPointerSize, - void* memberPointer); + void* context); void _embind_register_smart_ptr( TYPEID pointerType, @@ -135,8 +131,7 @@ namespace emscripten { unsigned argCount, TYPEID argTypes[], GenericFunction invoker, - size_t memberFunctionSize, - void* memberFunction); + void* context); void _embind_register_class_property( TYPEID classType, @@ -144,8 +139,7 @@ namespace emscripten { TYPEID fieldType, GenericFunction getter, GenericFunction setter, - size_t memberPointerSize, - void* memberPointer); + void* context); void _embind_register_class_class_function( TYPEID classType, @@ -354,15 +348,15 @@ namespace emscripten { typedef typename MemberBinding::WireType WireType; static WireType getWire( - const ClassType& ptr, - const MemberPointer& field + const MemberPointer& field, + const ClassType& ptr ) { return MemberBinding::toWireType(ptr.*field); } static void setWire( - ClassType& ptr, const MemberPointer& field, + ClassType& ptr, WireType value ) { ptr.*field = MemberBinding::fromWireType(value); @@ -370,22 +364,31 @@ namespace emscripten { template static WireType propertyGet( - const ClassType& ptr, - const Getter& getter + const Getter& getter, + const ClassType& ptr ) { return MemberBinding::toWireType(getter(ptr)); } template static void propertySet( - ClassType& ptr, const Setter& setter, + ClassType& ptr, WireType value ) { setter(ptr, MemberBinding::fromWireType(value)); } }; + // TODO: This could do a reinterpret-cast if sizeof(T) === sizeof(void*) + template + inline void* getContext(const T& t) { + // not a leak because this is called once per binding + void* p = malloc(sizeof(T)); + assert(p); + memcpy(p, &t, sizeof(T)); + return p; + } } //////////////////////////////////////////////////////////////////////////////// @@ -396,79 +399,75 @@ namespace emscripten { class value_tuple { public: value_tuple(const char* name) { - internal::_embind_register_tuple( - internal::TypeID::get(), + using namespace internal; + _embind_register_tuple( + TypeID::get(), name, - reinterpret_cast(&internal::raw_constructor), - reinterpret_cast(&internal::raw_destructor)); + reinterpret_cast(&raw_constructor), + reinterpret_cast(&raw_destructor)); } template value_tuple& element(ElementType ClassType::*field) { - internal::_embind_register_tuple_element( - internal::TypeID::get(), - internal::TypeID::get(), - reinterpret_cast(&internal::MemberAccess::getWire), - reinterpret_cast(&internal::MemberAccess::setWire), - sizeof(field), - &field); - + using namespace internal; + _embind_register_tuple_element( + TypeID::get(), + TypeID::get(), + reinterpret_cast(&MemberAccess::getWire), + reinterpret_cast(&MemberAccess::setWire), + getContext(field)); return *this; } template value_tuple& element(ElementType (*getter)(const ClassType&), void (*setter)(ClassType&, ElementType)) { - internal::_embind_register_tuple_element_accessor( - internal::TypeID::get(), - internal::TypeID::get(), - reinterpret_cast(&internal::MemberAccess::template propertyGet), - sizeof(getter), - &getter, - reinterpret_cast(&internal::MemberAccess::template propertySet), - sizeof(setter), - &setter); + using namespace internal; + _embind_register_tuple_element_accessor( + TypeID::get(), + TypeID::get(), + reinterpret_cast(&MemberAccess::template propertyGet), + getContext(getter), + reinterpret_cast(&MemberAccess::template propertySet), + getContext(setter)); return *this; } template value_tuple& element(ElementType (*getter)(const ClassType&), void (*setter)(ClassType&, const ElementType&)) { - internal::_embind_register_tuple_element_accessor( - internal::TypeID::get(), - internal::TypeID::get(), - reinterpret_cast(&internal::MemberAccess::template propertyGet), - sizeof(getter), - &getter, - reinterpret_cast(&internal::MemberAccess::template propertySet), - sizeof(setter), - &setter); + using namespace internal; + _embind_register_tuple_element_accessor( + TypeID::get(), + TypeID::get(), + reinterpret_cast(&MemberAccess::template propertyGet), + getContext(getter), + reinterpret_cast(&MemberAccess::template propertySet), + getContext(setter)); return *this; } template value_tuple& element(ElementType (*getter)(const ClassType&), void (*setter)(ClassType&, const ElementType&&)) { - internal::_embind_register_tuple_element_accessor( - internal::TypeID::get(), - internal::TypeID::get(), - reinterpret_cast(&internal::MemberAccess::template propertyGet), - sizeof(getter), - &getter, - reinterpret_cast(&internal::MemberAccess::template propertySet), - sizeof(setter), - &setter); + using namespace internal; + _embind_register_tuple_element_accessor( + TypeID::get(), + TypeID::get(), + reinterpret_cast(&MemberAccess::template propertyGet), + getContext(getter), + reinterpret_cast(&MemberAccess::template propertySet), + getContext(setter)); return *this; } template value_tuple& element(ElementType (*getter)(const ClassType&), void (*setter)(ClassType&, ElementType&)) { - internal::_embind_register_tuple_element_accessor( - internal::TypeID::get(), - internal::TypeID::get(), - reinterpret_cast(&internal::MemberAccess::template propertyGet), - sizeof(getter), - &getter, - reinterpret_cast(&internal::MemberAccess::template propertySet), - sizeof(setter), - &setter); + using namespace internal; + _embind_register_tuple_element_accessor( + TypeID::get(), + TypeID::get(), + reinterpret_cast(&MemberAccess::template propertyGet), + getContext(getter), + reinterpret_cast(&MemberAccess::template propertySet), + getContext(setter)); return *this; } }; @@ -481,24 +480,24 @@ namespace emscripten { class value_struct { public: value_struct(const char* name) { - internal::_embind_register_struct( - internal::TypeID::get(), + using namespace internal; + _embind_register_struct( + TypeID::get(), name, - reinterpret_cast(&internal::raw_constructor), - reinterpret_cast(&internal::raw_destructor)); + reinterpret_cast(&raw_constructor), + reinterpret_cast(&raw_destructor)); } template value_struct& field(const char* fieldName, FieldType ClassType::*field) { - internal::_embind_register_struct_field( - internal::TypeID::get(), + using namespace internal; + _embind_register_struct_field( + TypeID::get(), fieldName, - internal::TypeID::get(), - reinterpret_cast(&internal::MemberAccess::getWire), - reinterpret_cast(&internal::MemberAccess::setWire), - sizeof(field), - &field); - + TypeID::get(), + reinterpret_cast(&MemberAccess::getWire), + reinterpret_cast(&MemberAccess::setWire), + getContext(field)); return *this; } }; @@ -783,8 +782,7 @@ namespace emscripten { args.count, args.types, reinterpret_cast(&MethodInvoker::invoke), - sizeof(memberFunction), - &memberFunction); + getContext(memberFunction)); return *this; } @@ -799,8 +797,7 @@ namespace emscripten { args.count, args.types, reinterpret_cast(&MethodInvoker::invoke), - sizeof(memberFunction), - &memberFunction); + getContext(memberFunction)); return *this; } @@ -815,8 +812,7 @@ namespace emscripten { args.count, args.types, reinterpret_cast(&FunctionInvoker::invoke), - sizeof(function), - &function); + getContext(function)); return *this; } @@ -830,8 +826,7 @@ namespace emscripten { TypeID::get(), reinterpret_cast(&MemberAccess::getWire), reinterpret_cast(&MemberAccess::setWire), - sizeof(field), - &field); + getContext(field)); return *this; } From e30dd3017a318d1f5862bd3688679c687c66d621 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Mon, 1 Apr 2013 17:42:50 -0700 Subject: [PATCH 233/258] Restructure and generalize tuple accessors. --- src/embind/embind.js | 28 ++++++++------- system/include/emscripten/bind.h | 60 ++++++++------------------------ 2 files changed, 31 insertions(+), 57 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index f850c73f2..845bf98bb 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -419,30 +419,34 @@ function __embind_register_tuple_element( function __embind_register_tuple_element_accessor( rawTupleType, - rawElementType, - rawStaticGetter, + getterReturnType, + getter, getterContext, - rawStaticSetter, + setterArgumentType, + setter, setterContext ) { var tupleType = requireRegisteredType(rawTupleType, 'tuple'); - rawStaticGetter = FUNCTION_TABLE[rawStaticGetter]; - rawStaticSetter = FUNCTION_TABLE[rawStaticSetter]; + getter = FUNCTION_TABLE[getter]; + setter = FUNCTION_TABLE[setter]; - whenDependentTypesAreResolved([], [rawElementType], function(elementType) { - elementType = elementType[0]; + // TODO: test incomplete registration of value tuples + whenDependentTypesAreResolved([], [getterReturnType, setterArgumentType], function(types) { + var getterReturnType = types[0]; + var setterArgumentType = types[1]; tupleType.elements.push({ read: function(ptr) { - return elementType.fromWireType(rawStaticGetter( - getterContext, - ptr)); + return getterReturnType.fromWireType( + getter( + getterContext, + ptr)); }, write: function(ptr, o) { var destructors = []; - rawStaticSetter( + setter( setterContext, ptr, - elementType.toWireType(destructors, o)); + setterArgumentType.toWireType(destructors, o)); runDestructors(destructors); } }); diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index 947f0b307..a002ca239 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -77,9 +77,10 @@ namespace emscripten { void _embind_register_tuple_element_accessor( TYPEID tupleType, - TYPEID elementType, + TYPEID getterReturnType, GenericFunction staticGetter, void* getterContext, + TYPEID setterArgumentType, GenericFunction staticSetter, void* setterContext); @@ -419,54 +420,23 @@ namespace emscripten { return *this; } - template - value_tuple& element(ElementType (*getter)(const ClassType&), void (*setter)(ClassType&, ElementType)) { + template + value_tuple& element( + GetterReturnType (*getter)(const ClassType&), + void (*setter)(ClassType&, SetterArgumentType) + ) { using namespace internal; _embind_register_tuple_element_accessor( TypeID::get(), - TypeID::get(), - reinterpret_cast(&MemberAccess::template propertyGet), + TypeID::get(), + reinterpret_cast( + &MemberAccess + ::template propertyGet), getContext(getter), - reinterpret_cast(&MemberAccess::template propertySet), - getContext(setter)); - return *this; - } - - template - value_tuple& element(ElementType (*getter)(const ClassType&), void (*setter)(ClassType&, const ElementType&)) { - using namespace internal; - _embind_register_tuple_element_accessor( - TypeID::get(), - TypeID::get(), - reinterpret_cast(&MemberAccess::template propertyGet), - getContext(getter), - reinterpret_cast(&MemberAccess::template propertySet), - getContext(setter)); - return *this; - } - - template - value_tuple& element(ElementType (*getter)(const ClassType&), void (*setter)(ClassType&, const ElementType&&)) { - using namespace internal; - _embind_register_tuple_element_accessor( - TypeID::get(), - TypeID::get(), - reinterpret_cast(&MemberAccess::template propertyGet), - getContext(getter), - reinterpret_cast(&MemberAccess::template propertySet), - getContext(setter)); - return *this; - } - - template - value_tuple& element(ElementType (*getter)(const ClassType&), void (*setter)(ClassType&, ElementType&)) { - using namespace internal; - _embind_register_tuple_element_accessor( - TypeID::get(), - TypeID::get(), - reinterpret_cast(&MemberAccess::template propertyGet), - getContext(getter), - reinterpret_cast(&MemberAccess::template propertySet), + TypeID::get(), + reinterpret_cast( + &MemberAccess + ::template propertySet), getContext(setter)); return *this; } From de48fdc5b028df627b7070c17d37479654f71a13 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Mon, 1 Apr 2013 17:54:55 -0700 Subject: [PATCH 234/258] Further generalize support for tuple elements, and in the meantime, fix a bug that made it into one variant of the function but not the other... --- src/embind/embind.js | 38 +++++--------------------------- system/include/emscripten/bind.h | 17 ++++++-------- 2 files changed, 12 insertions(+), 43 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 845bf98bb..3daa4b018 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -387,37 +387,6 @@ function __embind_register_tuple(rawType, name, rawConstructor, rawDestructor) { } function __embind_register_tuple_element( - rawTupleType, - rawType, - getter, - setter, - context -) { - getter = FUNCTION_TABLE[getter]; - setter = FUNCTION_TABLE[setter]; - var tupleType = requireRegisteredType(rawTupleType, 'tuple'); - - var index = tupleType.elements.length; - tupleType.elements.push(undefined); - - // TODO: test incomplete registration of value tuples - whenDependentTypesAreResolved([], [rawType], function(type) { - type = type[0]; - tupleType.elements[index] = { - read: function(ptr) { - return type.fromWireType(getter(context, ptr)); - }, - write: function(ptr, o) { - var destructors = []; - setter(context, ptr, type.toWireType(destructors, o)); - runDestructors(destructors); - } - }; - return []; - }); -} - -function __embind_register_tuple_element_accessor( rawTupleType, getterReturnType, getter, @@ -430,11 +399,14 @@ function __embind_register_tuple_element_accessor( getter = FUNCTION_TABLE[getter]; setter = FUNCTION_TABLE[setter]; + var index = tupleType.elements.length; + tupleType.elements.push(undefined); + // TODO: test incomplete registration of value tuples whenDependentTypesAreResolved([], [getterReturnType, setterArgumentType], function(types) { var getterReturnType = types[0]; var setterArgumentType = types[1]; - tupleType.elements.push({ + tupleType.elements[index] = { read: function(ptr) { return getterReturnType.fromWireType( getter( @@ -449,7 +421,7 @@ function __embind_register_tuple_element_accessor( setterArgumentType.toWireType(destructors, o)); runDestructors(destructors); } - }); + }; return []; }); } diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index a002ca239..a5530744b 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -69,13 +69,6 @@ namespace emscripten { GenericFunction destructor); void _embind_register_tuple_element( - TYPEID tupleType, - TYPEID elementType, - GenericFunction getter, - GenericFunction setter, - void* context); - - void _embind_register_tuple_element_accessor( TYPEID tupleType, TYPEID getterReturnType, GenericFunction staticGetter, @@ -414,8 +407,12 @@ namespace emscripten { _embind_register_tuple_element( TypeID::get(), TypeID::get(), - reinterpret_cast(&MemberAccess::getWire), - reinterpret_cast(&MemberAccess::setWire), + reinterpret_cast( + &MemberAccess::getWire), + getContext(field), + TypeID::get(), + reinterpret_cast( + &MemberAccess::setWire), getContext(field)); return *this; } @@ -426,7 +423,7 @@ namespace emscripten { void (*setter)(ClassType&, SetterArgumentType) ) { using namespace internal; - _embind_register_tuple_element_accessor( + _embind_register_tuple_element( TypeID::get(), TypeID::get(), reinterpret_cast( From e5af621a189f729d8c1513f739a6a085adf39842 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Mon, 1 Apr 2013 18:47:55 -0700 Subject: [PATCH 235/258] Generalize support for struct fields --- src/embind/embind.js | 29 +++++++++++--------- system/include/emscripten/bind.h | 45 ++++++++++++++++++++++++++------ tests/embind/embind_test.cpp | 10 ++++++- 3 files changed, 63 insertions(+), 21 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 3daa4b018..16e29d9e2 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -470,27 +470,32 @@ function __embind_register_struct( } function __embind_register_struct_field( - rawStructType, + structType, fieldName, - rawFieldType, - rawGetter, - rawSetter, - context + getterReturnType, + getter, + getterContext, + setterArgumentType, + setter, + setterContext ) { - var structType = requireRegisteredType(rawStructType, 'struct'); + structType = requireRegisteredType(structType, 'struct'); fieldName = Pointer_stringify(fieldName); - rawGetter = FUNCTION_TABLE[rawGetter]; - rawSetter = FUNCTION_TABLE[rawSetter]; + getter = FUNCTION_TABLE[getter]; + setter = FUNCTION_TABLE[setter]; + // TODO: test incomplete registration of value structs - whenDependentTypesAreResolved([], [rawFieldType], function(fieldType) { - fieldType = fieldType[0]; + whenDependentTypesAreResolved([], [getterReturnType, setterArgumentType], function(types) { + var getterReturnType = types[0]; + var setterArgumentType = types[1]; structType.fields[fieldName] = { read: function(ptr) { - return fieldType.fromWireType(rawGetter(context, ptr)); + return getterReturnType.fromWireType( + getter(getterContext, ptr)); }, write: function(ptr, o) { var destructors = []; - rawSetter(context, ptr, fieldType.toWireType(destructors, o)); + setter(setterContext, ptr, setterArgumentType.toWireType(destructors, o)); runDestructors(destructors); } }; diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index a5530744b..fe0c97119 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -71,25 +71,27 @@ namespace emscripten { void _embind_register_tuple_element( TYPEID tupleType, TYPEID getterReturnType, - GenericFunction staticGetter, + GenericFunction getter, void* getterContext, TYPEID setterArgumentType, - GenericFunction staticSetter, + GenericFunction setter, void* setterContext); void _embind_register_struct( TYPEID structType, - const char* name, + const char* fieldName, GenericFunction constructor, GenericFunction destructor); void _embind_register_struct_field( TYPEID structType, - const char* name, - TYPEID fieldType, + const char* fieldName, + TYPEID getterReturnType, GenericFunction getter, + void* getterContext, + TYPEID setterArgumentType, GenericFunction setter, - void* context); + void* setterContext); void _embind_register_smart_ptr( TYPEID pointerType, @@ -462,11 +464,38 @@ namespace emscripten { TypeID::get(), fieldName, TypeID::get(), - reinterpret_cast(&MemberAccess::getWire), - reinterpret_cast(&MemberAccess::setWire), + reinterpret_cast( + &MemberAccess::getWire), + getContext(field), + TypeID::get(), + reinterpret_cast( + &MemberAccess::setWire), getContext(field)); return *this; } + + template + value_struct& field( + const char* fieldName, + GetterReturnType (*getter)(const ClassType&), + void (*setter)(ClassType&, SetterArgumentType) + ) { + using namespace internal; + _embind_register_struct_field( + TypeID::get(), + fieldName, + TypeID::get(), + reinterpret_cast( + &MemberAccess + ::template propertyGet), + getContext(getter), + TypeID::get(), + reinterpret_cast( + &MemberAccess + ::template propertySet), + getContext(setter)); + return *this; + } }; //////////////////////////////////////////////////////////////////////////////// diff --git a/tests/embind/embind_test.cpp b/tests/embind/embind_test.cpp index e9feccfef..156c86808 100644 --- a/tests/embind/embind_test.cpp +++ b/tests/embind/embind_test.cpp @@ -758,6 +758,14 @@ struct StructVector { float x, y, z; }; +float readStructVectorZ(const StructVector& v) { + return v.z; +} + +void writeStructVectorZ(StructVector& v, float z) { + v.z = z; +} + StructVector emval_test_return_StructVector() { StructVector v; v.x = 1; @@ -1354,7 +1362,7 @@ EMSCRIPTEN_BINDINGS(tests) { value_struct("StructVector") .field("x", &StructVector::x) .field("y", &StructVector::y) - .field("z", &StructVector::z) + .field("z", &readStructVectorZ, &writeStructVectorZ) ; function("emval_test_return_StructVector", &emval_test_return_StructVector); From ecb01ee4b190a7351e8240df103614e705540f90 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Mon, 1 Apr 2013 19:13:17 -0700 Subject: [PATCH 236/258] Support accessing value struct and tuple fields and elements via base class pointers. --- system/include/emscripten/bind.h | 72 ++++++++++++++++++++------------ tests/embind/embind_test.cpp | 32 +++++++------- 2 files changed, 61 insertions(+), 43 deletions(-) diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index fe0c97119..1828d95c1 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -337,12 +337,13 @@ namespace emscripten { } }; - template + template struct MemberAccess { - typedef MemberType ClassType::*MemberPointer; + typedef MemberType InstanceType::*MemberPointer; typedef internal::BindingType MemberBinding; typedef typename MemberBinding::WireType WireType; + template static WireType getWire( const MemberPointer& field, const ClassType& ptr @@ -350,6 +351,7 @@ namespace emscripten { return MemberBinding::toWireType(ptr.*field); } + template static void setWire( const MemberPointer& field, ClassType& ptr, @@ -357,6 +359,12 @@ namespace emscripten { ) { ptr.*field = MemberBinding::fromWireType(value); } + }; + + template + struct PropertyAccess { + typedef internal::BindingType MemberBinding; + typedef typename MemberBinding::WireType WireType; template static WireType propertyGet( @@ -403,39 +411,45 @@ namespace emscripten { reinterpret_cast(&raw_destructor)); } - template - value_tuple& element(ElementType ClassType::*field) { + template + value_tuple& element(ElementType InstanceType::*field) { using namespace internal; _embind_register_tuple_element( TypeID::get(), TypeID::get(), reinterpret_cast( - &MemberAccess::getWire), + &MemberAccess + ::template getWire), getContext(field), TypeID::get(), reinterpret_cast( - &MemberAccess::setWire), + &MemberAccess + ::template setWire), getContext(field)); return *this; } - template + template< + typename GetterReturnType, + typename GetterThisType, + typename SetterArgumentType, + typename SetterThisType> value_tuple& element( - GetterReturnType (*getter)(const ClassType&), - void (*setter)(ClassType&, SetterArgumentType) + GetterReturnType (*getter)(const GetterThisType&), + void (*setter)(SetterThisType&, SetterArgumentType) ) { using namespace internal; _embind_register_tuple_element( TypeID::get(), TypeID::get(), reinterpret_cast( - &MemberAccess - ::template propertyGet), + &PropertyAccess + ::template propertyGet), getContext(getter), TypeID::get(), reinterpret_cast( - &MemberAccess - ::template propertySet), + &PropertyAccess + ::template propertySet), getContext(setter)); return *this; } @@ -457,28 +471,34 @@ namespace emscripten { reinterpret_cast(&raw_destructor)); } - template - value_struct& field(const char* fieldName, FieldType ClassType::*field) { + template + value_struct& field(const char* fieldName, FieldType InstanceType::*field) { using namespace internal; _embind_register_struct_field( TypeID::get(), fieldName, TypeID::get(), reinterpret_cast( - &MemberAccess::getWire), + &MemberAccess + ::template getWire), getContext(field), TypeID::get(), reinterpret_cast( - &MemberAccess::setWire), + &MemberAccess + ::template setWire), getContext(field)); return *this; } - template + template< + typename GetterReturnType, + typename GetterThisType, + typename SetterArgumentType, + typename SetterThisType> value_struct& field( const char* fieldName, - GetterReturnType (*getter)(const ClassType&), - void (*setter)(ClassType&, SetterArgumentType) + GetterReturnType (*getter)(const GetterThisType&), + void (*setter)(SetterThisType&, SetterArgumentType) ) { using namespace internal; _embind_register_struct_field( @@ -486,13 +506,13 @@ namespace emscripten { fieldName, TypeID::get(), reinterpret_cast( - &MemberAccess - ::template propertyGet), + &PropertyAccess + ::template propertyGet), getContext(getter), TypeID::get(), reinterpret_cast( - &MemberAccess - ::template propertySet), + &PropertyAccess + ::template propertySet), getContext(setter)); return *this; } @@ -820,8 +840,8 @@ namespace emscripten { TypeID::get(), fieldName, TypeID::get(), - reinterpret_cast(&MemberAccess::getWire), - reinterpret_cast(&MemberAccess::setWire), + reinterpret_cast(&MemberAccess::template getWire), + reinterpret_cast(&MemberAccess::template setWire), getContext(field)); return *this; } diff --git a/tests/embind/embind_test.cpp b/tests/embind/embind_test.cpp index 156c86808..229d30d16 100644 --- a/tests/embind/embind_test.cpp +++ b/tests/embind/embind_test.cpp @@ -720,15 +720,25 @@ std::map embind_test_get_string_int_map() { return m; }; -struct TupleVector { +struct Vector { float x, y, z; }; -float readTupleVectorZ(const TupleVector& v) { +struct DummyDataToTestPointerAdjustment { + std::string dummy; +}; + +struct TupleVector : DummyDataToTestPointerAdjustment, Vector { +}; + +struct StructVector : DummyDataToTestPointerAdjustment, Vector { +}; + +float readVectorZ(const Vector& v) { return v.z; } -void writeTupleVectorZ(TupleVector& v, float z) { +void writeVectorZ(Vector& v, float z) { v.z = z; } @@ -754,18 +764,6 @@ TupleVectorTuple emval_test_return_TupleVectorTuple() { return cvt; } -struct StructVector { - float x, y, z; -}; - -float readStructVectorZ(const StructVector& v) { - return v.z; -} - -void writeStructVectorZ(StructVector& v, float z) { - v.z = z; -} - StructVector emval_test_return_StructVector() { StructVector v; v.x = 1; @@ -1347,7 +1345,7 @@ EMSCRIPTEN_BINDINGS(tests) { .element(&TupleVector::x) .element(&TupleVector::y) //.element(&TupleVector::z) - .element(&readTupleVectorZ, &writeTupleVectorZ) + .element(&readVectorZ, &writeVectorZ) ; function("emval_test_return_TupleVector", &emval_test_return_TupleVector); @@ -1362,7 +1360,7 @@ EMSCRIPTEN_BINDINGS(tests) { value_struct("StructVector") .field("x", &StructVector::x) .field("y", &StructVector::y) - .field("z", &readStructVectorZ, &writeStructVectorZ) + .field("z", &readVectorZ, &writeVectorZ) ; function("emval_test_return_StructVector", &emval_test_return_StructVector); From fbcb6a4b34c0d0e4b16cbcdfccdb11a9e3a6b080 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Mon, 1 Apr 2013 19:44:01 -0700 Subject: [PATCH 237/258] add value_struct and value_tuple support for member function getters and setters. --- system/include/emscripten/bind.h | 81 +++++++++++++++++++++++++++++--- tests/embind/embind_test.cpp | 12 +++-- 2 files changed, 84 insertions(+), 9 deletions(-) diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index 1828d95c1..6e420a293 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -367,21 +367,38 @@ namespace emscripten { typedef typename MemberBinding::WireType WireType; template - static WireType propertyGet( + static WireType getByFunction( const Getter& getter, const ClassType& ptr ) { return MemberBinding::toWireType(getter(ptr)); } + template + static WireType getByMemberFunction( + const Getter& getter, + const ClassType& ptr + ) { + return MemberBinding::toWireType((ptr.*getter)()); + } + template - static void propertySet( + static void setByFunction( const Setter& setter, ClassType& ptr, WireType value ) { setter(ptr, MemberBinding::fromWireType(value)); } + + template + static void setByMemberFunction( + const Setter& setter, + ClassType& ptr, + WireType value + ) { + (ptr.*setter)(MemberBinding::fromWireType(value)); + } }; // TODO: This could do a reinterpret-cast if sizeof(T) === sizeof(void*) @@ -429,6 +446,31 @@ namespace emscripten { return *this; } + template< + typename GetterReturnType, + typename GetterThisType, + typename SetterArgumentType, + typename SetterThisType> + value_tuple& element( + GetterReturnType (GetterThisType::*getter)() const, + void (SetterThisType::*setter)(SetterArgumentType) + ) { + using namespace internal; + _embind_register_tuple_element( + TypeID::get(), + TypeID::get(), + reinterpret_cast( + &PropertyAccess + ::template getByMemberFunction), + getContext(getter), + TypeID::get(), + reinterpret_cast( + &PropertyAccess + ::template setByMemberFunction), + getContext(setter)); + return *this; + } + template< typename GetterReturnType, typename GetterThisType, @@ -444,12 +486,12 @@ namespace emscripten { TypeID::get(), reinterpret_cast( &PropertyAccess - ::template propertyGet), + ::template getByFunction), getContext(getter), TypeID::get(), reinterpret_cast( &PropertyAccess - ::template propertySet), + ::template setByFunction), getContext(setter)); return *this; } @@ -490,6 +532,33 @@ namespace emscripten { return *this; } + template< + typename GetterReturnType, + typename GetterThisType, + typename SetterArgumentType, + typename SetterThisType> + value_struct& field( + const char* fieldName, + GetterReturnType (GetterThisType::*getter)() const, + void (SetterThisType::*setter)(SetterArgumentType) + ) { + using namespace internal; + _embind_register_struct_field( + TypeID::get(), + fieldName, + TypeID::get(), + reinterpret_cast( + &PropertyAccess + ::template getByMemberFunction), + getContext(getter), + TypeID::get(), + reinterpret_cast( + &PropertyAccess + ::template setByMemberFunction), + getContext(setter)); + return *this; + } + template< typename GetterReturnType, typename GetterThisType, @@ -507,12 +576,12 @@ namespace emscripten { TypeID::get(), reinterpret_cast( &PropertyAccess - ::template propertyGet), + ::template getByFunction), getContext(getter), TypeID::get(), reinterpret_cast( &PropertyAccess - ::template propertySet), + ::template setByFunction), getContext(setter)); return *this; } diff --git a/tests/embind/embind_test.cpp b/tests/embind/embind_test.cpp index 229d30d16..470309aa2 100644 --- a/tests/embind/embind_test.cpp +++ b/tests/embind/embind_test.cpp @@ -722,6 +722,13 @@ std::map embind_test_get_string_int_map() { struct Vector { float x, y, z; + + float getY() const { + return y; + } + void setY(float _y) { + y = _y; + } }; struct DummyDataToTestPointerAdjustment { @@ -1343,8 +1350,7 @@ EMSCRIPTEN_BINDINGS(tests) { value_tuple("TupleVector") .element(&TupleVector::x) - .element(&TupleVector::y) - //.element(&TupleVector::z) + .element(&Vector::getY, &Vector::setY) .element(&readVectorZ, &writeVectorZ) ; @@ -1359,7 +1365,7 @@ EMSCRIPTEN_BINDINGS(tests) { value_struct("StructVector") .field("x", &StructVector::x) - .field("y", &StructVector::y) + .field("y", &Vector::getY, &Vector::setY) .field("z", &readVectorZ, &writeVectorZ) ; From def5681c4f191c5e273765624b200619a50cf805 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Tue, 2 Apr 2013 02:24:21 -0700 Subject: [PATCH 238/258] Generalize and support mix-and-match member/non-member getter/setter for value types. --- system/include/emscripten/bind.h | 227 ++++++++++++++----------------- 1 file changed, 99 insertions(+), 128 deletions(-) diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index 6e420a293..3161289d6 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -361,46 +361,6 @@ namespace emscripten { } }; - template - struct PropertyAccess { - typedef internal::BindingType MemberBinding; - typedef typename MemberBinding::WireType WireType; - - template - static WireType getByFunction( - const Getter& getter, - const ClassType& ptr - ) { - return MemberBinding::toWireType(getter(ptr)); - } - - template - static WireType getByMemberFunction( - const Getter& getter, - const ClassType& ptr - ) { - return MemberBinding::toWireType((ptr.*getter)()); - } - - template - static void setByFunction( - const Setter& setter, - ClassType& ptr, - WireType value - ) { - setter(ptr, MemberBinding::fromWireType(value)); - } - - template - static void setByMemberFunction( - const Setter& setter, - ClassType& ptr, - WireType value - ) { - (ptr.*setter)(MemberBinding::fromWireType(value)); - } - }; - // TODO: This could do a reinterpret-cast if sizeof(T) === sizeof(void*) template inline void* getContext(const T& t) { @@ -410,6 +370,84 @@ namespace emscripten { memcpy(p, &t, sizeof(T)); return p; } + + template + struct GetterPolicy; + + template + struct GetterPolicy { + typedef GetterReturnType ReturnType; + typedef GetterReturnType (GetterThisType::*Context)() const; + + typedef internal::BindingType Binding; + typedef typename Binding::WireType WireType; + + template + static WireType get(const Context& context, const ClassType& ptr) { + return Binding::toWireType((ptr.*context)()); + } + + static void* getContext(Context context) { + return internal::getContext(context); + } + }; + + template + struct GetterPolicy { + typedef GetterReturnType ReturnType; + typedef GetterReturnType (*Context)(const GetterThisType&); + + typedef internal::BindingType Binding; + typedef typename Binding::WireType WireType; + + template + static WireType get(const Context& context, const ClassType& ptr) { + return Binding::toWireType(context(ptr)); + } + + static void* getContext(Context context) { + return internal::getContext(context); + } + }; + + template + struct SetterPolicy; + + template + struct SetterPolicy { + typedef SetterArgumentType ArgumentType; + typedef void (SetterThisType::*Context)(SetterArgumentType); + + typedef internal::BindingType Binding; + typedef typename Binding::WireType WireType; + + template + static void set(const Context& context, ClassType& ptr, WireType wt) { + (ptr.*context)(Binding::fromWireType(wt)); + } + + static void* getContext(Context context) { + return internal::getContext(context); + } + }; + + template + struct SetterPolicy { + typedef SetterArgumentType ArgumentType; + typedef void (*Context)(SetterThisType&, SetterArgumentType); + + typedef internal::BindingType Binding; + typedef typename Binding::WireType WireType; + + template + static void set(const Context& context, ClassType& ptr, WireType wt) { + context(ptr, Binding::fromWireType(wt)); + } + + static void* getContext(Context context) { + return internal::getContext(context); + } + }; } //////////////////////////////////////////////////////////////////////////////// @@ -446,55 +484,21 @@ namespace emscripten { return *this; } - template< - typename GetterReturnType, - typename GetterThisType, - typename SetterArgumentType, - typename SetterThisType> - value_tuple& element( - GetterReturnType (GetterThisType::*getter)() const, - void (SetterThisType::*setter)(SetterArgumentType) - ) { + template + value_tuple& element(Getter getter, Setter setter) { using namespace internal; + typedef GetterPolicy GP; + typedef SetterPolicy SP; _embind_register_tuple_element( TypeID::get(), - TypeID::get(), - reinterpret_cast( - &PropertyAccess - ::template getByMemberFunction), - getContext(getter), - TypeID::get(), - reinterpret_cast( - &PropertyAccess - ::template setByMemberFunction), - getContext(setter)); + TypeID::get(), + reinterpret_cast(&GP::template get), + GP::getContext(getter), + TypeID::get(), + reinterpret_cast(&SP::template set), + SP::getContext(setter)); return *this; } - - template< - typename GetterReturnType, - typename GetterThisType, - typename SetterArgumentType, - typename SetterThisType> - value_tuple& element( - GetterReturnType (*getter)(const GetterThisType&), - void (*setter)(SetterThisType&, SetterArgumentType) - ) { - using namespace internal; - _embind_register_tuple_element( - TypeID::get(), - TypeID::get(), - reinterpret_cast( - &PropertyAccess - ::template getByFunction), - getContext(getter), - TypeID::get(), - reinterpret_cast( - &PropertyAccess - ::template setByFunction), - getContext(setter)); - return *this; - } }; //////////////////////////////////////////////////////////////////////////////// @@ -532,57 +536,24 @@ namespace emscripten { return *this; } - template< - typename GetterReturnType, - typename GetterThisType, - typename SetterArgumentType, - typename SetterThisType> + template value_struct& field( const char* fieldName, - GetterReturnType (GetterThisType::*getter)() const, - void (SetterThisType::*setter)(SetterArgumentType) + Getter getter, + Setter setter ) { using namespace internal; + typedef GetterPolicy GP; + typedef SetterPolicy SP; _embind_register_struct_field( TypeID::get(), fieldName, - TypeID::get(), - reinterpret_cast( - &PropertyAccess - ::template getByMemberFunction), - getContext(getter), - TypeID::get(), - reinterpret_cast( - &PropertyAccess - ::template setByMemberFunction), - getContext(setter)); - return *this; - } - - template< - typename GetterReturnType, - typename GetterThisType, - typename SetterArgumentType, - typename SetterThisType> - value_struct& field( - const char* fieldName, - GetterReturnType (*getter)(const GetterThisType&), - void (*setter)(SetterThisType&, SetterArgumentType) - ) { - using namespace internal; - _embind_register_struct_field( - TypeID::get(), - fieldName, - TypeID::get(), - reinterpret_cast( - &PropertyAccess - ::template getByFunction), - getContext(getter), - TypeID::get(), - reinterpret_cast( - &PropertyAccess - ::template setByFunction), - getContext(setter)); + TypeID::get(), + reinterpret_cast(&GP::template get), + GP::getContext(getter), + TypeID::get(), + reinterpret_cast(&SP::template set), + SP::getContext(setter)); return *this; } }; From e8afd54698387cb38dafdea9317c764440063909 Mon Sep 17 00:00:00 2001 From: Jukka Jylanki Date: Tue, 2 Apr 2013 12:26:32 +0300 Subject: [PATCH 239/258] Add support for binding multiple class constructors with embind. The ctors must have different number of arguments each. --- src/embind/embind.js | 17 +++++++++++++---- tests/embind/embind.test.js | 20 ++++++++++++++++++-- tests/embind/embind_test.cpp | 31 +++++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+), 6 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 16e29d9e2..c298d8ba7 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -814,10 +814,13 @@ function __embind_register_class( if (Object.getPrototypeOf(this) !== instancePrototype) { throw new BindingError("Use 'new' to construct " + name); } - var body = registeredClass.constructor_body; - if (undefined === body) { + if (undefined === registeredClass.constructor_body) { throw new BindingError(name + " has no accessible constructor"); } + var body = registeredClass.constructor_body[arguments.length]; + if (undefined === body) { + throw new BindingError("Tried to invoke ctor of " + name + " with invalid number of parameters (" + arguments.length + ") - expected (" + Object.keys(registeredClass.constructor_body).toString() + ") parameters instead!"); + } return body.apply(this, arguments); }); @@ -884,12 +887,18 @@ function __embind_register_class_constructor( classType = classType[0]; var humanName = 'constructor ' + classType.name; - classType.registeredClass.constructor_body = function() { + if (undefined === classType.registeredClass.constructor_body) { + classType.registeredClass.constructor_body = []; + } + if (undefined !== classType.registeredClass.constructor_body[argCount - 1]) { + throw new BindingError("Cannot register multiple constructors with identical number of parameters (" + (argCount-1) + ") for class '" + classType.name + "'! Overload resolution is currently only performed using the parameter count, not actual type info!"); + } + classType.registeredClass.constructor_body[argCount - 1] = function() { throwUnboundTypeError('Cannot construct ' + classType.name + ' due to unbound types', rawArgTypes); }; whenDependentTypesAreResolved([], rawArgTypes, function(argTypes) { - classType.registeredClass.constructor_body = function() { + classType.registeredClass.constructor_body[argCount - 1] = function() { if (arguments.length !== argCount - 1) { throwBindingError(humanName + ' called with ' + arguments.length + ' arguments, expected ' + (argCount-1)); } diff --git a/tests/embind/embind.test.js b/tests/embind/embind.test.js index 94d935f2e..46b7646de 100755 --- a/tests/embind/embind.test.js +++ b/tests/embind/embind.test.js @@ -589,8 +589,24 @@ module({ assert.throws(TypeError, function() { cm.long_to_string(2147483648); }); assert.throws(TypeError, function() { cm.unsigned_long_to_string(-1); }); assert.throws(TypeError, function() { cm.unsigned_long_to_string(4294967296); }); + }); - }); + test("access multiple class ctors", function() { + var a = new cm.MultipleCtors(10); + assert.equal(a.WhichCtorCalled(), 1); + var b = new cm.MultipleCtors(20, 20); + assert.equal(b.WhichCtorCalled(), 2); + var c = new cm.MultipleCtors(30, 30, 30); + assert.equal(c.WhichCtorCalled(), 3); + a.delete(); + b.delete(); + c.delete(); + }); + + test("wrong number of constructor arguments throws", function() { + assert.throws(cm.BindingError, function() { new cm.MultipleCtors(); }); + assert.throws(cm.BindingError, function() { new cm.MultipleCtors(1,2,3,4); }); + }); /* test("can get templated member classes then call its member functions", function() { @@ -1424,7 +1440,7 @@ module({ test("unbound constructor argument", function() { assertMessage( function() { - new cm.HasConstructorUsingUnboundArgument; + new cm.HasConstructorUsingUnboundArgument(1); }, 'Cannot construct HasConstructorUsingUnboundArgument due to unbound types: UnboundClass'); }); diff --git a/tests/embind/embind_test.cpp b/tests/embind/embind_test.cpp index 470309aa2..f23b91521 100644 --- a/tests/embind/embind_test.cpp +++ b/tests/embind/embind_test.cpp @@ -1314,6 +1314,31 @@ std::string unsigned_long_to_string(unsigned long val) { return str; } +class MultipleCtors { +public: + int value; + + MultipleCtors(int i) { + value = 1; + assert(i == 10); + } + MultipleCtors(int i, int j) { + value = 2; + assert(i == 20); + assert(j == 20); + } + MultipleCtors(int i, int j, int k) { + value = 3; + assert(i == 30); + assert(j == 30); + assert(k == 30); + } + + int WhichCtorCalled() const { + return value; + } +}; + EMSCRIPTEN_BINDINGS(tests) { register_js_interface(); @@ -1759,6 +1784,12 @@ EMSCRIPTEN_BINDINGS(tests) { function("unsigned_int_to_string", &unsigned_int_to_string); function("long_to_string", &long_to_string); function("unsigned_long_to_string", &unsigned_long_to_string); + + class_("MultipleCtors") + .constructor() + .constructor() + .constructor() + .function("WhichCtorCalled", &MultipleCtors::WhichCtorCalled); } // tests for out-of-order registration From 034ebb08d36e282d0cd48d31c86ce995fa432b7b Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Tue, 2 Apr 2013 02:45:39 -0700 Subject: [PATCH 240/258] add a convenience for selecting a particular overload when binding a function. --- system/include/emscripten/bind.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index 3161289d6..4ecd35e96 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -208,6 +208,11 @@ namespace emscripten { struct allow_raw_pointer : public allow_raw_pointers { }; + template + typename std::add_pointer::type select_overload(typename std::add_pointer::type fn) { + return fn; + } + namespace internal { template struct Invoker { From 783c443cc84ca990e181b07f2ba7f310368d9b75 Mon Sep 17 00:00:00 2001 From: Jukka Jylanki Date: Tue, 2 Apr 2013 12:48:45 +0300 Subject: [PATCH 241/258] Clean up issues found with jshint. --- tests/embind/embind.test.js | 1 + tests/embind/imvu_test_adapter.js | 8 +++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/embind/embind.test.js b/tests/embind/embind.test.js index 46b7646de..539b837fa 100755 --- a/tests/embind/embind.test.js +++ b/tests/embind/embind.test.js @@ -1505,6 +1505,7 @@ module({ }); }); +/* global run_all_tests */ // If running as part of the emscripten test runner suite, and not as part of the IMVU suite, // we launch the test execution from here. IMVU suite uses its own dedicated mechanism instead of this. if (typeof run_all_tests !== "undefined") diff --git a/tests/embind/imvu_test_adapter.js b/tests/embind/imvu_test_adapter.js index 03405d084..421e86c8a 100755 --- a/tests/embind/imvu_test_adapter.js +++ b/tests/embind/imvu_test_adapter.js @@ -8,6 +8,8 @@ To run the Embind tests using the Emscripten test runner, invoke 'python tests/runner.py other.test_embind' in the Emscripten root directory. */ +/* global Module, console, global, process */ + //=== testing glue function module(ignore, func) { @@ -383,7 +385,7 @@ function module(ignore, func) { //////////////////////////////////////////////////////////////////////////////// // EXCEPTIONS - assert['throws'] = function(exception, fn) { + assert.throws = function(exception, fn) { try { fn(); } catch (e) { @@ -537,7 +539,7 @@ function module(ignore, func) { fail(new AssertionError(decipherDomElement(el) + ' expected NOT to be empty')); } } - } + }; // }; function decipherDomElement(selectorOrJQueryObject) { @@ -603,7 +605,7 @@ function module(ignore, func) { // IMVU runner uses a separate runner & reporting mechanism. function run_all_tests() { function report_to_stdout(msg) { - if (msg.type == "test-complete") + if (msg.type === "test-complete") console.log(msg.name + ": " + msg.verdict); } run_all(report_to_stdout); From 616b5c3579556c5e1be6ba1063c132680d9e2d43 Mon Sep 17 00:00:00 2001 From: Jukka Jylanki Date: Tue, 2 Apr 2013 15:35:36 +0300 Subject: [PATCH 242/258] Add support for registering overloads of free functions based on argument count. --- src/embind/embind.js | 68 ++++++++++++++++++++++++++++++++---- tests/embind/embind.test.js | 12 +++++++ tests/embind/embind_test.cpp | 16 +++++++++ 3 files changed, 89 insertions(+), 7 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index c298d8ba7..e050d3c24 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -39,18 +39,72 @@ function throwUnboundTypeError(message, types) { throw new UnboundTypeError(message + ': ' + unboundTypes.map(getTypeName).join([', '])); } -function exposePublicSymbol(name, value) { +/* Registers a symbol (function, class, enum, ...) as part of the Module JS object so that + hand-written code is able to access that symbol via 'Module.name'. + name: The name of the symbol that's being exposed. + value: The object itself to expose (function, class, ...) + numArguments: For functions, specifies the number of arguments the function takes in. For other types, unused and undefined. + + To implement support for multiple overloads of a function, an 'overload selector' function is used. That selector function chooses + the appropriate overload to call from an function overload table. This selector function is only used if multiple overloads are + actually registered, since it carries a slight performance penalty. */ +function exposePublicSymbol(name, value, numArguments) { if (Module.hasOwnProperty(name)) { - throwBindingError("Cannot register public name '" + name + "' twice"); + if (undefined === numArguments || (undefined !== Module[name].overloadTable && undefined !== Module[name].overloadTable[numArguments])) { + throwBindingError("Cannot register public name '" + name + "' twice"); + } + + // We are exposing a function with the same name as an existing function. Create an overload table and a function selector + // that routes between the two. + + // If we don't yet have an overload selector, install an overload selector that routes the function call to a table of overloads based on # of arguments to function. + if (undefined === Module[name].overloadTable) { + var prevFunc = Module[name]; + + // Inject an overload selector in place of the previous function. + Module[name] = function() { + // TODO This check can be removed in -O3 level "unsafe" optimizations. + if (!Module[name].overloadTable.hasOwnProperty(arguments.length)) { + throwBindingError("Function '" + name + "' called with an invalid number of arguments (" + arguments.length + ") - expects one of (" + Object.keys(Module[name].overloadTable) + ")!"); + } + return Module[name].overloadTable[arguments.length].apply(this, arguments); + }; + // An overloadTable maintains a registry of all function overloads. + Module[name].overloadTable = []; + // Move the old function into the overload table. + Module[name].overloadTable[prevFunc.numArguments] = prevFunc; + } + + if (Module.hasOwnProperty(numArguments)) { + throwBindingError("Cannot register multiple overloads of a function with the same number of arguments (" + numArguments + ")!"); + } + // Add the new function into the overload table. + Module[name].overloadTable[numArguments] = value; + } + else { + Module[name] = value; + if (undefined !== numArguments) { + Module[name].numArguments = numArguments; + } } - Module[name] = value; } -function replacePublicSymbol(name, value) { +function replacePublicSymbol(name, value, numArguments) { if (!Module.hasOwnProperty(name)) { throwInternalError('Replacing nonexistant public symbol'); } - Module[name] = value; + // If there's an overload table for this symbol, replace the symbol in the overload table instead. + if (undefined !== Module[name].overloadTable && undefined !== numArguments) { + Module[name].overloadTable[numArguments] = value; + } + else { + Module[name] = value; + /* XXX TODO unneeded?! + if (undefined !== numArguments) { + Module[name].numArguments = numArguments; + } + */ + } } // from https://github.com/imvu/imvujs/blob/master/src/error.js @@ -345,10 +399,10 @@ function __embind_register_function(name, argCount, rawArgTypesAddr, rawInvoker, exposePublicSymbol(name, function() { throwUnboundTypeError('Cannot call ' + name + ' due to unbound types', argTypes); - }); + }, argCount - 1); whenDependentTypesAreResolved([], argTypes, function(argTypes) { - replacePublicSymbol(name, makeInvoker(name, argCount, argTypes, rawInvoker, fn)); + replacePublicSymbol(name, makeInvoker(name, argCount, argTypes, rawInvoker, fn), argCount - 1); return []; }); } diff --git a/tests/embind/embind.test.js b/tests/embind/embind.test.js index 539b837fa..a577cd377 100755 --- a/tests/embind/embind.test.js +++ b/tests/embind/embind.test.js @@ -608,6 +608,18 @@ module({ assert.throws(cm.BindingError, function() { new cm.MultipleCtors(1,2,3,4); }); }); + test("overloading of free functions", function() { + var a = cm.overloaded_function(10); + assert.equal(a, 1); + var b = cm.overloaded_function(20, 20); + assert.equal(b, 2); + }); + + test("wrong number of arguments to an overloaded free function", function() { + assert.throws(cm.BindingError, function() { cm.overloaded_function(); }); + assert.throws(cm.BindingError, function() { cm.overloaded_function(30, 30, 30); }); + }); + /* test("can get templated member classes then call its member functions", function() { var p = new cm.ContainsTemplatedMemberClass(); diff --git a/tests/embind/embind_test.cpp b/tests/embind/embind_test.cpp index f23b91521..0ae1801a3 100644 --- a/tests/embind/embind_test.cpp +++ b/tests/embind/embind_test.cpp @@ -1339,6 +1339,19 @@ public: } }; +int overloaded_function(int i) +{ + assert(i == 10); + return 1; +} + +int overloaded_function(int i, int j) +{ + assert(i == 20); + assert(j == 20); + return 2; +} + EMSCRIPTEN_BINDINGS(tests) { register_js_interface(); @@ -1785,6 +1798,9 @@ EMSCRIPTEN_BINDINGS(tests) { function("long_to_string", &long_to_string); function("unsigned_long_to_string", &unsigned_long_to_string); + function("overloaded_function", (int(*)(int))&overloaded_function); + function("overloaded_function", (int(*)(int, int))&overloaded_function); + class_("MultipleCtors") .constructor() .constructor() From d4bf6fbe297ea7e5720b49bfa6fc58b696540144 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Wed, 3 Apr 2013 17:28:11 -0700 Subject: [PATCH 243/258] Add support for global constants. --- src/embind/embind.js | 12 ++++++++++++ system/include/emscripten/bind.h | 31 +++++++++++++++++++++++++++++++ system/include/emscripten/wire.h | 2 +- tests/embind/embind.test.js | 6 ++++++ tests/embind/embind_test.cpp | 10 ++++++++++ 5 files changed, 60 insertions(+), 1 deletion(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index e050d3c24..46fdeee4e 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -1245,3 +1245,15 @@ function __embind_register_interface( }); } +function __embind_register_constant(name, type, value, destructor) { + name = Pointer_stringify(name); + whenDependentTypesAreResolved([], [type], function(type) { + type = type[0]; + /*global console*/ + //console.log('type', type); + //console.log('value', value); + Module[name] = type.fromWireType(value); + // todo: I need to natively destruct 'value' here + return []; + }); +} diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index 4ecd35e96..e767afaa4 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -159,6 +159,11 @@ namespace emscripten { const char* name, GenericFunction constructor, GenericFunction destructor); + + void _embind_register_constant( + const char* name, + TYPEID constantType, + uintptr_t value); } } } @@ -1023,6 +1028,32 @@ namespace emscripten { } }; + //////////////////////////////////////////////////////////////////////////////// + // CONSTANTS + //////////////////////////////////////////////////////////////////////////////// + + namespace internal { + template + uintptr_t asGenericValue(T t) { + return static_cast(t); + } + + template + uintptr_t asGenericValue(T* p) { + return reinterpret_cast(p); + } + } + + template + void constant(const char* name, const ConstantType& v) { + using namespace internal; + typedef BindingType BT; + _embind_register_constant( + name, + TypeID::get(), + asGenericValue(BindingType::toWireType(v))); + } + namespace internal { template class optional { diff --git a/system/include/emscripten/wire.h b/system/include/emscripten/wire.h index 0c0f5eb81..9c8cd096f 100755 --- a/system/include/emscripten/wire.h +++ b/system/include/emscripten/wire.h @@ -122,7 +122,7 @@ namespace emscripten { template<> \ struct BindingType { \ typedef type WireType; \ - constexpr static WireType toWireType(const type& v) { \ + constexpr static WireType toWireType(const type& v) { \ return v; \ } \ constexpr static type fromWireType(WireType v) { \ diff --git a/tests/embind/embind.test.js b/tests/embind/embind.test.js index a577cd377..7bb3985ff 100755 --- a/tests/embind/embind.test.js +++ b/tests/embind/embind.test.js @@ -1515,6 +1515,12 @@ module({ assert.equal('ValHolder$setVal', cm.ValHolder.prototype.setVal.name); assert.equal('ValHolder$makeConst', cm.ValHolder.makeConst.name); }); + + BaseFixture.extend("constants", function() { + assert.equal(10, cm.INT_CONSTANT); + assert.equal("some string", cm.STRING_CONSTANT); + //assert.deepEqual([1, 2, 3], cm.VALUE_TUPLE_CONSTANT); + }); }); /* global run_all_tests */ diff --git a/tests/embind/embind_test.cpp b/tests/embind/embind_test.cpp index 0ae1801a3..4d1b376b9 100644 --- a/tests/embind/embind_test.cpp +++ b/tests/embind/embind_test.cpp @@ -1352,6 +1352,16 @@ int overloaded_function(int i, int j) return 2; } +EMSCRIPTEN_BINDINGS(constants) { + constant("INT_CONSTANT", 10); + constant("STRING_CONSTANT", std::string("some string")); + TupleVector tv; + tv.x = 1; + tv.y = 2; + tv.z = 3; + constant("VALUE_TUPLE_CONSTANT", tv); +} + EMSCRIPTEN_BINDINGS(tests) { register_js_interface(); From f508d6306ff0be4966d3f26b99a26c46f7c2c1ef Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Wed, 3 Apr 2013 19:50:50 -0700 Subject: [PATCH 244/258] Allow value_tuple and value_struct to be registered as global constants. This involved reworking how value_struct and value_tuple are registered. --- src/embind/embind.js | 254 +++++++++++++++++-------------- system/include/emscripten/bind.h | 26 +++- tests/embind/embind.test.js | 3 +- tests/embind/embind_test.cpp | 7 + 4 files changed, 176 insertions(+), 114 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 46fdeee4e..37439912c 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -407,37 +407,15 @@ function __embind_register_function(name, argCount, rawArgTypesAddr, rawInvoker, }); } +var tupleRegistrations = {}; + function __embind_register_tuple(rawType, name, rawConstructor, rawDestructor) { - name = Pointer_stringify(name); - rawConstructor = FUNCTION_TABLE[rawConstructor]; - rawDestructor = FUNCTION_TABLE[rawDestructor]; - registerType(rawType, { - name: name, - rawConstructor: rawConstructor, - rawDestructor: rawDestructor, + tupleRegistrations[rawType] = { + name: Pointer_stringify(name), + rawConstructor: FUNCTION_TABLE[rawConstructor], + rawDestructor: FUNCTION_TABLE[rawDestructor], elements: [], - fromWireType: function(ptr) { - var len = this.elements.length; - var rv = new Array(len); - for (var i = 0; i < len; ++i) { - rv[i] = this.elements[i].read(ptr); - } - this.rawDestructor(ptr); - return rv; - }, - toWireType: function(destructors, o) { - var len = this.elements.length; - if (len !== o.length) { - throw new TypeError("Incorrect number of tuple elements"); - } - var ptr = this.rawConstructor(); - for (var i = 0; i < len; ++i) { - this.elements[i].write(ptr, o[i]); - } - destructors.push(rawDestructor, ptr); - return ptr; - }, - }); + }; } function __embind_register_tuple_element( @@ -449,78 +427,84 @@ function __embind_register_tuple_element( setter, setterContext ) { - var tupleType = requireRegisteredType(rawTupleType, 'tuple'); - getter = FUNCTION_TABLE[getter]; - setter = FUNCTION_TABLE[setter]; - - var index = tupleType.elements.length; - tupleType.elements.push(undefined); - - // TODO: test incomplete registration of value tuples - whenDependentTypesAreResolved([], [getterReturnType, setterArgumentType], function(types) { - var getterReturnType = types[0]; - var setterArgumentType = types[1]; - tupleType.elements[index] = { - read: function(ptr) { - return getterReturnType.fromWireType( - getter( - getterContext, - ptr)); - }, - write: function(ptr, o) { - var destructors = []; - setter( - setterContext, - ptr, - setterArgumentType.toWireType(destructors, o)); - runDestructors(destructors); - } - }; - return []; + tupleRegistrations[rawTupleType].elements.push({ + getterReturnType: getterReturnType, + getter: FUNCTION_TABLE[getter], + getterContext: getterContext, + setterArgumentType: setterArgumentType, + setter: FUNCTION_TABLE[setter], + setterContext: setterContext, }); } +function __embind_finalize_tuple(rawTupleType) { + var reg = tupleRegistrations[rawTupleType]; + delete tupleRegistrations[rawTupleType]; + var elements = reg.elements; + var elementsLength = elements.length; + var elementTypes = elements.map(function(elt) { return elt.getterReturnType; }). + concat(elements.map(function(elt) { return elt.setterArgumentType; })); + + var rawConstructor = reg.rawConstructor; + var rawDestructor = reg.rawDestructor; + + whenDependentTypesAreResolved([rawTupleType], elementTypes, function(elementTypes) { + elements.forEach(function(elt, i) { + var getterReturnType = elementTypes[i]; + var getter = elt.getter; + var getterContext = elt.getterContext; + var setterArgumentType = elementTypes[i + elementsLength]; + var setter = elt.setter; + var setterContext = elt.setterContext; + elt.read = function(ptr) { + return getterReturnType.fromWireType(getter(getterContext, ptr)); + }; + elt.write = function(ptr, o) { + var destructors = []; + setter(setterContext, ptr, setterArgumentType.toWireType(destructors, o)); + runDestructors(destructors); + }; + }); + + return [{ + name: reg.name, + fromWireType: function(ptr) { + var rv = new Array(elementsLength); + for (var i = 0; i < elementsLength; ++i) { + rv[i] = elements[i].read(ptr); + } + rawDestructor(ptr); + return rv; + }, + toWireType: function(destructors, o) { + if (elementsLength !== o.length) { + throw new TypeError("Incorrect number of tuple elements"); + } + var ptr = rawConstructor(); + for (var i = 0; i < elementsLength; ++i) { + elements[i].write(ptr, o[i]); + } + destructors.push(rawDestructor, ptr); + return ptr; + }, + }]; + }); +} + +var structRegistrations = {}; + function __embind_register_struct( rawType, name, rawConstructor, rawDestructor ) { - name = Pointer_stringify(name); - rawConstructor = FUNCTION_TABLE[rawConstructor]; - rawDestructor = FUNCTION_TABLE[rawDestructor]; - - registerType(rawType, { - name: name, - rawConstructor: rawConstructor, - rawDestructor: rawDestructor, - fields: {}, - fromWireType: function(ptr) { - var fields = this.fields; - var rv = {}; - for (var i in fields) { - rv[i] = fields[i].read(ptr); - } - this.rawDestructor(ptr); - return rv; - }, - toWireType: function(destructors, o) { - var fields = this.fields; - // todo: Here we have an opportunity for -O3 level "unsafe" optimizations: - // assume all fields are present without checking. - for (var fieldName in fields) { - if (!(fieldName in o)) { - throw new TypeError('Missing field'); - } - } - var ptr = this.rawConstructor(); - for (fieldName in fields) { - fields[fieldName].write(ptr, o[fieldName]); - } - destructors.push(rawDestructor, ptr); - return ptr; - }, - }); + structRegistrations[rawType] = { + name: Pointer_stringify(name), + rawConstructor: FUNCTION_TABLE[rawConstructor], + rawDestructor: FUNCTION_TABLE[rawDestructor], + fields: [], + }; } function __embind_register_struct_field( @@ -533,27 +517,75 @@ function __embind_register_struct_field( setter, setterContext ) { - structType = requireRegisteredType(structType, 'struct'); - fieldName = Pointer_stringify(fieldName); - getter = FUNCTION_TABLE[getter]; - setter = FUNCTION_TABLE[setter]; + structRegistrations[structType].fields.push({ + fieldName: Pointer_stringify(fieldName), + getterReturnType: getterReturnType, + getter: FUNCTION_TABLE[getter], + getterContext: getterContext, + setterArgumentType: setterArgumentType, + setter: FUNCTION_TABLE[setter], + setterContext: setterContext, + }); +} - // TODO: test incomplete registration of value structs - whenDependentTypesAreResolved([], [getterReturnType, setterArgumentType], function(types) { - var getterReturnType = types[0]; - var setterArgumentType = types[1]; - structType.fields[fieldName] = { - read: function(ptr) { - return getterReturnType.fromWireType( - getter(getterContext, ptr)); +function __embind_finalize_struct(structType) { + var reg = structRegistrations[structType]; + delete structRegistrations[structType]; + + var rawConstructor = reg.rawConstructor; + var rawDestructor = reg.rawDestructor; + var fieldRecords = reg.fields; + var fieldTypes = fieldRecords.map(function(field) { return field.getterReturnType; }). + concat(fieldRecords.map(function(field) { return field.setterArgumentType; })); + whenDependentTypesAreResolved([structType], fieldTypes, function(fieldTypes) { + var fields = {}; + fieldRecords.forEach(function(field, i) { + var fieldName = field.fieldName; + var getterReturnType = fieldTypes[i]; + var getter = field.getter; + var getterContext = field.getterContext; + var setterArgumentType = fieldTypes[i + fieldRecords.length]; + var setter = field.setter; + var setterContext = field.setterContext; + fields[fieldName] = { + read: function(ptr) { + return getterReturnType.fromWireType( + getter(getterContext, ptr)); + }, + write: function(ptr, o) { + var destructors = []; + setter(setterContext, ptr, setterArgumentType.toWireType(destructors, o)); + runDestructors(destructors); + } + }; + }); + + return [{ + name: reg.name, + fromWireType: function(ptr) { + var rv = {}; + for (var i in fields) { + rv[i] = fields[i].read(ptr); + } + rawDestructor(ptr); + return rv; }, - write: function(ptr, o) { - var destructors = []; - setter(setterContext, ptr, setterArgumentType.toWireType(destructors, o)); - runDestructors(destructors); - } - }; - return []; + toWireType: function(destructors, o) { + // todo: Here we have an opportunity for -O3 level "unsafe" optimizations: + // assume all fields are present without checking. + for (var fieldName in fields) { + if (!(fieldName in o)) { + throw new TypeError('Missing field'); + } + } + var ptr = rawConstructor(); + for (fieldName in fields) { + fields[fieldName].write(ptr, o[fieldName]); + } + destructors.push(rawDestructor, ptr); + return ptr; + }, + }]; }); } diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index e767afaa4..bd170dea6 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -77,6 +77,8 @@ namespace emscripten { GenericFunction setter, void* setterContext); + void _embind_finalize_tuple(TYPEID tupleType); + void _embind_register_struct( TYPEID structType, const char* fieldName, @@ -93,6 +95,8 @@ namespace emscripten { GenericFunction setter, void* setterContext); + void _embind_finalize_struct(TYPEID structType); + void _embind_register_smart_ptr( TYPEID pointerType, TYPEID pointeeType, @@ -458,6 +462,15 @@ namespace emscripten { return internal::getContext(context); } }; + + class noncopyable { + protected: + noncopyable() {} + ~noncopyable() {} + private: + noncopyable(const noncopyable&) = delete; + const noncopyable& operator=(const noncopyable&) = delete; + }; } //////////////////////////////////////////////////////////////////////////////// @@ -465,7 +478,7 @@ namespace emscripten { //////////////////////////////////////////////////////////////////////////////// template - class value_tuple { + class value_tuple : public internal::noncopyable { public: value_tuple(const char* name) { using namespace internal; @@ -476,6 +489,11 @@ namespace emscripten { reinterpret_cast(&raw_destructor)); } + ~value_tuple() { + using namespace internal; + _embind_finalize_tuple(TypeID::get()); + } + template value_tuple& element(ElementType InstanceType::*field) { using namespace internal; @@ -516,7 +534,7 @@ namespace emscripten { //////////////////////////////////////////////////////////////////////////////// template - class value_struct { + class value_struct : public internal::noncopyable { public: value_struct(const char* name) { using namespace internal; @@ -527,6 +545,10 @@ namespace emscripten { reinterpret_cast(&raw_destructor)); } + ~value_struct() { + _embind_finalize_struct(internal::TypeID::get()); + } + template value_struct& field(const char* fieldName, FieldType InstanceType::*field) { using namespace internal; diff --git a/tests/embind/embind.test.js b/tests/embind/embind.test.js index 7bb3985ff..11746214e 100755 --- a/tests/embind/embind.test.js +++ b/tests/embind/embind.test.js @@ -1519,7 +1519,8 @@ module({ BaseFixture.extend("constants", function() { assert.equal(10, cm.INT_CONSTANT); assert.equal("some string", cm.STRING_CONSTANT); - //assert.deepEqual([1, 2, 3], cm.VALUE_TUPLE_CONSTANT); + assert.deepEqual([1, 2, 3], cm.VALUE_TUPLE_CONSTANT); + assert.deepEqual({x:1,y:2,z:3}, cm.VALUE_STRUCT_CONSTANT); }); }); diff --git a/tests/embind/embind_test.cpp b/tests/embind/embind_test.cpp index 4d1b376b9..be089f9f2 100644 --- a/tests/embind/embind_test.cpp +++ b/tests/embind/embind_test.cpp @@ -1355,11 +1355,18 @@ int overloaded_function(int i, int j) EMSCRIPTEN_BINDINGS(constants) { constant("INT_CONSTANT", 10); constant("STRING_CONSTANT", std::string("some string")); + TupleVector tv; tv.x = 1; tv.y = 2; tv.z = 3; constant("VALUE_TUPLE_CONSTANT", tv); + + StructVector sv; + sv.x = 1; + sv.y = 2; + sv.z = 3; + constant("VALUE_STRUCT_CONSTANT", sv); } EMSCRIPTEN_BINDINGS(tests) { From 7eb89c3e4feabd85b50cfad9f3c44a9235540a05 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Wed, 3 Apr 2013 20:03:18 -0700 Subject: [PATCH 245/258] refactoring --- src/embind/embind.js | 6 +----- system/include/emscripten/wire.h | 18 ++---------------- 2 files changed, 3 insertions(+), 21 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 37439912c..df5812985 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -1277,15 +1277,11 @@ function __embind_register_interface( }); } -function __embind_register_constant(name, type, value, destructor) { +function __embind_register_constant(name, type, value) { name = Pointer_stringify(name); whenDependentTypesAreResolved([], [type], function(type) { type = type[0]; - /*global console*/ - //console.log('type', type); - //console.log('value', value); Module[name] = type.fromWireType(value); - // todo: I need to natively destruct 'value' here return []; }); } diff --git a/system/include/emscripten/wire.h b/system/include/emscripten/wire.h index 9c8cd096f..64114491a 100755 --- a/system/include/emscripten/wire.h +++ b/system/include/emscripten/wire.h @@ -182,25 +182,11 @@ namespace emscripten { }; template - struct BindingType { - typedef typename BindingType::WireType WireType; - static WireType toWireType(const T& v) { - return BindingType::toWireType(v); - } - static T fromWireType(WireType wt) { - return BindingType::fromWireType(wt); - } + struct BindingType : public BindingType { }; template - struct BindingType { - typedef typename BindingType::WireType WireType; - static WireType toWireType(const T& v) { - return BindingType::toWireType(v); - } - static T fromWireType(WireType wt) { - return BindingType::fromWireType(wt); - } + struct BindingType : public BindingType { }; template From f80caa7c2dbfbba8b6988168715ce3421322191c Mon Sep 17 00:00:00 2001 From: Jukka Jylanki Date: Thu, 4 Apr 2013 09:19:18 +0300 Subject: [PATCH 246/258] Add support for overloading class member functions and class static functions based on function argument count. --- src/embind/embind.js | 87 +++++++++++++++++++++++++++++---- tests/embind/embind.test.js | 56 ++++++++++++++++++++++ tests/embind/embind_test.cpp | 93 ++++++++++++++++++++++++++++++++++++ 3 files changed, 227 insertions(+), 9 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index df5812985..f48bcf7b8 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -1060,14 +1060,40 @@ function __embind_register_class_function( classType = classType[0]; var humanName = classType.name + '.' + methodName; - classType.registeredClass.instancePrototype[methodName] = function() { - throwUnboundTypeError('Cannot call ' + humanName + ' due to unbound types', rawArgTypes); - }; + var unboundTypesHandler = function() { + throwUnboundTypeError('Cannot call ' + humanName + ' due to unbound types', rawArgTypes); + }; + + var method = classType.registeredClass.instancePrototype[methodName]; + if (undefined === method || (undefined === method.overloadTable && method.className !== classType.name && method.argCount === argCount-2)) { + // This is the first overload to be registered, OR we are replacing a function in the base class with a function in the derived class. + classType.registeredClass.instancePrototype[methodName] = unboundTypesHandler; + classType.registeredClass.instancePrototype[methodName].argCount = argCount-2; + classType.registeredClass.instancePrototype[methodName].className = classType.name; + } else { + // There was an existing function with the same name registered. Set up a function overload routing table. + if (undefined === classType.registeredClass.instancePrototype[methodName].overloadTable) { + var prevFunc = classType.registeredClass.instancePrototype[methodName]; + + // Inject an overload resolver function that routes to the appropriate overload based on the number of arguments. + classType.registeredClass.instancePrototype[methodName] = function() { + // TODO This check can be removed in -O3 level "unsafe" optimizations. + if (!classType.registeredClass.instancePrototype[methodName].overloadTable.hasOwnProperty(arguments.length)) { + throwBindingError("Member function '" + humanName + "' called with an invalid number of arguments (" + arguments.length + ") - expects one of (" + classType.registeredClass.instancePrototype[methodName].overloadTable + ")!"); + } + return classType.registeredClass.instancePrototype[methodName].overloadTable[arguments.length].apply(this, arguments); + }; + // Move the previous function into the overload table. + classType.registeredClass.instancePrototype[methodName].overloadTable = []; + classType.registeredClass.instancePrototype[methodName].overloadTable[prevFunc.argCount] = prevFunc; + } + classType.registeredClass.instancePrototype[methodName].overloadTable[argCount-2] = unboundTypesHandler; + } whenDependentTypesAreResolved([], rawArgTypes, function(argTypes) { - classType.registeredClass.instancePrototype[methodName] = createNamedFunction(makeLegalFunctionName(humanName), function() { + var memberFunction = createNamedFunction(makeLegalFunctionName(humanName), function() { if (arguments.length !== argCount - 2) { - throwBindingError(humanName + ' called with ' + arguments.length + ' arguments, expected ' + (argCount-1)); + throwBindingError(humanName + ' called with ' + arguments.length + ' arguments, expected ' + (argCount-2)); } validateThis(this, classType, humanName); @@ -1084,6 +1110,15 @@ function __embind_register_class_function( runDestructors(destructors); return rv; }); + + // Replace the initial unbound-handler-stub function with the appropriate member function, now that all types + // are resolved. If multiple overloads are registered for this function, the function goes into an overload table. + if (undefined === classType.registeredClass.instancePrototype[methodName].overloadTable) { + classType.registeredClass.instancePrototype[methodName] = memberFunction; + } else { + classType.registeredClass.instancePrototype[methodName].overloadTable[argCount-2] = memberFunction; + } + return []; }); return []; @@ -1105,12 +1140,46 @@ function __embind_register_class_class_function( classType = classType[0]; var humanName = classType.name + '.' + methodName; - classType.registeredClass.constructor[methodName] = function() { - throwUnboundTypeError('Cannot call ' + humanName + ' due to unbound types', rawArgTypes); - }; + var unboundTypesHandler = function() { + throwUnboundTypeError('Cannot call ' + humanName + ' due to unbound types', rawArgTypes); + }; + + if (undefined === classType.registeredClass.constructor[methodName]) { + // This is the first function to be registered with this name. + classType.registeredClass.constructor[methodName] = unboundTypesHandler; + classType.registeredClass.constructor[methodName].argCount = argCount-1; + } else { + // There was an existing function to be registered with this name. Set up an overload table to + // resolve between them. + if (undefined === classType.registeredClass.constructor[methodName].overloadTable) { + var prevFunc = classType.registeredClass.constructor[methodName]; + + // Inject an overload handler function that resolves the proper function to call based on the + // number of parameters passed to the function. + classType.registeredClass.constructor[methodName] = function() { + // TODO This check can be removed in -O3 level "unsafe" optimizations. + if (!classType.registeredClass.constructor[methodName].overloadTable.hasOwnProperty(arguments.length)) { + throwBindingError("Static member function '" + humanName + "' called with an invalid number of arguments (" + arguments.length + ") - expects one of (" + classType.registeredClass.constructor[methodName].overloadTable + ")!"); + } + return classType.registeredClass.constructor[methodName].overloadTable[arguments.length].apply(this, arguments); + }; + + classType.registeredClass.constructor[methodName].overloadTable = []; + // Move the old function into the overload table. + classType.registeredClass.constructor[methodName].overloadTable[prevFunc.argCount] = prevFunc; + } + classType.registeredClass.constructor[methodName].overloadTable[argCount-1] = unboundTypesHandler; + } whenDependentTypesAreResolved([], rawArgTypes, function(argTypes) { - classType.registeredClass.constructor[methodName] = makeInvoker(humanName, argCount, argTypes, rawInvoker, fn); + // Replace the initial unbound-types-handler stub with the proper function. If multiple overloads are registered, + // the function handlers go into an overload table. + var func = makeInvoker(humanName, argCount, argTypes, rawInvoker, fn); + if (undefined === classType.registeredClass.constructor[methodName].overloadTable) { + classType.registeredClass.constructor[methodName] = func; + } else { + classType.registeredClass.constructor[methodName].overloadTable[argCount-1] = func; + } return []; }); return []; diff --git a/tests/embind/embind.test.js b/tests/embind/embind.test.js index 11746214e..020719d5f 100755 --- a/tests/embind/embind.test.js +++ b/tests/embind/embind.test.js @@ -620,6 +620,62 @@ module({ assert.throws(cm.BindingError, function() { cm.overloaded_function(30, 30, 30); }); }); + test("overloading of class member functions", function() { + var foo = new cm.MultipleOverloads(); + assert.equal(foo.Func(10), 1); + assert.equal(foo.WhichFuncCalled(), 1); + assert.equal(foo.Func(20, 20), 2); + assert.equal(foo.WhichFuncCalled(), 2); + foo.delete(); + }); + + test("wrong number of arguments to an overloaded class member function", function() { + var foo = new cm.MultipleOverloads(); + assert.throws(cm.BindingError, function() { foo.Func(); }); + assert.throws(cm.BindingError, function() { foo.Func(30, 30, 30); }); + foo.delete(); + }); + + test("wrong number of arguments to an overloaded class static function", function() { + assert.throws(cm.BindingError, function() { cm.MultipleOverloads.StaticFunc(); }); + assert.throws(cm.BindingError, function() { cm.MultipleOverloads.StaticFunc(30, 30, 30); }); + }); + + test("overloading of derived class member functions", function() { + var foo = new cm.MultipleOverloadsDerived(); + + // NOTE: In C++, default lookup rules will hide overloads from base class if derived class creates them. + // In JS, we make the base class overloads implicitly available. In C++, they would need to be explicitly + // invoked, like foo.MultipleOverloads::Func(10); + assert.equal(foo.Func(10), 1); + assert.equal(foo.WhichFuncCalled(), 1); + assert.equal(foo.Func(20, 20), 2); + assert.equal(foo.WhichFuncCalled(), 2); + + assert.equal(foo.Func(30, 30, 30), 3); + assert.equal(foo.WhichFuncCalled(), 3); + assert.equal(foo.Func(40, 40, 40, 40), 4); + assert.equal(foo.WhichFuncCalled(), 4); + foo.delete(); + }); + + test("overloading of class static functions", function() { + assert.equal(cm.MultipleOverloads.StaticFunc(10), 1); + assert.equal(cm.MultipleOverloads.WhichStaticFuncCalled(), 1); + assert.equal(cm.MultipleOverloads.StaticFunc(20, 20), 2); + assert.equal(cm.MultipleOverloads.WhichStaticFuncCalled(), 2); + }); + + test("overloading of derived class static functions", function() { + assert.equal(cm.MultipleOverloadsDerived.StaticFunc(30, 30, 30), 3); + // TODO: Cannot access static member functions of a Base class via Derived. +// assert.equal(cm.MultipleOverloadsDerived.WhichStaticFuncCalled(), 3); + assert.equal(cm.MultipleOverloads.WhichStaticFuncCalled(), 3); + assert.equal(cm.MultipleOverloadsDerived.StaticFunc(40, 40, 40, 40), 4); + // TODO: Cannot access static member functions of a Base class via Derived. +// assert.equal(cm.MultipleOverloadsDerived.WhichStaticFuncCalled(), 4); + assert.equal(cm.MultipleOverloads.WhichStaticFuncCalled(), 4); + }); /* test("can get templated member classes then call its member functions", function() { var p = new cm.ContainsTemplatedMemberClass(); diff --git a/tests/embind/embind_test.cpp b/tests/embind/embind_test.cpp index be089f9f2..1ef3fb169 100644 --- a/tests/embind/embind_test.cpp +++ b/tests/embind/embind_test.cpp @@ -1339,6 +1339,83 @@ public: } }; +class MultipleOverloads { +public: + MultipleOverloads() {} + + int value; + static int staticValue; + + int Func(int i) { + assert(i == 10); + value = 1; + return 1; + } + int Func(int i, int j) { + assert(i == 20); + assert(j == 20); + value = 2; + return 2; + } + + int WhichFuncCalled() const { + return value; + } + + static int StaticFunc(int i) { + assert(i == 10); + staticValue = 1; + return 1; + } + static int StaticFunc(int i, int j) { + assert(i == 20); + assert(j == 20); + staticValue = 2; + return 2; + } + + static int WhichStaticFuncCalled() { + return staticValue; + } +}; + +class MultipleOverloadsDerived : public MultipleOverloads { +public: + MultipleOverloadsDerived() {} + + int Func(int i, int j, int k) { + assert(i == 30); + assert(j == 30); + assert(k == 30); + value = 3; + return 3; + } + int Func(int i, int j, int k, int l) { + assert(i == 40); + assert(j == 40); + assert(k == 40); + assert(l == 40); + value = 4; + return 4; + } + + static int StaticFunc(int i, int j, int k) { + assert(i == 30); + assert(j == 30); + assert(k == 30); + staticValue = 3; + return 3; + } + static int StaticFunc(int i, int j, int k, int l) { + assert(i == 40); + assert(j == 40); + assert(k == 40); + assert(l == 40); + staticValue = 4; + return 4; + } +}; + int overloaded_function(int i) { assert(i == 10); @@ -1823,6 +1900,22 @@ EMSCRIPTEN_BINDINGS(tests) { .constructor() .constructor() .function("WhichCtorCalled", &MultipleCtors::WhichCtorCalled); + + class_("MultipleOverloads") + .constructor<>() + .function("Func", (int(MultipleOverloads::*)(int))&MultipleOverloads::Func) + .function("Func", (int(MultipleOverloads::*)(int,int))&MultipleOverloads::Func) + .function("WhichFuncCalled", &MultipleOverloads::WhichFuncCalled) + .class_function("StaticFunc", (int(*)(int))&MultipleOverloads::StaticFunc) + .class_function("StaticFunc", (int(*)(int,int))&MultipleOverloads::StaticFunc) + .class_function("WhichStaticFuncCalled", &MultipleOverloads::WhichStaticFuncCalled); + + class_ >("MultipleOverloadsDerived") + .constructor<>() + .function("Func", (int(MultipleOverloadsDerived::*)(int,int,int))&MultipleOverloadsDerived::Func) + .function("Func", (int(MultipleOverloadsDerived::*)(int,int,int,int))&MultipleOverloadsDerived::Func) + .class_function("StaticFunc", (int(*)(int,int,int))&MultipleOverloadsDerived::StaticFunc) + .class_function("StaticFunc", (int(*)(int,int,int,int))&MultipleOverloadsDerived::StaticFunc); } // tests for out-of-order registration From 3585aa62068fbfe453e0b12ff577a1950a4094fd Mon Sep 17 00:00:00 2001 From: Jukka Jylanki Date: Thu, 4 Apr 2013 10:36:53 +0300 Subject: [PATCH 247/258] Cleaned up some code repetition in embind --- src/embind/embind.js | 64 ++++++++++++++++++++++---------------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index f48bcf7b8..0361d8daa 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -1064,30 +1064,29 @@ function __embind_register_class_function( throwUnboundTypeError('Cannot call ' + humanName + ' due to unbound types', rawArgTypes); }; - var method = classType.registeredClass.instancePrototype[methodName]; + var proto = classType.registeredClass.instancePrototype; + var method = proto[methodName]; if (undefined === method || (undefined === method.overloadTable && method.className !== classType.name && method.argCount === argCount-2)) { // This is the first overload to be registered, OR we are replacing a function in the base class with a function in the derived class. - classType.registeredClass.instancePrototype[methodName] = unboundTypesHandler; - classType.registeredClass.instancePrototype[methodName].argCount = argCount-2; - classType.registeredClass.instancePrototype[methodName].className = classType.name; + 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. - if (undefined === classType.registeredClass.instancePrototype[methodName].overloadTable) { - var prevFunc = classType.registeredClass.instancePrototype[methodName]; - + if (undefined === method.overloadTable) { // Inject an overload resolver function that routes to the appropriate overload based on the number of arguments. - classType.registeredClass.instancePrototype[methodName] = function() { + proto[methodName] = function() { // TODO This check can be removed in -O3 level "unsafe" optimizations. - if (!classType.registeredClass.instancePrototype[methodName].overloadTable.hasOwnProperty(arguments.length)) { - throwBindingError("Member function '" + humanName + "' called with an invalid number of arguments (" + arguments.length + ") - expects one of (" + classType.registeredClass.instancePrototype[methodName].overloadTable + ")!"); + if (!proto[methodName].overloadTable.hasOwnProperty(arguments.length)) { + throwBindingError("Member function '" + humanName + "' called with an invalid number of arguments (" + arguments.length + ") - expects one of (" + proto[methodName].overloadTable + ")!"); } - return classType.registeredClass.instancePrototype[methodName].overloadTable[arguments.length].apply(this, arguments); + return proto[methodName].overloadTable[arguments.length].apply(this, arguments); }; // Move the previous function into the overload table. - classType.registeredClass.instancePrototype[methodName].overloadTable = []; - classType.registeredClass.instancePrototype[methodName].overloadTable[prevFunc.argCount] = prevFunc; + proto[methodName].overloadTable = []; + proto[methodName].overloadTable[method.argCount] = method; } - classType.registeredClass.instancePrototype[methodName].overloadTable[argCount-2] = unboundTypesHandler; + proto[methodName].overloadTable[argCount-2] = unboundTypesHandler; } whenDependentTypesAreResolved([], rawArgTypes, function(argTypes) { @@ -1113,10 +1112,10 @@ function __embind_register_class_function( // Replace the initial unbound-handler-stub function with the appropriate member function, now that all types // are resolved. If multiple overloads are registered for this function, the function goes into an overload table. - if (undefined === classType.registeredClass.instancePrototype[methodName].overloadTable) { - classType.registeredClass.instancePrototype[methodName] = memberFunction; + if (undefined === proto[methodName].overloadTable) { + proto[methodName] = memberFunction; } else { - classType.registeredClass.instancePrototype[methodName].overloadTable[argCount-2] = memberFunction; + proto[methodName].overloadTable[argCount-2] = memberFunction; } return []; @@ -1144,41 +1143,42 @@ function __embind_register_class_class_function( throwUnboundTypeError('Cannot call ' + humanName + ' due to unbound types', rawArgTypes); }; - if (undefined === classType.registeredClass.constructor[methodName]) { + var proto = classType.registeredClass.constructor; + if (undefined === proto[methodName]) { // This is the first function to be registered with this name. - classType.registeredClass.constructor[methodName] = unboundTypesHandler; - classType.registeredClass.constructor[methodName].argCount = argCount-1; + unboundTypesHandler.argCount = argCount-1; + proto[methodName] = unboundTypesHandler; } else { // There was an existing function to be registered with this name. Set up an overload table to // resolve between them. - if (undefined === classType.registeredClass.constructor[methodName].overloadTable) { - var prevFunc = classType.registeredClass.constructor[methodName]; + if (undefined === proto[methodName].overloadTable) { + var prevFunc = proto[methodName]; // Inject an overload handler function that resolves the proper function to call based on the // number of parameters passed to the function. - classType.registeredClass.constructor[methodName] = function() { + proto[methodName] = function() { // TODO This check can be removed in -O3 level "unsafe" optimizations. - if (!classType.registeredClass.constructor[methodName].overloadTable.hasOwnProperty(arguments.length)) { - throwBindingError("Static member function '" + humanName + "' called with an invalid number of arguments (" + arguments.length + ") - expects one of (" + classType.registeredClass.constructor[methodName].overloadTable + ")!"); + if (!proto[methodName].overloadTable.hasOwnProperty(arguments.length)) { + throwBindingError("Static member function '" + humanName + "' called with an invalid number of arguments (" + arguments.length + ") - expects one of (" + proto[methodName].overloadTable + ")!"); } - return classType.registeredClass.constructor[methodName].overloadTable[arguments.length].apply(this, arguments); + return proto[methodName].overloadTable[arguments.length].apply(this, arguments); }; - classType.registeredClass.constructor[methodName].overloadTable = []; + proto[methodName].overloadTable = []; // Move the old function into the overload table. - classType.registeredClass.constructor[methodName].overloadTable[prevFunc.argCount] = prevFunc; + proto[methodName].overloadTable[prevFunc.argCount] = prevFunc; } - classType.registeredClass.constructor[methodName].overloadTable[argCount-1] = unboundTypesHandler; + 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 func = makeInvoker(humanName, argCount, argTypes, rawInvoker, fn); - if (undefined === classType.registeredClass.constructor[methodName].overloadTable) { - classType.registeredClass.constructor[methodName] = func; + if (undefined === proto[methodName].overloadTable) { + proto[methodName] = func; } else { - classType.registeredClass.constructor[methodName].overloadTable[argCount-1] = func; + proto[methodName].overloadTable[argCount-1] = func; } return []; }); From 31f2ba4afbc2a5f5ae2ff859a3a5cecf1393c25e Mon Sep 17 00:00:00 2001 From: Jukka Jylanki Date: Thu, 4 Apr 2013 11:08:00 +0300 Subject: [PATCH 248/258] Remove code duplication on embind --- src/embind/embind.js | 54 ++++++++++++++++++-------------------------- 1 file changed, 22 insertions(+), 32 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index 0361d8daa..db10e03e1 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -1044,6 +1044,25 @@ function validateThis(this_, classType, humanName) { classType.registeredClass); } +// Creates a function overload resolution table to the given method 'methodName' in the given prototype, +// if the overload table doesn't yet exist. +function ensureOverloadTable(proto, methodName, humanName) { + if (undefined === proto[methodName].overloadTable) { + var prevFunc = proto[methodName]; + // Inject an overload resolver function that routes to the appropriate overload based on the number of arguments. + proto[methodName] = function() { + // TODO This check can be removed in -O3 level "unsafe" optimizations. + if (!proto[methodName].overloadTable.hasOwnProperty(arguments.length)) { + throwBindingError("Function '" + humanName + "' called with an invalid number of arguments (" + arguments.length + ") - expects one of (" + proto[methodName].overloadTable + ")!"); + } + return proto[methodName].overloadTable[arguments.length].apply(this, arguments); + }; + // Move the previous function into the overload table. + proto[methodName].overloadTable = []; + proto[methodName].overloadTable[prevFunc.argCount] = prevFunc; + } +} + function __embind_register_class_function( rawClassType, methodName, @@ -1073,19 +1092,7 @@ function __embind_register_class_function( proto[methodName] = unboundTypesHandler; } else { // There was an existing function with the same name registered. Set up a function overload routing table. - if (undefined === method.overloadTable) { - // Inject an overload resolver function that routes to the appropriate overload based on the number of arguments. - proto[methodName] = function() { - // TODO This check can be removed in -O3 level "unsafe" optimizations. - if (!proto[methodName].overloadTable.hasOwnProperty(arguments.length)) { - throwBindingError("Member function '" + humanName + "' called with an invalid number of arguments (" + arguments.length + ") - expects one of (" + proto[methodName].overloadTable + ")!"); - } - return proto[methodName].overloadTable[arguments.length].apply(this, arguments); - }; - // Move the previous function into the overload table. - proto[methodName].overloadTable = []; - proto[methodName].overloadTable[method.argCount] = method; - } + ensureOverloadTable(proto, methodName, humanName); proto[methodName].overloadTable[argCount-2] = unboundTypesHandler; } @@ -1149,25 +1156,8 @@ function __embind_register_class_class_function( unboundTypesHandler.argCount = argCount-1; proto[methodName] = unboundTypesHandler; } else { - // There was an existing function to be registered with this name. Set up an overload table to - // resolve between them. - if (undefined === proto[methodName].overloadTable) { - var prevFunc = proto[methodName]; - - // Inject an overload handler function that resolves the proper function to call based on the - // number of parameters passed to the function. - proto[methodName] = function() { - // TODO This check can be removed in -O3 level "unsafe" optimizations. - if (!proto[methodName].overloadTable.hasOwnProperty(arguments.length)) { - throwBindingError("Static member function '" + humanName + "' called with an invalid number of arguments (" + arguments.length + ") - expects one of (" + proto[methodName].overloadTable + ")!"); - } - return proto[methodName].overloadTable[arguments.length].apply(this, arguments); - }; - - proto[methodName].overloadTable = []; - // Move the old function into the overload table. - proto[methodName].overloadTable[prevFunc.argCount] = prevFunc; - } + // 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; } From 2466f601dbc72ff04421904e216cbd966530d0a1 Mon Sep 17 00:00:00 2001 From: Jukka Jylanki Date: Thu, 4 Apr 2013 11:49:51 +0300 Subject: [PATCH 249/258] More code duplication cleanup on embind --- src/embind/embind.js | 63 ++++++++++++++------------------------------ 1 file changed, 20 insertions(+), 43 deletions(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index db10e03e1..a38e0bcc6 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -39,6 +39,25 @@ function throwUnboundTypeError(message, types) { throw new UnboundTypeError(message + ': ' + unboundTypes.map(getTypeName).join([', '])); } +// Creates a function overload resolution table to the given method 'methodName' in the given prototype, +// if the overload table doesn't yet exist. +function ensureOverloadTable(proto, methodName, humanName) { + if (undefined === proto[methodName].overloadTable) { + var prevFunc = proto[methodName]; + // Inject an overload resolver function that routes to the appropriate overload based on the number of arguments. + proto[methodName] = function() { + // TODO This check can be removed in -O3 level "unsafe" optimizations. + if (!proto[methodName].overloadTable.hasOwnProperty(arguments.length)) { + throwBindingError("Function '" + humanName + "' called with an invalid number of arguments (" + arguments.length + ") - expects one of (" + proto[methodName].overloadTable + ")!"); + } + return proto[methodName].overloadTable[arguments.length].apply(this, arguments); + }; + // Move the previous function into the overload table. + proto[methodName].overloadTable = []; + proto[methodName].overloadTable[prevFunc.argCount] = prevFunc; + } +} + /* Registers a symbol (function, class, enum, ...) as part of the Module JS object so that hand-written code is able to access that symbol via 'Module.name'. name: The name of the symbol that's being exposed. @@ -56,25 +75,7 @@ function exposePublicSymbol(name, value, numArguments) { // We are exposing a function with the same name as an existing function. Create an overload table and a function selector // that routes between the two. - - // If we don't yet have an overload selector, install an overload selector that routes the function call to a table of overloads based on # of arguments to function. - if (undefined === Module[name].overloadTable) { - var prevFunc = Module[name]; - - // Inject an overload selector in place of the previous function. - Module[name] = function() { - // TODO This check can be removed in -O3 level "unsafe" optimizations. - if (!Module[name].overloadTable.hasOwnProperty(arguments.length)) { - throwBindingError("Function '" + name + "' called with an invalid number of arguments (" + arguments.length + ") - expects one of (" + Object.keys(Module[name].overloadTable) + ")!"); - } - return Module[name].overloadTable[arguments.length].apply(this, arguments); - }; - // An overloadTable maintains a registry of all function overloads. - Module[name].overloadTable = []; - // Move the old function into the overload table. - Module[name].overloadTable[prevFunc.numArguments] = prevFunc; - } - + ensureOverloadTable(Module, name, name); if (Module.hasOwnProperty(numArguments)) { throwBindingError("Cannot register multiple overloads of a function with the same number of arguments (" + numArguments + ")!"); } @@ -99,11 +100,6 @@ function replacePublicSymbol(name, value, numArguments) { } else { Module[name] = value; - /* XXX TODO unneeded?! - if (undefined !== numArguments) { - Module[name].numArguments = numArguments; - } - */ } } @@ -1044,25 +1040,6 @@ function validateThis(this_, classType, humanName) { classType.registeredClass); } -// Creates a function overload resolution table to the given method 'methodName' in the given prototype, -// if the overload table doesn't yet exist. -function ensureOverloadTable(proto, methodName, humanName) { - if (undefined === proto[methodName].overloadTable) { - var prevFunc = proto[methodName]; - // Inject an overload resolver function that routes to the appropriate overload based on the number of arguments. - proto[methodName] = function() { - // TODO This check can be removed in -O3 level "unsafe" optimizations. - if (!proto[methodName].overloadTable.hasOwnProperty(arguments.length)) { - throwBindingError("Function '" + humanName + "' called with an invalid number of arguments (" + arguments.length + ") - expects one of (" + proto[methodName].overloadTable + ")!"); - } - return proto[methodName].overloadTable[arguments.length].apply(this, arguments); - }; - // Move the previous function into the overload table. - proto[methodName].overloadTable = []; - proto[methodName].overloadTable[prevFunc.argCount] = prevFunc; - } -} - function __embind_register_class_function( rawClassType, methodName, From 9baac4cd7c98e5e8e3b96926ac300701dbdf0950 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Thu, 4 Apr 2013 01:53:47 -0700 Subject: [PATCH 250/258] Add support for index access to value_tuple and value_struct --- system/include/emscripten/bind.h | 63 +++++++++++++++++++++++++++++++- tests/embind/embind.test.js | 28 +++++++------- tests/embind/embind_test.cpp | 45 +++++++++++++---------- 3 files changed, 101 insertions(+), 35 deletions(-) diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index bd170dea6..81ee38e98 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -471,8 +471,30 @@ namespace emscripten { noncopyable(const noncopyable&) = delete; const noncopyable& operator=(const noncopyable&) = delete; }; + + template + struct ReturnType; + + template + struct ReturnType { + typedef RT type; + }; + + template + typename BindingType::WireType get_by_index(int index, ClassType& ptr) { + return BindingType::toWireType(ptr[index]); + } + + template + void set_by_index(int index, ClassType& ptr, typename BindingType::WireType wt) { + ptr[index] = BindingType::fromWireType(wt); + } } + template + struct index { + }; + //////////////////////////////////////////////////////////////////////////////// // VALUE TUPLES //////////////////////////////////////////////////////////////////////////////// @@ -526,7 +548,26 @@ namespace emscripten { reinterpret_cast(&SP::template set), SP::getContext(setter)); return *this; - } + } + + template + value_tuple& element(index) { + using namespace internal; + typedef + typename std::remove_reference< + typename ReturnType::type + >::type + ElementType; + _embind_register_tuple_element( + TypeID::get(), + TypeID::get(), + reinterpret_cast(&internal::get_by_index), + reinterpret_cast(Index), + TypeID::get(), + reinterpret_cast(&internal::set_by_index), + reinterpret_cast(Index)); + return *this; + } }; //////////////////////////////////////////////////////////////////////////////// @@ -588,6 +629,26 @@ namespace emscripten { SP::getContext(setter)); return *this; } + + template + value_struct& field(const char* fieldName, index) { + using namespace internal; + typedef + typename std::remove_reference< + typename ReturnType::type + >::type + ElementType; + _embind_register_struct_field( + TypeID::get(), + fieldName, + TypeID::get(), + reinterpret_cast(&internal::get_by_index), + reinterpret_cast(Index), + TypeID::get(), + reinterpret_cast(&internal::set_by_index), + reinterpret_cast(Index)); + return *this; + } }; //////////////////////////////////////////////////////////////////////////////// diff --git a/tests/embind/embind.test.js b/tests/embind/embind.test.js index 020719d5f..f365c1b86 100755 --- a/tests/embind/embind.test.js +++ b/tests/embind/embind.test.js @@ -977,32 +977,32 @@ module({ test("can return tuples by value", function() { var c = cm.emval_test_return_TupleVector(); - assert.deepEqual([1, 2, 3], c); + assert.deepEqual([1, 2, 3, 4], c); }); test("tuples can contain tuples", function() { var c = cm.emval_test_return_TupleVectorTuple(); - assert.deepEqual([[1, 2, 3]], c); + assert.deepEqual([[1, 2, 3, 4]], c); }); test("can pass tuples by value", function() { - var c = cm.emval_test_take_and_return_TupleVector([4, 5, 6]); - assert.deepEqual([4, 5, 6], c); + var c = cm.emval_test_take_and_return_TupleVector([4, 5, 6, 7]); + assert.deepEqual([4, 5, 6, 7], c); }); test("can return structs by value", function() { var c = cm.emval_test_return_StructVector(); - assert.deepEqual({x: 1, y: 2, z: 3}, c); + assert.deepEqual({x: 1, y: 2, z: 3, w: 4}, c); }); test("can pass structs by value", function() { - var c = cm.emval_test_take_and_return_StructVector({x: 4, y: 5, z: 6}); - assert.deepEqual({x: 4, y: 5, z: 6}, c); + var c = cm.emval_test_take_and_return_StructVector({x: 4, y: 5, z: 6, w: 7}); + assert.deepEqual({x: 4, y: 5, z: 6, w: 7}, c); }); test("can pass and return tuples in structs", function() { - var d = cm.emval_test_take_and_return_TupleInStruct({field: [1, 2, 3]}); - assert.deepEqual({field: [1, 2, 3]}, d); + var d = cm.emval_test_take_and_return_TupleInStruct({field: [1, 2, 3, 4]}); + assert.deepEqual({field: [1, 2, 3, 4]}, d); }); test("can clone handles", function() { @@ -1364,9 +1364,9 @@ module({ 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.deepEqual([1.25, 2.5, 3.75, 4], tv); + assert.deepEqual({x: 1.25, y: 2.5, z: 3.75, w:4}, sv); + }, 10, 1.5, [1.25, 2.5, 3.75, 4], {x: 1.25, y: 2.5, z: 3.75, w:4}); assert.true(called); }); }); @@ -1575,8 +1575,8 @@ module({ BaseFixture.extend("constants", function() { assert.equal(10, cm.INT_CONSTANT); assert.equal("some string", cm.STRING_CONSTANT); - assert.deepEqual([1, 2, 3], cm.VALUE_TUPLE_CONSTANT); - assert.deepEqual({x:1,y:2,z:3}, cm.VALUE_STRUCT_CONSTANT); + assert.deepEqual([1, 2, 3, 4], cm.VALUE_TUPLE_CONSTANT); + assert.deepEqual({x:1,y:2,z:3,w:4}, cm.VALUE_STRUCT_CONSTANT); }); }); diff --git a/tests/embind/embind_test.cpp b/tests/embind/embind_test.cpp index 1ef3fb169..ea332f2d9 100644 --- a/tests/embind/embind_test.cpp +++ b/tests/embind/embind_test.cpp @@ -721,7 +721,20 @@ std::map embind_test_get_string_int_map() { }; struct Vector { - float x, y, z; + Vector() = delete; + + Vector(float x_, float y_, float z_, float w_) + : x(x_) + , y(y_) + , z(z_) + , w(w_) + {} + + float x, y, z, w; + + float& operator[](int i) { + return (&x)[i]; + } float getY() const { return y; @@ -736,9 +749,13 @@ struct DummyDataToTestPointerAdjustment { }; struct TupleVector : DummyDataToTestPointerAdjustment, Vector { + TupleVector(): Vector(0, 0, 0, 0) {} + TupleVector(float x, float y, float z, float w): Vector(x, y, z, w) {} }; struct StructVector : DummyDataToTestPointerAdjustment, Vector { + StructVector(): Vector(0, 0, 0, 0) {} + StructVector(float x, float y, float z, float w): Vector(x, y, z, w) {} }; float readVectorZ(const Vector& v) { @@ -750,15 +767,11 @@ void writeVectorZ(Vector& v, float z) { } struct TupleVectorTuple { - TupleVector v; + TupleVector v = TupleVector(0, 0, 0, 0); }; TupleVector emval_test_return_TupleVector() { - TupleVector cv; - cv.x = 1; - cv.y = 2; - cv.z = 3; - return cv; + return TupleVector(1, 2, 3, 4); } TupleVector emval_test_take_and_return_TupleVector(TupleVector v) { @@ -772,11 +785,7 @@ TupleVectorTuple emval_test_return_TupleVectorTuple() { } StructVector emval_test_return_StructVector() { - StructVector v; - v.x = 1; - v.y = 2; - v.z = 3; - return v; + return StructVector(1, 2, 3, 4); } StructVector emval_test_take_and_return_StructVector(StructVector v) { @@ -1433,16 +1442,10 @@ EMSCRIPTEN_BINDINGS(constants) { constant("INT_CONSTANT", 10); constant("STRING_CONSTANT", std::string("some string")); - TupleVector tv; - tv.x = 1; - tv.y = 2; - tv.z = 3; + TupleVector tv(1, 2, 3, 4); constant("VALUE_TUPLE_CONSTANT", tv); - StructVector sv; - sv.x = 1; - sv.y = 2; - sv.z = 3; + StructVector sv(1, 2, 3, 4); constant("VALUE_STRUCT_CONSTANT", sv); } @@ -1484,6 +1487,7 @@ EMSCRIPTEN_BINDINGS(tests) { .element(&TupleVector::x) .element(&Vector::getY, &Vector::setY) .element(&readVectorZ, &writeVectorZ) + .element(index<3>()) ; function("emval_test_return_TupleVector", &emval_test_return_TupleVector); @@ -1499,6 +1503,7 @@ EMSCRIPTEN_BINDINGS(tests) { .field("x", &StructVector::x) .field("y", &Vector::getY, &Vector::setY) .field("z", &readVectorZ, &writeVectorZ) + .field("w", index<3>()) ; function("emval_test_return_StructVector", &emval_test_return_StructVector); From 545dc8ef97e90ffc1a5a62c59edffdc6e3b8e0b3 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Thu, 4 Apr 2013 02:03:48 -0700 Subject: [PATCH 251/258] support overloaded index access --- system/include/emscripten/bind.h | 22 ++++------------------ tests/embind/embind_test.cpp | 4 ++++ 2 files changed, 8 insertions(+), 18 deletions(-) diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index 81ee38e98..a0e825ad1 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -472,14 +472,6 @@ namespace emscripten { const noncopyable& operator=(const noncopyable&) = delete; }; - template - struct ReturnType; - - template - struct ReturnType { - typedef RT type; - }; - template typename BindingType::WireType get_by_index(int index, ClassType& ptr) { return BindingType::toWireType(ptr[index]); @@ -553,11 +545,8 @@ namespace emscripten { template value_tuple& element(index) { using namespace internal; - typedef - typename std::remove_reference< - typename ReturnType::type - >::type - ElementType; + ClassType* null = 0; + typedef typename std::remove_reference::type ElementType; _embind_register_tuple_element( TypeID::get(), TypeID::get(), @@ -633,11 +622,8 @@ namespace emscripten { template value_struct& field(const char* fieldName, index) { using namespace internal; - typedef - typename std::remove_reference< - typename ReturnType::type - >::type - ElementType; + ClassType* null = 0; + typedef typename std::remove_reference::type ElementType; _embind_register_struct_field( TypeID::get(), fieldName, diff --git a/tests/embind/embind_test.cpp b/tests/embind/embind_test.cpp index ea332f2d9..1384406a0 100644 --- a/tests/embind/embind_test.cpp +++ b/tests/embind/embind_test.cpp @@ -736,6 +736,10 @@ struct Vector { return (&x)[i]; } + const float& operator[](int i) const { + return (&x)[i]; + } + float getY() const { return y; } From 8ee0c6a12d1488c2fa131da25db91de0d2d44203 Mon Sep 17 00:00:00 2001 From: Jukka Jylanki Date: Thu, 4 Apr 2013 18:08:04 +0300 Subject: [PATCH 252/258] Use _embind_repr instead of IMVU.repr in embind.js, since IMVU.repr is not available in kripken/emscripten. --- src/embind/embind.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/embind/embind.js b/src/embind/embind.js index a38e0bcc6..ee717f4be 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -641,7 +641,7 @@ RegisteredPointer.prototype.toWireType = function(destructors, handle) { } } if (!(handle instanceof this.registeredClass.constructor)) { - throwBindingError('Expected null or instance of ' + this.name + ', got ' + IMVU.repr(handle)); + throwBindingError('Expected null or instance of ' + this.name + ', got ' + _embind_repr(handle)); } // TODO: this is not strictly true // We could support BY_EMVAL conversions from raw pointers to smart pointers From 01c2ac873fdd7c219bea0dfa8cf3ba9580d8a933 Mon Sep 17 00:00:00 2001 From: Bruce Mitchener Date: Thu, 4 Apr 2013 22:45:40 +0700 Subject: [PATCH 253/258] Stop using cxa_demangle in embind. With an updated libcxx, we no longer have libcxxabi in the include path, so we can't have the demangling code in the build. --- system/lib/embind/bind.cpp | 6 ++++++ tests/embind/embind.test.js | 18 +++++++++--------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/system/lib/embind/bind.cpp b/system/lib/embind/bind.cpp index 417d9ffd6..1619eddcb 100755 --- a/system/lib/embind/bind.cpp +++ b/system/lib/embind/bind.cpp @@ -1,5 +1,7 @@ #include +#ifdef USE_CXA_DEMANGLE #include <../lib/libcxxabi/include/cxxabi.h> +#endif #include #include #include @@ -11,6 +13,7 @@ using namespace emscripten; extern "C" { const char* EMSCRIPTEN_KEEPALIVE __getTypeName(const std::type_info* ti) { +#ifdef USE_CXA_DEMANGLE int stat; char* demangled = abi::__cxa_demangle(ti->name(), NULL, NULL, &stat); if (stat == 0 && demangled) { @@ -27,6 +30,9 @@ extern "C" { default: return strdup(""); } +#else + return strdup(ti->name()); +#endif } } diff --git a/tests/embind/embind.test.js b/tests/embind/embind.test.js index f365c1b86..290fed723 100755 --- a/tests/embind/embind.test.js +++ b/tests/embind/embind.test.js @@ -1487,7 +1487,7 @@ module({ function() { cm.getUnboundClass(); }, - 'Cannot call getUnboundClass due to unbound types: UnboundClass'); + 'Cannot call getUnboundClass due to unbound types: 12UnboundClass'); }); test("unbound base class produces error", function() { @@ -1495,14 +1495,14 @@ module({ function() { cm.getHasUnboundBase(); }, - 'Cannot call getHasUnboundBase due to unbound types: UnboundClass'); + 'Cannot call getHasUnboundBase due to unbound types: 12UnboundClass'); }); test("construct of class with unbound base", function() { assertMessage( function() { new cm.HasUnboundBase; - }, 'Cannot construct HasUnboundBase due to unbound types: UnboundClass'); + }, 'Cannot construct HasUnboundBase due to unbound types: 12UnboundClass'); }); test("unbound constructor argument", function() { @@ -1510,7 +1510,7 @@ module({ function() { new cm.HasConstructorUsingUnboundArgument(1); }, - 'Cannot construct HasConstructorUsingUnboundArgument due to unbound types: UnboundClass'); + 'Cannot construct HasConstructorUsingUnboundArgument due to unbound types: 12UnboundClass'); }); test("unbound constructor argument of class with unbound base", function() { @@ -1518,7 +1518,7 @@ module({ function() { new cm.HasConstructorUsingUnboundArgumentAndUnboundBase; }, - 'Cannot construct HasConstructorUsingUnboundArgumentAndUnboundBase due to unbound types: SecondUnboundClass'); + 'Cannot construct HasConstructorUsingUnboundArgumentAndUnboundBase due to unbound types: 18SecondUnboundClass'); }); test('class function with unbound argument', function() { @@ -1526,7 +1526,7 @@ module({ assertMessage( function() { x.method(); - }, 'Cannot call BoundClass.method due to unbound types: UnboundClass'); + }, 'Cannot call BoundClass.method due to unbound types: 12UnboundClass'); x.delete(); }); @@ -1534,7 +1534,7 @@ module({ assertMessage( function() { cm.BoundClass.classfunction(); - }, 'Cannot call BoundClass.classfunction due to unbound types: UnboundClass'); + }, 'Cannot call BoundClass.classfunction due to unbound types: 12UnboundClass'); }); test('class property of unbound type', function() { @@ -1543,11 +1543,11 @@ module({ assertMessage( function() { y = x.property; - }, 'Cannot access BoundClass.property due to unbound types: UnboundClass'); + }, 'Cannot access BoundClass.property due to unbound types: 12UnboundClass'); assertMessage( function() { x.property = 10; - }, 'Cannot access BoundClass.property due to unbound types: UnboundClass'); + }, 'Cannot access BoundClass.property due to unbound types: 12UnboundClass'); x.delete(); }); From a28ef129d6a56ae671d1f2d3aa287bbdaffc7442 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Fri, 5 Apr 2013 02:21:15 -0700 Subject: [PATCH 254/258] Allow instantiation of subclass wrappers with smart pointers. --- system/include/emscripten/bind.h | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index a0e825ad1..57e44c0c2 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -276,6 +276,11 @@ namespace emscripten { return new ClassType(args...); } + template + WrapperType wrapped_new(Args... args) { + return WrapperType(new ClassType(args...)); + } + template ClassType* raw_constructor( typename internal::BindingType::WireType... args @@ -808,7 +813,22 @@ namespace emscripten { template struct is_ptr> { enum { value = true }; - }; + }; + + template + struct SmartPtrIfNeeded { + template + SmartPtrIfNeeded(U& cls) { + cls.template smart_ptr(); + } + }; + + template + struct SmartPtrIfNeeded { + template + SmartPtrIfNeeded(U&) { + } + }; }; template @@ -892,17 +912,17 @@ namespace emscripten { return *this; } - template + template class_& allow_subclass() { using namespace internal; - class_>(typeid(WrapperType).name()) - .template constructor() + auto cls = class_>(typeid(WrapperType).name()) ; + SmartPtrIfNeeded _(cls); return class_function( "implement", - &operator_new, + &wrapped_new, allow_raw_pointer()); } From af586c3c8e712751f2f99e10aa71c749edaeca41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jukka=20Jyl=C3=A4nki?= Date: Fri, 12 Apr 2013 13:03:13 +0300 Subject: [PATCH 255/258] Revert "Bring back EMSCRIPTEN_KEEPALIVE" - instead directly use the __attribute__((used)) macro in embind/bind.cpp. This reverts commit cbf636a88bcfcabf084331fc4d8a445cacb158f9. --- system/include/emscripten/emscripten.h | 2 +- system/lib/embind/bind.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/system/include/emscripten/emscripten.h b/system/include/emscripten/emscripten.h index a4474670d..61634b0e4 100644 --- a/system/include/emscripten/emscripten.h +++ b/system/include/emscripten/emscripten.h @@ -24,7 +24,7 @@ extern "C" { * with closure, asm.js, etc. For example * -s EXPORTED_FUNCTIONS=["_main", "myfunc"] */ -#define EMSCRIPTEN_KEEPALIVE __attribute__((used)) +/* #define EMSCRIPTEN_KEEPALIVE __attribute__((used)) */ /* * Interface to the underlying JS engine. This function will diff --git a/system/lib/embind/bind.cpp b/system/lib/embind/bind.cpp index 1619eddcb..deb55138f 100755 --- a/system/lib/embind/bind.cpp +++ b/system/lib/embind/bind.cpp @@ -12,7 +12,7 @@ using namespace emscripten; extern "C" { - const char* EMSCRIPTEN_KEEPALIVE __getTypeName(const std::type_info* ti) { + const char* __attribute__((used)) __getTypeName(const std::type_info* ti) { #ifdef USE_CXA_DEMANGLE int stat; char* demangled = abi::__cxa_demangle(ti->name(), NULL, NULL, &stat); From 9437ede8d7d46da1715948f67b97d39475028e93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jukka=20Jyl=C3=A4nki?= Date: Fri, 12 Apr 2013 13:26:45 +0300 Subject: [PATCH 256/258] Update AUTHORS to refer to all people who contributed to embind. --- AUTHORS | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/AUTHORS b/AUTHORS index 87a656d66..d3463c8d4 100644 --- a/AUTHORS +++ b/AUTHORS @@ -24,7 +24,7 @@ a license to everyone to use it as detailed in LICENSE.) * Pierre Renaux * Brian Anderson * Jon Bardin -* Jukka Jylänki +* Jukka Jylänki * Aleksander Guryanov * Chad Austin (copyright owned by IMVU) * nandhp @@ -46,7 +46,7 @@ a license to everyone to use it as detailed in LICENSE.) * Anthony Liot * Michael Riss * Jasper St. Pierre -* Manuel Schölling +* Manuel Schölling * Bruce Mitchener, Jr. * Michael Bishop * Roger Braun @@ -57,9 +57,13 @@ a license to everyone to use it as detailed in LICENSE.) * Ting-Yuan Huang * Joshua Granick * Felix H. Dahlke -* Éloi Rivard +* Éloi Rivard * Alexander Gladysh -* Arlo Breault -* Jacob Lee (copyright owned by Google, Inc.) +* Arlo Breault * Jacob Lee (copyright owned by Google, Inc.) * Joe Lee (copyright owned by IMVU) - +* Andy Friesen (copyright owned by IMVU) +* Bill Welden (copyright owned by IMVU) +* Michael Ey (copyright owned by IMVU) +* Llorens Marti Garcia (copyright owned by IMVU) +* Jinsuck Kim (copyright owned by IMVU) +* Todd Lee (copyright owned by IMVU) From 2f7322d0d0987c66c6ab15d34357db8ab054ae74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jukka=20Jyl=C3=A4nki?= Date: Tue, 16 Apr 2013 11:44:51 +0300 Subject: [PATCH 257/258] Revert "Fix cxa_demangle compile errors". By feedback from azakai, we want to keep our libcxxabi clean from custom changes w.r.t. upsstream libcxxabi. This reverts commit 848c186df3a8f1eff6e7b1af0b08df25aefae0e3. --- system/lib/libcxxabi/src/cxa_demangle.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/system/lib/libcxxabi/src/cxa_demangle.cpp b/system/lib/libcxxabi/src/cxa_demangle.cpp index 804ea7ce5..c1e126035 100644 --- a/system/lib/libcxxabi/src/cxa_demangle.cpp +++ b/system/lib/libcxxabi/src/cxa_demangle.cpp @@ -5695,7 +5695,7 @@ public: if (__right_ != 0) r += __right_->size(); else if (__size_ != 0) - r += static_cast(snprintf(0, 0, "%lu", static_cast(__size_))); + r += static_cast(snprintf(0, 0, "%ld", __size_)); const_cast(__cached_size_) = static_cast(r); } return static_cast(__cached_size_); @@ -5710,7 +5710,7 @@ public: buf = __right_->get_demangled_name(buf); else if (__size_ != 0) { - int rs = sprintf(buf, "%lu", static_cast(__size_)); + int rs = sprintf(buf, "%ld", __size_); buf += rs; } *buf++ = ']'; @@ -5735,7 +5735,7 @@ public: if (__right_ != 0) r += __right_->size(); else if (__size_ != 0) - r += static_cast(snprintf(0, 0, "%lu", static_cast(__size_))); + r += static_cast(snprintf(0, 0, "%ld", __size_)); return r; } @@ -5747,7 +5747,7 @@ public: buf = __right_->get_demangled_name(buf); else if (__size_ != 0) { - int off = sprintf(buf, "%lu", static_cast(__size_)); + int off = sprintf(buf, "%ld", __size_); buf += off; } char* t = buf; From 8ae67d7642ebdd77a3c5f0d8fa7115fb372e98ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jukka=20Jyl=C3=A4nki?= Date: Tue, 16 Apr 2013 11:45:11 +0300 Subject: [PATCH 258/258] Fix newline typo in AUTHORS. --- AUTHORS | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/AUTHORS b/AUTHORS index d3463c8d4..1c6cf0ecd 100644 --- a/AUTHORS +++ b/AUTHORS @@ -59,7 +59,8 @@ a license to everyone to use it as detailed in LICENSE.) * Felix H. Dahlke * Éloi Rivard * Alexander Gladysh -* Arlo Breault * Jacob Lee (copyright owned by Google, Inc.) +* Arlo Breault +* Jacob Lee (copyright owned by Google, Inc.) * Joe Lee (copyright owned by IMVU) * Andy Friesen (copyright owned by IMVU) * Bill Welden (copyright owned by IMVU)