diff --git a/dtool/src/cppparser/cppBison.yxx b/dtool/src/cppparser/cppBison.yxx index 6ca6e73980..9cf217d404 100644 --- a/dtool/src/cppparser/cppBison.yxx +++ b/dtool/src/cppparser/cppBison.yxx @@ -21,7 +21,7 @@ #include "cppClassTemplateParameter.h" #include "cppTemplateParameterList.h" #include "cppInstanceIdentifier.h" -#include "cppTypedef.h" +#include "cppTypedefType.h" #include "cppTypeDeclaration.h" #include "cppVisibility.h" #include "cppIdentifier.h" @@ -241,7 +241,8 @@ pop_struct() { %token KW_BOOL %token KW_CATCH %token KW_CHAR -%token KW_WCHAR_T +%token KW_CHAR16_T +%token KW_CHAR32_T %token KW_CLASS %token KW_CONST %token KW_DELETE @@ -294,6 +295,7 @@ pop_struct() { %token KW_VIRTUAL %token KW_VOID %token KW_VOLATILE +%token KW_WCHAR_T %token KW_WHILE /* These special tokens are used to set the starting state of the @@ -325,8 +327,7 @@ pop_struct() { %type full_type %type anonymous_struct %type named_struct -%type anonymous_enum -%type named_enum +%type enum %type enum_keyword %type struct_keyword %type simple_type @@ -334,6 +335,7 @@ pop_struct() { %type simple_float_type %type simple_void_type %type class_derivation_name +%type enum_element_type /*%type typedefname*/ %type name %type string @@ -353,7 +355,7 @@ pop_struct() { /* Precedence rules. */ -%left IDENTIFIER TYPENAME_IDENTIFIER TYPEDEFNAME KW_ENUM ELLIPSIS KW_OPERATOR KW_TYPENAME KW_INT KW_SHORT KW_UNSIGNED KW_SIGNED KW_LONG KW_FLOAT KW_DOUBLE KW_CHAR KW_WCHAR_T KW_BOOL +%left IDENTIFIER TYPENAME_IDENTIFIER TYPEDEFNAME KW_ENUM ELLIPSIS KW_OPERATOR KW_TYPENAME KW_INT KW_SHORT KW_UNSIGNED KW_SIGNED KW_LONG KW_FLOAT KW_DOUBLE KW_CHAR KW_WCHAR_T KW_CHAR16_T KW_CHAR32_T KW_BOOL %left '{' ',' ';' @@ -717,7 +719,8 @@ typedef_declaration: if (inst != (CPPInstance *)NULL) { inst->_storage_class |= (current_storage_class | $1); current_scope->add_declaration(inst, global_scope, current_lexer, @2); - current_scope->add_declaration(new CPPTypedef(inst, current_scope == global_scope), global_scope, current_lexer, @2); + CPPTypedefType *typedef_type = new CPPTypedefType(inst->_type, inst->_ident, current_scope); + current_scope->add_declaration(typedef_type, global_scope, current_lexer, @2); } } } @@ -726,19 +729,15 @@ typedef_declaration: typedef_instance_identifiers: instance_identifier maybe_initialize_or_function_body { - CPPInstance *inst = new CPPInstance(current_type, $1, - current_storage_class, - @1.file); - inst->set_initializer($2); - current_scope->add_declaration(new CPPTypedef(inst, current_scope == global_scope), global_scope, current_lexer, @1); + CPPType *target_type = current_type; + CPPTypedefType *typedef_type = new CPPTypedefType(target_type, $1, current_scope, @1.file); + current_scope->add_declaration(typedef_type, global_scope, current_lexer, @1); } | instance_identifier maybe_initialize ',' typedef_instance_identifiers { - CPPInstance *inst = new CPPInstance(current_type, $1, - current_storage_class, - @1.file); - inst->set_initializer($2); - current_scope->add_declaration(new CPPTypedef(inst, current_scope == global_scope), global_scope, current_lexer, @1); + CPPType *target_type = current_type; + CPPTypedefType *typedef_type = new CPPTypedefType(target_type, $1, current_scope, @1.file); + current_scope->add_declaration(typedef_type, global_scope, current_lexer, @1); } ; @@ -746,20 +745,16 @@ typedef_const_instance_identifiers: instance_identifier maybe_initialize_or_function_body { $1->add_modifier(IIT_const); - CPPInstance *inst = new CPPInstance(current_type, $1, - current_storage_class, - @1.file); - inst->set_initializer($2); - current_scope->add_declaration(new CPPTypedef(inst, current_scope == global_scope), global_scope, current_lexer, @1); + CPPType *target_type = current_type; + CPPTypedefType *typedef_type = new CPPTypedefType(target_type, $1, current_scope, @1.file); + current_scope->add_declaration(typedef_type, global_scope, current_lexer, @1); } | instance_identifier maybe_initialize ',' typedef_const_instance_identifiers { $1->add_modifier(IIT_const); - CPPInstance *inst = new CPPInstance(current_type, $1, - current_storage_class, - @1.file); - inst->set_initializer($2); - current_scope->add_declaration(new CPPTypedef(inst, current_scope == global_scope), global_scope, current_lexer, @1); + CPPType *target_type = current_type; + CPPTypedefType *typedef_type = new CPPTypedefType(target_type, $1, current_scope, @1.file); + current_scope->add_declaration(typedef_type, global_scope, current_lexer, @1); } ; @@ -1585,11 +1580,7 @@ type: { $$ = CPPType::new_type($1); } - | anonymous_enum -{ - $$ = CPPType::new_type($1); -} - | named_enum + | enum { $$ = CPPType::new_type($1); } @@ -1609,7 +1600,7 @@ type: $$ = et; } } - | enum_keyword name + | enum_keyword name ':' enum_element_type { CPPType *type = $2->find_type(current_scope, global_scope, false, current_lexer); if (type != NULL) { @@ -1649,11 +1640,7 @@ type_decl: { $$ = new CPPTypeDeclaration(CPPType::new_type($1)); } - | anonymous_enum -{ - $$ = new CPPTypeDeclaration(CPPType::new_type($1)); -} - | named_enum + | enum { $$ = new CPPTypeDeclaration(CPPType::new_type($1)); } @@ -1673,7 +1660,7 @@ type_decl: $$ = et; } } - | enum_keyword name + | enum_keyword name ':' enum_element_type { CPPType *type = $2->find_type(current_scope, global_scope, false, current_lexer); if (type != NULL) { @@ -1858,27 +1845,41 @@ base_specification: } ; -anonymous_enum: - enum_keyword '{' -{ - current_enum = new CPPEnumType(NULL, current_scope, @1.file); -} - enum_body '}' +enum: + enum_decl '{' enum_body '}' { $$ = current_enum; current_enum = NULL; } ; -named_enum: - enum_keyword name '{' +enum_decl: + enum_keyword name ':' enum_element_type +{ + current_enum = new CPPEnumType($2, $4, current_scope, @1.file); +} + | enum_keyword name { current_enum = new CPPEnumType($2, current_scope, @1.file); } - enum_body '}' + | enum_keyword ':' enum_element_type { - $$ = current_enum; - current_enum = NULL; + current_enum = new CPPEnumType(NULL, $3, current_scope, @1.file); +} + | enum_keyword +{ + current_enum = new CPPEnumType(NULL, current_scope, @1.file); +} + ; + +enum_element_type: + simple_int_type +{ + $$ = CPPType::new_type($1); +} + | TYPENAME_IDENTIFIER +{ + $$ = $1->find_type(current_scope, global_scope, false, current_lexer); } ; @@ -1994,6 +1995,14 @@ simple_int_type: | KW_WCHAR_T { $$ = new CPPSimpleType(CPPSimpleType::T_wchar_t); +} + | KW_CHAR16_T +{ + $$ = new CPPSimpleType(CPPSimpleType::T_char16_t); +} + | KW_CHAR32_T +{ + $$ = new CPPSimpleType(CPPSimpleType::T_char32_t); } | KW_SHORT { @@ -2132,7 +2141,8 @@ element: | SCOPE | PLUSPLUS | MINUSMINUS | TIMESEQUAL | DIVIDEEQUAL | MODEQUAL | PLUSEQUAL | MINUSEQUAL | OREQUAL | ANDEQUAL | XOREQUAL | LSHIFTEQUAL | RSHIFTEQUAL - | KW_BOOL | KW_CATCH | KW_CHAR | KW_WCHAR_T | KW_CLASS | KW_CONST + | KW_BOOL | KW_CATCH | KW_CHAR | KW_CHAR16_T | KW_CHAR32_T + | KW_WCHAR_T | KW_CLASS | KW_CONST | KW_DELETE | KW_DOUBLE | KW_DYNAMIC_CAST | KW_ELSE | KW_ENUM | KW_EXTERN | KW_EXPLICIT | KW_FALSE | KW_FLOAT | KW_FRIEND | KW_FOR | KW_GOTO @@ -2369,6 +2379,18 @@ const_expr: CPPType *type = CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_wchar_t)); $$ = new CPPExpression(CPPExpression::construct_op(type, $3)); +} + | KW_CHAR16_T '(' optional_const_expr_comma ')' +{ + CPPType *type = + CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_char16_t)); + $$ = new CPPExpression(CPPExpression::construct_op(type, $3)); +} + | KW_CHAR32_T '(' optional_const_expr_comma ')' +{ + CPPType *type = + CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_char32_t)); + $$ = new CPPExpression(CPPExpression::construct_op(type, $3)); } | KW_BOOL '(' optional_const_expr_comma ')' { diff --git a/dtool/src/cppparser/cppDeclaration.cxx b/dtool/src/cppparser/cppDeclaration.cxx index c124e797c0..13c81e734d 100644 --- a/dtool/src/cppparser/cppDeclaration.cxx +++ b/dtool/src/cppparser/cppDeclaration.cxx @@ -177,13 +177,13 @@ as_class_template_parameter() { } //////////////////////////////////////////////////////////////////// -// Function: CPPDeclaration::as_typedef +// Function: CPPDeclaration::as_typedef_type // Access: Public, Virtual // Description: //////////////////////////////////////////////////////////////////// -CPPTypedef *CPPDeclaration:: -as_typedef() { - return (CPPTypedef *)NULL; +CPPTypedefType *CPPDeclaration:: +as_typedef_type() { + return (CPPTypedefType *)NULL; } //////////////////////////////////////////////////////////////////// @@ -399,3 +399,24 @@ is_less(const CPPDeclaration *other) const { return this < other; } + +ostream & +operator << (ostream &out, const CPPDeclaration::SubstDecl &subst) { + CPPDeclaration::SubstDecl::const_iterator it; + for (it = subst.begin(); it != subst.end(); ++it) { + out << " "; + if (it->first == NULL) { + out << "(null)"; + } else { + out << *(it->first); + } + out << " -> "; + if (it->second == NULL) { + out << "(null)"; + } else { + out << *(it->second); + } + out << "\n"; + } + return out; +} diff --git a/dtool/src/cppparser/cppDeclaration.h b/dtool/src/cppparser/cppDeclaration.h index ba4c515142..db15f1ee2a 100644 --- a/dtool/src/cppparser/cppDeclaration.h +++ b/dtool/src/cppparser/cppDeclaration.h @@ -30,7 +30,7 @@ using namespace std; class CPPInstance; class CPPTemplateParameterList; -class CPPTypedef; +class CPPTypedefType; class CPPTypeDeclaration; class CPPExpression; class CPPType; @@ -64,7 +64,6 @@ public: enum SubType { // Subtypes of CPPDeclaration ST_instance, - ST_typedef, ST_type_declaration, ST_expression, ST_type, @@ -87,6 +86,7 @@ public: ST_class_template_parameter, ST_tbd, ST_type_proxy, + ST_typedef, }; CPPDeclaration(const CPPFile &file); @@ -120,7 +120,7 @@ public: virtual CPPInstance *as_instance(); virtual CPPClassTemplateParameter *as_class_template_parameter(); - virtual CPPTypedef *as_typedef(); + virtual CPPTypedefType *as_typedef_type(); virtual CPPTypeDeclaration *as_type_declaration(); virtual CPPExpression *as_expression(); virtual CPPType *as_type(); @@ -157,4 +157,7 @@ operator << (ostream &out, const CPPDeclaration &decl) { return out; } +ostream & +operator << (ostream &out, const CPPDeclaration::SubstDecl &decl); + #endif diff --git a/dtool/src/cppparser/cppEnumType.cxx b/dtool/src/cppparser/cppEnumType.cxx index 48e1bda523..5a79d8aabb 100644 --- a/dtool/src/cppparser/cppEnumType.cxx +++ b/dtool/src/cppparser/cppEnumType.cxx @@ -13,7 +13,7 @@ //////////////////////////////////////////////////////////////////// #include "cppEnumType.h" -#include "cppTypedef.h" +#include "cppTypedefType.h" #include "cppExpression.h" #include "cppSimpleType.h" #include "cppConstType.h" @@ -24,14 +24,37 @@ //////////////////////////////////////////////////////////////////// // Function: CPPEnumType::Constructor // Access: Public -// Description: +// Description: Creates an untyped, unscoped enum. //////////////////////////////////////////////////////////////////// CPPEnumType:: CPPEnumType(CPPIdentifier *ident, CPPScope *current_scope, const CPPFile &file) : CPPExtensionType(T_enum, ident, current_scope, file), + _parent_scope(current_scope), + _element_type(NULL), _last_value(NULL) { + if (ident != NULL) { + ident->_native_scope = current_scope; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPEnumType::Constructor +// Access: Public +// Description: Creates a typed but unscoped enum. +//////////////////////////////////////////////////////////////////// +CPPEnumType:: +CPPEnumType(CPPIdentifier *ident, CPPType *element_type, + CPPScope *current_scope, const CPPFile &file) : + CPPExtensionType(T_enum, ident, current_scope, file), + _parent_scope(current_scope), + _element_type(element_type), + _last_value(NULL) +{ + if (ident != NULL) { + ident->_native_scope = current_scope; + } } //////////////////////////////////////////////////////////////////// @@ -41,12 +64,24 @@ CPPEnumType(CPPIdentifier *ident, CPPScope *current_scope, //////////////////////////////////////////////////////////////////// CPPInstance *CPPEnumType:: add_element(const string &name, CPPExpression *value) { - CPPType *type = - CPPType::new_type(new CPPConstType(new CPPSimpleType( - CPPSimpleType::T_int, CPPSimpleType::F_unsigned))); - CPPIdentifier *ident = new CPPIdentifier(name); - CPPInstance *inst = new CPPInstance(type, ident); + ident->_native_scope = _parent_scope; + CPPInstance *inst; + + static CPPType *default_element_type = NULL; + if (_element_type == NULL) { + // This enum is untyped. Use a suitable default, ie. 'int'. + if (default_element_type == NULL) { + default_element_type = + CPPType::new_type(new CPPConstType(new CPPSimpleType(CPPSimpleType::T_int, 0))); + } + + inst = new CPPInstance(default_element_type, ident); + } else { + // This enum has an explicit type, so use that. + inst = new CPPInstance(CPPType::new_type(new CPPConstType(_element_type)), ident); + } + _elements.push_back(inst); if (value == (CPPExpression *)NULL) { @@ -81,6 +116,38 @@ is_incomplete() const { return false; } +//////////////////////////////////////////////////////////////////// +// Function: CPPEnumType::is_fully_specified +// Access: Public, Virtual +// Description: Returns true if this declaration is an actual, +// factual declaration, or false if some part of the +// declaration depends on a template parameter which has +// not yet been instantiated. +//////////////////////////////////////////////////////////////////// +bool CPPEnumType:: +is_fully_specified() const { + if (!CPPDeclaration::is_fully_specified()) { + return false; + } + + if (_ident != NULL && !_ident->is_fully_specified()) { + return false; + } + + if (_element_type != NULL && !_element_type->is_fully_specified()) { + return false; + } + + Elements::const_iterator ei; + for (ei = _elements.begin(); ei != _elements.end(); ++ei) { + if (!(*ei)->is_fully_specified()) { + return false; + } + } + + return true; +} + //////////////////////////////////////////////////////////////////// // Function: CPPEnumType::substitute_decl // Access: Public, Virtual @@ -95,12 +162,34 @@ substitute_decl(CPPDeclaration::SubstDecl &subst, } CPPEnumType *rep = new CPPEnumType(*this); + if (_ident != NULL) { rep->_ident = _ident->substitute_decl(subst, current_scope, global_scope); } - if (rep->_ident == _ident) { + if (_element_type != NULL) { + rep->_element_type = + _element_type->substitute_decl(subst, current_scope, global_scope) + ->as_type(); + } + + bool any_changed = false; + + for (int i = 0; i < _elements.size(); ++i) { + CPPInstance *elem_rep = + _elements[i]->substitute_decl(subst, current_scope, global_scope) + ->as_instance(); + + if (elem_rep != _elements[i]) { + rep->_elements[i] = elem_rep; + any_changed = true; + } + } + + if (rep->_ident == _ident && + rep->_element_type == _element_type && + !any_changed) { delete rep; rep = this; } @@ -124,15 +213,14 @@ output(ostream &out, int indent_level, CPPScope *scope, bool complete) const { } out << _ident->get_local_name(scope); - } else if (!complete && !_typedefs.empty()) { - // If we have a typedef name, use it. - out << _typedefs.front()->get_local_name(scope); - } else { out << _type; if (_ident != NULL) { out << " " << _ident->get_local_name(scope); } + if (_element_type != NULL) { + out << " : " << _element_type->get_local_name(scope); + } out << " {\n"; Elements::const_iterator ei; diff --git a/dtool/src/cppparser/cppEnumType.h b/dtool/src/cppparser/cppEnumType.h index 35d36b28f4..6fd9b395af 100644 --- a/dtool/src/cppparser/cppEnumType.h +++ b/dtool/src/cppparser/cppEnumType.h @@ -34,12 +34,15 @@ class CPPEnumType : public CPPExtensionType { public: CPPEnumType(CPPIdentifier *ident, CPPScope *current_scope, const CPPFile &file); + CPPEnumType(CPPIdentifier *ident, CPPType *element_type, + CPPScope *current_scope, const CPPFile &file); CPPInstance *add_element(const string &name, CPPExpression *value = (CPPExpression *)NULL); virtual bool is_incomplete() const; + virtual bool is_fully_specified() const; virtual CPPDeclaration *substitute_decl(SubstDecl &subst, CPPScope *current_scope, CPPScope *global_scope); @@ -50,6 +53,9 @@ public: virtual CPPEnumType *as_enum_type(); + CPPScope *_parent_scope; + CPPType *_element_type; + typedef vector Elements; Elements _elements; CPPExpression *_last_value; diff --git a/dtool/src/cppparser/cppExpression.cxx b/dtool/src/cppparser/cppExpression.cxx index 2bd295193d..2b344c36ef 100644 --- a/dtool/src/cppparser/cppExpression.cxx +++ b/dtool/src/cppparser/cppExpression.cxx @@ -147,6 +147,30 @@ as_pointer() const { } } +//////////////////////////////////////////////////////////////////// +// Function: CPPExpression::Result::as_boolean +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool CPPExpression::Result:: +as_boolean() const { + switch (_type) { + case RT_integer: + return (_u._integer != 0); + + case RT_real: + return (_u._real != 0.0); + + case RT_pointer: + return (_u._pointer != NULL); + + default: + cerr << "Invalid type\n"; + assert(false); + return false; + } +} + //////////////////////////////////////////////////////////////////// // Function: CPPExpression::Result::output // Access: Public @@ -226,7 +250,7 @@ CPPExpression(CPPIdentifier *ident, CPPScope *current_scope, CPPDeclaration(CPPFile()) { CPPDeclaration *decl = - ident->find_symbol(current_scope, global_scope, error_sink); + ident->find_symbol(current_scope, global_scope); if (decl != NULL) { CPPInstance *inst = decl->as_instance(); @@ -245,6 +269,7 @@ CPPExpression(CPPIdentifier *ident, CPPScope *current_scope, _type = T_unknown_ident; _u._ident = ident; + _u._ident->_native_scope = current_scope; } //////////////////////////////////////////////////////////////////// @@ -801,6 +826,66 @@ determine_type() const { return NULL; // Compiler kludge; can't get here. } +//////////////////////////////////////////////////////////////////// +// Function: CPPExpression::is_fully_specified +// Access: Public, Virtual +// Description: Returns true if this declaration is an actual, +// factual declaration, or false if some part of the +// declaration depends on a template parameter which has +// not yet been instantiated. +//////////////////////////////////////////////////////////////////// +bool CPPExpression:: +is_fully_specified() const { + if (!CPPDeclaration::is_fully_specified()) { + return false; + } + + switch (_type) { + case T_integer: + case T_real: + case T_string: + return false; + + case T_variable: + return _u._variable->is_fully_specified(); + + case T_function: + return _u._fgroup->is_fully_specified(); + + case T_unknown_ident: + return _u._ident->is_fully_specified(); + + case T_typecast: + case T_construct: + case T_new: + return (_u._typecast._to->is_fully_specified() && + _u._typecast._op1->is_fully_specified()); + + case T_default_construct: + case T_default_new: + case T_sizeof: + return _u._typecast._to->is_fully_specified(); + + case T_trinary_operation: + if (!_u._op._op3->is_fully_specified()) { + return false; + } + // Fall through + + case T_binary_operation: + if (!_u._op._op2->is_fully_specified()) { + return false; + } + // Fall through + + case T_unary_operation: + return _u._op._op1->is_fully_specified(); + + default: + return true; + } +} + //////////////////////////////////////////////////////////////////// // Function: CPPExpression::substitute_decl // Access: Public, Virtual @@ -827,6 +912,7 @@ substitute_decl(CPPDeclaration::SubstDecl &subst, // Replacing the variable reference with another variable reference. rep->_u._variable = decl->as_instance(); any_changed = true; + } else if (decl->as_expression()) { // Replacing the variable reference with an expression. delete rep; @@ -836,6 +922,43 @@ substitute_decl(CPPDeclaration::SubstDecl &subst, } break; + case T_unknown_ident: + rep->_u._ident = _u._ident->substitute_decl(subst, current_scope, global_scope); + any_changed = any_changed || (rep->_u._ident != _u._ident); + + // See if we can define it now. + decl = rep->_u._ident->find_symbol(current_scope, global_scope, subst); + if (decl != NULL) { + CPPInstance *inst = decl->as_instance(); + if (inst != NULL) { + rep->_type = T_variable; + rep->_u._variable = inst; + any_changed = true; + + decl = inst->substitute_decl(subst, current_scope, global_scope); + if (decl != inst) { + if (decl->as_instance()) { + // Replacing the variable reference with another variable reference. + rep->_u._variable = decl->as_instance(); + + } else if (decl->as_expression()) { + // Replacing the variable reference with an expression. + delete rep; + rep = decl->as_expression(); + } + } + break; + } + CPPFunctionGroup *fgroup = decl->as_function_group(); + if (fgroup != NULL) { + rep->_type = T_function; + rep->_u._fgroup = fgroup; + any_changed = true; + } + } + + break; + case T_typecast: case T_construct: case T_new: @@ -909,6 +1032,9 @@ is_tbd() const { return true; + case T_unknown_ident: + return true; + case T_typecast: case T_construct: case T_new: @@ -996,8 +1122,12 @@ output(ostream &out, int indent_level, CPPScope *scope, bool) const { break; case T_variable: + // We can just refer to the variable by name, except if it's a + // private constant, in which case we have to compute the value, + // since we may have to use it in generated code. if (_u._variable->_type != NULL && - _u._variable->_initializer != NULL) { + _u._variable->_initializer != NULL && + _u._variable->_vis > V_public) { // A const variable. Fetch its assigned value. CPPConstType *const_type = _u._variable->_type->as_const_type(); if (const_type != NULL) { @@ -1434,4 +1564,3 @@ is_less(const CPPDeclaration *other) const { return false; } - diff --git a/dtool/src/cppparser/cppExpression.h b/dtool/src/cppparser/cppExpression.h index 9ad8f35cf1..cd535b7320 100644 --- a/dtool/src/cppparser/cppExpression.h +++ b/dtool/src/cppparser/cppExpression.h @@ -63,6 +63,7 @@ public: int as_integer() const; double as_real() const; void *as_pointer() const; + bool as_boolean() const; void output(ostream &out) const; ResultType _type; @@ -78,6 +79,7 @@ public: CPPType *determine_type() const; bool is_tbd() const; + virtual bool is_fully_specified() const; virtual CPPDeclaration *substitute_decl(SubstDecl &subst, CPPScope *current_scope, CPPScope *global_scope); diff --git a/dtool/src/cppparser/cppExtensionType.cxx b/dtool/src/cppparser/cppExtensionType.cxx index fc2a1d8845..26022cf8bf 100644 --- a/dtool/src/cppparser/cppExtensionType.cxx +++ b/dtool/src/cppparser/cppExtensionType.cxx @@ -14,7 +14,7 @@ #include "cppExtensionType.h" -#include "cppTypedef.h" +#include "cppTypedefType.h" #include "cppIdentifier.h" #include "cppParser.h" #include "indent.h" @@ -130,6 +130,29 @@ substitute_decl(CPPDeclaration::SubstDecl &subst, return rep; } +//////////////////////////////////////////////////////////////////// +// Function: CPPExtensionType::resolve_type +// Access: Public, Virtual +// Description: If this CPPType object is a forward reference or +// other nonspecified reference to a type that might now +// be known a real type, returns the real type. +// Otherwise returns the type itself. +//////////////////////////////////////////////////////////////////// +CPPType *CPPExtensionType:: +resolve_type(CPPScope *current_scope, CPPScope *global_scope) { + if (_ident == NULL) { + // We can't resolve anonymous types. But that's OK, since they + // can't be forward declared anyway. + return this; + } + + // Maybe it has been defined by now. + CPPType *type = _ident->find_type(current_scope, global_scope); + if (type != NULL) { + return type; + } + return this; +} //////////////////////////////////////////////////////////////////// // Function: CPPExtensionType::is_equivalent_type diff --git a/dtool/src/cppparser/cppExtensionType.h b/dtool/src/cppparser/cppExtensionType.h index f8771f70ea..d1d810209c 100644 --- a/dtool/src/cppparser/cppExtensionType.h +++ b/dtool/src/cppparser/cppExtensionType.h @@ -25,7 +25,9 @@ class CPPIdentifier; /////////////////////////////////////////////////////////////////// // Class : CPPExtensionType -// Description : +// Description : Base class of enum, class, struct, and union types. +// An instance of the base class (instead of one of +// the specializations) is used for forward references. //////////////////////////////////////////////////////////////////// class CPPExtensionType : public CPPType { public: @@ -50,6 +52,9 @@ public: CPPScope *current_scope, CPPScope *global_scope); + virtual CPPType *resolve_type(CPPScope *current_scope, + CPPScope *global_scope); + virtual bool is_equivalent(const CPPType &other) const; diff --git a/dtool/src/cppparser/cppIdentifier.cxx b/dtool/src/cppparser/cppIdentifier.cxx index 14cd926e50..5c44b45a5a 100644 --- a/dtool/src/cppparser/cppIdentifier.cxx +++ b/dtool/src/cppparser/cppIdentifier.cxx @@ -39,7 +39,7 @@ CPPIdentifier(const string &name, const CPPFile &file) : _file(file) { // Description: //////////////////////////////////////////////////////////////////// CPPIdentifier:: -CPPIdentifier(const CPPNameComponent &name) { +CPPIdentifier(const CPPNameComponent &name, const CPPFile &file) : _file(file) { _names.push_back(name); _native_scope = (CPPScope *)NULL; } @@ -146,8 +146,10 @@ get_local_name(CPPScope *scope) const { if (scope == NULL || (_native_scope == NULL && _names.size() == 1)) { result = _names.back().get_name_with_templ(scope); + } else if (_names.front().empty()) { result = get_fully_scoped_name(); + } else { // Determine the scope of everything up until but not including the // last name. @@ -382,6 +384,7 @@ find_type(CPPScope *current_scope, CPPScope *global_scope, if (scope == NULL) { return NULL; } + CPPType *type = scope->find_type(get_simple_name(), subst, global_scope); if (type != NULL && _names.back().has_templ()) { // This is a template type. @@ -398,7 +401,6 @@ find_type(CPPScope *current_scope, CPPScope *global_scope, assert(new_type != NULL); if (new_type == type) { type = CPPType::new_type(new CPPTBDType((CPPIdentifier *)this)); - // type = new_type; } else { type = new_type; } @@ -424,6 +426,7 @@ find_symbol(CPPScope *current_scope, CPPScope *global_scope, if (scope == NULL) { return NULL; } + CPPDeclaration *sym; if (!_names.back().has_templ()) { sym = scope->find_symbol(get_simple_name()); @@ -446,6 +449,43 @@ find_symbol(CPPScope *current_scope, CPPScope *global_scope, return sym; } +//////////////////////////////////////////////////////////////////// +// Function: CPPIdentifier::find_symbol +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPDeclaration *CPPIdentifier:: +find_symbol(CPPScope *current_scope, CPPScope *global_scope, + CPPDeclaration::SubstDecl &subst, + CPPPreprocessor *error_sink) const { + CPPScope *scope = get_scope(current_scope, global_scope, subst, error_sink); + if (scope == NULL) { + return NULL; + } + + CPPDeclaration *sym; + if (!_names.back().has_templ()) { + sym = scope->find_symbol(get_simple_name()); + + } else { + sym = scope->find_template(get_simple_name()); + + if (sym != NULL) { + CPPType *type = sym->as_type(); + if (type != NULL && type->is_incomplete()) { + // We can't instantiate an incomplete type. + sym = CPPType::new_type(new CPPTBDType((CPPIdentifier *)this)); + } else { + // Instantiate the symbol. + sym = sym->instantiate(_names.back().get_templ(), current_scope, + global_scope, error_sink); + } + } + } + + return sym; +} + //////////////////////////////////////////////////////////////////// // Function: CPPIdentifier::find_template // Access: Public diff --git a/dtool/src/cppparser/cppIdentifier.h b/dtool/src/cppparser/cppIdentifier.h index 14a57d926b..6c3e95b030 100644 --- a/dtool/src/cppparser/cppIdentifier.h +++ b/dtool/src/cppparser/cppIdentifier.h @@ -36,7 +36,7 @@ class CPPTemplateParameterList; class CPPIdentifier { public: CPPIdentifier(const string &name, const CPPFile &file = CPPFile()); - CPPIdentifier(const CPPNameComponent &name); + CPPIdentifier(const CPPNameComponent &name, const CPPFile &file = CPPFile()); void add_name(const string &name); void add_name(const CPPNameComponent &name); @@ -69,6 +69,10 @@ public: CPPDeclaration *find_symbol(CPPScope *current_scope, CPPScope *global_scope, CPPPreprocessor *error_sink = NULL) const; + CPPDeclaration *find_symbol(CPPScope *current_scope, + CPPScope *global_scope, + CPPDeclaration::SubstDecl &subst, + CPPPreprocessor *error_sink = NULL) const; CPPDeclaration *find_template(CPPScope *current_scope, CPPScope *global_scope, CPPPreprocessor *error_sink = NULL) const; diff --git a/dtool/src/cppparser/cppInstance.cxx b/dtool/src/cppparser/cppInstance.cxx index 6eeed02226..aaaff2eb24 100644 --- a/dtool/src/cppparser/cppInstance.cxx +++ b/dtool/src/cppparser/cppInstance.cxx @@ -372,6 +372,7 @@ CPPDeclaration *CPPInstance:: instantiate(const CPPTemplateParameterList *actual_params, CPPScope *current_scope, CPPScope *global_scope, CPPPreprocessor *error_sink) const { + if (!is_template()) { if (error_sink != NULL) { error_sink->warning("Ignoring template parameters for instance " + @@ -457,7 +458,6 @@ substitute_decl(CPPDeclaration::SubstDecl &subst, rep->_type = new_type->as_type(); if (rep->_type == NULL) { - cerr << "Type " << *_type << " became " << *new_type << " which isn't a type\n"; rep->_type = _type; } diff --git a/dtool/src/cppparser/cppManifest.cxx b/dtool/src/cppparser/cppManifest.cxx index e8843147ac..ff0d50e8e4 100644 --- a/dtool/src/cppparser/cppManifest.cxx +++ b/dtool/src/cppparser/cppManifest.cxx @@ -47,8 +47,8 @@ ExpansionNode(const string &str, bool paste) : //////////////////////////////////////////////////////////////////// CPPManifest:: CPPManifest(const string &args, const CPPFile &file) : - _file(file), _variadic_param(-1), + _file(file), _expr((CPPExpression *)NULL) { assert(!args.empty()); @@ -184,7 +184,7 @@ expand(const vector_string &args) const { // to a comma and no arguments are passed, the comma // is removed. MSVC does this automatically. Not sure // if we should allow MSVC behavior as well. - if (*result.rbegin() == ',') { + if (!result.empty() && *result.rbegin() == ',') { result.resize(result.size() - 1); } } diff --git a/dtool/src/cppparser/cppPreprocessor.cxx b/dtool/src/cppparser/cppPreprocessor.cxx index 591f856da6..42a786e729 100644 --- a/dtool/src/cppparser/cppPreprocessor.cxx +++ b/dtool/src/cppparser/cppPreprocessor.cxx @@ -200,6 +200,7 @@ CPPPreprocessor() { _warning_count = 0; _error_count = 0; + _error_abort = true; #ifdef CPP_VERBOSE_LEX _token_index = 0; #endif @@ -404,8 +405,7 @@ get_next_token0() { int token_type = IDENTIFIER; CPPDeclaration *decl = ident->find_symbol(current_scope, global_scope); - if (decl != NULL && - (decl->as_typedef() != NULL || decl->as_type() != NULL)) { + if (decl != NULL && decl->as_type() != NULL) { token_type = TYPENAME_IDENTIFIER; } @@ -432,10 +432,14 @@ warning(const string &message, int line, int col, CPPFile file) { if (file.empty()) { file = get_file(); } - indent(cerr, _files.size() * 2) + int indent_level = 0; + if (_verbose >= 3) { + indent_level = _files.size() * 2; + } + indent(cerr, indent_level) << "*** Warning in " << file << " near line " << line << ", column " << col << ":\n"; - indent(cerr, _files.size() * 2) + indent(cerr, indent_level) << message << "\n"; } _warning_count++; @@ -452,7 +456,7 @@ error(const string &message, int line, int col, CPPFile file) { // Don't report or log errors in the nested state. These will be // reported when the nesting level collapses. return; - }; + } if (_verbose >= 1) { if (line == 0) { @@ -462,11 +466,20 @@ error(const string &message, int line, int col, CPPFile file) { if (file.empty()) { file = get_file(); } - indent(cerr, _files.size() * 2) + int indent_level = 0; + if (_verbose >= 3) { + indent_level = _files.size() * 2; + } + indent(cerr, indent_level) << "*** Error in " << file << " near line " << line << ", column " << col << ":\n"; - indent(cerr, _files.size() * 2) + indent(cerr, indent_level) << message << "\n"; + + if (_error_abort) { + cerr << "Aborting.\n"; + abort(); + } } _error_count++; } @@ -603,7 +616,7 @@ init_type(const string &type) { //////////////////////////////////////////////////////////////////// bool CPPPreprocessor:: push_file(const CPPFile &file) { - if (_verbose >= 2) { + if (_verbose >= 3) { indent(cerr, _files.size() * 2) << "Reading " << file << "\n"; } @@ -2009,7 +2022,8 @@ check_keyword(const string &name) { if (name == "bool") return KW_BOOL; if (name == "catch") return KW_CATCH; if (name == "char") return KW_CHAR; - if (name == "wchar_t") return KW_WCHAR_T; + if (name == "char16_t") return KW_CHAR16_T; + if (name == "char32_t") return KW_CHAR32_T; if (name == "class") return KW_CLASS; if (name == "const") return KW_CONST; if (name == "delete") return KW_DELETE; @@ -2061,6 +2075,7 @@ check_keyword(const string &name) { if (name == "virtual") return KW_VIRTUAL; if (name == "void") return KW_VOID; if (name == "volatile") return KW_VOLATILE; + if (name == "wchar_t") return KW_WCHAR_T; if (name == "while") return KW_WHILE; // These are alternative ways to refer to built-in operators. @@ -2287,7 +2302,6 @@ unget(int c) { _unget = c; } - //////////////////////////////////////////////////////////////////// // Function: CPPPreprocessor::nested_parse_template_instantiation // Access: Private diff --git a/dtool/src/cppparser/cppPreprocessor.h b/dtool/src/cppparser/cppPreprocessor.h index 34647193c8..6d86a06846 100644 --- a/dtool/src/cppparser/cppPreprocessor.h +++ b/dtool/src/cppparser/cppPreprocessor.h @@ -209,6 +209,7 @@ private: int _warning_count; int _error_count; + bool _error_abort; }; #endif diff --git a/dtool/src/cppparser/cppScope.cxx b/dtool/src/cppparser/cppScope.cxx index d238d30fcd..565a9ef4e5 100644 --- a/dtool/src/cppparser/cppScope.cxx +++ b/dtool/src/cppparser/cppScope.cxx @@ -16,7 +16,7 @@ #include "cppScope.h" #include "cppDeclaration.h" #include "cppNamespace.h" -#include "cppTypedef.h" +#include "cppTypedefType.h" #include "cppTypeDeclaration.h" #include "cppExtensionType.h" #include "cppInstance.h" @@ -136,7 +136,7 @@ add_declaration(CPPDeclaration *decl, CPPScope *global_scope, _declarations.push_back(decl); - handle_declaration(decl, global_scope); + handle_declaration(decl, global_scope, preprocessor); } //////////////////////////////////////////////////////////////////// @@ -185,9 +185,12 @@ add_enum_value(CPPInstance *inst, CPPPreprocessor *preprocessor, // Description: //////////////////////////////////////////////////////////////////// void CPPScope:: -define_extension_type(CPPExtensionType *type) { +define_extension_type(CPPExtensionType *type, CPPPreprocessor *error_sink) { assert(type != NULL); - string name = type->get_simple_name(); + string name = type->get_local_name(this); + if (name.empty()) { + return; + } switch (type->_type) { case CPPExtensionType::T_class: @@ -204,31 +207,55 @@ define_extension_type(CPPExtensionType *type) { case CPPExtensionType::T_enum: _enums[name] = type; + break; } // Create an implicit typedef for the extension. - CPPIdentifier *ident = new CPPIdentifier(name); - CPPTypedef *td = new CPPTypedef(new CPPInstance(type, ident), false); - pair result = - _typedefs.insert(Typedefs::value_type(name, td)); + //CPPTypedefType *td = new CPPTypedefType(type, name); + pair result = + _types.insert(Types::value_type(name, type)); if (!result.second) { // There's already a typedef for this extension. This one - // overrides if it has template parameters and the other one - // doesn't. - CPPType *other_type = (*result.first).second->_type; - if (type->is_template() && !other_type->is_template()) { - (*result.first).second = td; + // overrides it only if the other is a forward declaration. + CPPType *other_type = (*result.first).second; - // Or if the other one is a forward reference. - } else if (other_type->get_subtype() == CPPDeclaration::ST_extension) { - (*result.first).second = td; + if (other_type->get_subtype() == CPPDeclaration::ST_extension) { + CPPExtensionType *other_ext = other_type->as_extension_type(); + + if (other_ext->_type != type->_type) { + if (error_sink != NULL) { + ostringstream errstr; + errstr << other_ext->_type << " " << type->get_fully_scoped_name() + << " was previously declared as " << other_ext->_type << "\n"; + error_sink->error(errstr.str()); + } + } + (*result.first).second = type; + + } else { + CPPTypedefType *other_td = other_type->as_typedef_type(); + + // Error out if the declaration is different than the previous one. + if (other_type != type && + (other_td == NULL || other_td->_type != type)) { + + if (error_sink != NULL) { + ostringstream errstr; + type->output(errstr, 0, NULL, false); + errstr << " has conflicting declaration as "; + other_type->output(errstr, 0, NULL, true); + error_sink->error(errstr.str()); + } + } } } if (type->is_template()) { + string simple_name = type->get_simple_name(); + pair result = - _templates.insert(Templates::value_type(name, type)); + _templates.insert(Templates::value_type(simple_name, type)); if (!result.second) { // The template was not inserted because we already had a @@ -278,7 +305,7 @@ add_using(CPPUsing *using_decl, CPPScope *global_scope, } else { CPPDeclaration *decl = using_decl->_ident->find_symbol(this, global_scope); if (decl != NULL) { - handle_declaration(decl, global_scope); + handle_declaration(decl, global_scope, error_sink); } else { if (error_sink != NULL) { error_sink->warning("Attempt to use unknown symbol: " + using_decl->_ident->get_fully_scoped_name()); @@ -392,11 +419,13 @@ instantiate(const CPPTemplateParameterList *actual_params, CPPDeclaration *decl = (*pi); CPPClassTemplateParameter *ctp = decl->as_class_template_parameter(); if (ctp != NULL) { - CPPInstance *inst = new CPPInstance(ctp, ctp->_ident); - CPPTypedef *td = new CPPTypedef(inst, true); - scope->_typedefs.insert(Typedefs::value_type - (ctp->_ident->get_local_name(), - td)); + //CPPTypedefType *td = new CPPTypedefType(ctp, ctp->_ident); + //scope->_typedefs.insert(Typedefs::value_type + // (ctp->_ident->get_local_name(), + // td)); + scope->_types.insert(Types::value_type + (ctp->_ident->get_local_name(), + ctp)); } } } @@ -466,10 +495,10 @@ substitute_decl(CPPDeclaration::SubstDecl &subst, //////////////////////////////////////////////////////////////////// CPPType *CPPScope:: find_type(const string &name, bool recurse) const { - Typedefs::const_iterator ti; - ti = _typedefs.find(name); - if (ti != _typedefs.end()) { - return (*ti).second->_type; + Types::const_iterator ti; + ti = _types.find(name); + if (ti != _types.end()) { + return ti->second; } Using::const_iterator ui; @@ -510,11 +539,11 @@ find_type(const string &name, bool recurse) const { CPPType *CPPScope:: find_type(const string &name, CPPDeclaration::SubstDecl &subst, CPPScope *global_scope, bool recurse) const { - Typedefs::const_iterator ti; - ti = _typedefs.find(name); - if (ti != _typedefs.end()) { + Types::const_iterator ti; + ti = _types.find(name); + if (ti != _types.end()) { CPPScope *current_scope = (CPPScope *)this; - return (*ti).second->_type->substitute_decl + return (*ti).second->substitute_decl (subst, current_scope, global_scope)->as_type(); } @@ -563,10 +592,14 @@ find_scope(const string &name, bool recurse) const { CPPType *type = (CPPType *)NULL; - Typedefs::const_iterator ti; - ti = _typedefs.find(name); - if (ti != _typedefs.end()) { - type = (*ti).second->_type; + Types::const_iterator ti; + ti = _types.find(name); + if (ti != _types.end()) { + type = (*ti).second; + // Resolve if this is a typedef. + while (type->as_typedef_type() != (CPPTypedefType *)NULL) { + type = type->as_typedef_type()->_type; + } } else if (_struct_type != NULL) { CPPStructType::Derivation::const_iterator di; @@ -614,10 +647,17 @@ find_scope(const string &name, CPPDeclaration::SubstDecl &subst, if (type == NULL) { return NULL; } + + // Resolve this if it is a typedef. + while (type->get_subtype() == CPPDeclaration::ST_typedef) { + type = type->as_typedef_type()->_type; + } + CPPStructType *st = type->as_struct_type(); if (st == NULL) { return NULL; } + return st->_scope; } @@ -632,10 +672,10 @@ find_symbol(const string &name, bool recurse) const { return _struct_type; } - Typedefs::const_iterator ti; - ti = _typedefs.find(name); - if (ti != _typedefs.end()) { - return (*ti).second->_type; + Types::const_iterator ti; + ti = _types.find(name); + if (ti != _types.end()) { + return (*ti).second; } Variables::const_iterator vi; @@ -756,9 +796,14 @@ get_local_name(CPPScope *scope) const { } */ - if (scope != NULL && _parent_scope != NULL && _parent_scope != scope) { - return _parent_scope->get_local_name(scope) + "::" + - _name.get_name_with_templ(); + if (scope != NULL && _parent_scope != NULL/* && _parent_scope != scope*/) { + string parent_scope_name = _parent_scope->get_local_name(scope); + if (parent_scope_name.empty()) { + return _name.get_name_with_templ(); + } else { + return parent_scope_name + "::" + + _name.get_name_with_templ(); + } } else { return _name.get_name_with_templ(); } @@ -817,8 +862,7 @@ write(ostream &out, int indent_level, CPPScope *scope) const { } bool complete = false; - if (cd->as_typedef() != NULL || cd->as_type() != NULL || - cd->as_namespace() != NULL) { + if (cd->as_type() != NULL || cd->as_namespace() != NULL) { complete = true; } @@ -881,7 +925,7 @@ copy_substitute_decl(CPPScope *to_scope, CPPDeclaration::SubstDecl &subst, } to_scope->_struct_type = new CPPStructType(_struct_type->_type, - new CPPIdentifier(to_scope->_name), + new CPPIdentifier(to_scope->_name, _struct_type->_file), native_scope, to_scope, _struct_type->_file); to_scope->_struct_type->_incomplete = false; @@ -986,11 +1030,11 @@ copy_substitute_decl(CPPScope *to_scope, CPPDeclaration::SubstDecl &subst, } } - Typedefs::const_iterator ti; - for (ti = _typedefs.begin(); ti != _typedefs.end(); ++ti) { - CPPTypedef *td = - (*ti).second->substitute_decl(subst, to_scope, global_scope)->as_typedef(); - to_scope->_typedefs.insert(Typedefs::value_type((*ti).first, td)); + Types::const_iterator ti; + for (ti = _types.begin(); ti != _types.end(); ++ti) { + CPPType *td = + (*ti).second->substitute_decl(subst, to_scope, global_scope)->as_type(); + to_scope->_types.insert(Types::value_type((*ti).first, td)); if (td != (*ti).second) { anything_changed = true; } @@ -1000,6 +1044,19 @@ copy_substitute_decl(CPPScope *to_scope, CPPDeclaration::SubstDecl &subst, CPPInstance *inst = (*vi).second->substitute_decl(subst, to_scope, global_scope)->as_instance(); to_scope->_variables.insert(Variables::value_type((*vi).first, inst)); + if (inst != (*vi).second) { + // I don't know if this _native_scope assignment is right, but it + // fixes some issues with variables in instantiated template scopes + // being printed out with an uninstantiated template scope prefix. ~rdb + inst->_ident->_native_scope = to_scope; + anything_changed = true; + } + } + + for (vi = _enum_values.begin(); vi != _enum_values.end(); ++vi) { + CPPInstance *inst = + (*vi).second->substitute_decl(subst, to_scope, global_scope)->as_instance(); + to_scope->_enum_values.insert(Variables::value_type((*vi).first, inst)); if (inst != (*vi).second) { anything_changed = true; } @@ -1027,25 +1084,41 @@ copy_substitute_decl(CPPScope *to_scope, CPPDeclaration::SubstDecl &subst, // functions, or whatever. //////////////////////////////////////////////////////////////////// void CPPScope:: -handle_declaration(CPPDeclaration *decl, CPPScope *global_scope) { - CPPTypedef *def = decl->as_typedef(); +handle_declaration(CPPDeclaration *decl, CPPScope *global_scope, + CPPPreprocessor *error_sink) { + CPPTypedefType *def = decl->as_typedef_type(); if (def != NULL) { string name = def->get_simple_name(); - _typedefs[name] = def; + + pair result = + _types.insert(Types::value_type(name, def)); + + if (!result.second) { + CPPType *other_type = result.first->second; + CPPTypedefType *other_td = other_type->as_typedef_type(); + + // We don't do redefinitions of typedefs. But we don't complain + // as long as this is actually a typedef to the previous definition. + if (other_type != def->_type && + (other_td == NULL || other_td->_type != def->_type)) { + + if (error_sink != NULL) { + ostringstream errstr; + def->output(errstr, 0, NULL, false); + errstr << " has conflicting declaration as "; + other_type->output(errstr, 0, NULL, true); + error_sink->error(errstr.str()); + } + } + } else { + _types[name] = def; + } CPPExtensionType *et = def->_type->as_extension_type(); if (et != NULL) { - define_extension_type(et); + define_extension_type(et, error_sink); } - if (!name.empty() && def->get_scope(this, global_scope) == this) { - // Don't add a new template definition if we already had one - // by the same name in another scope. - - if (find_template(name) == NULL) { - _templates.insert(Templates::value_type(name, def)); - } - } return; } @@ -1053,7 +1126,7 @@ handle_declaration(CPPDeclaration *decl, CPPScope *global_scope) { if (typedecl != (CPPTypeDeclaration *)NULL) { CPPExtensionType *et = typedecl->_type->as_extension_type(); if (et != NULL) { - define_extension_type(et); + define_extension_type(et, error_sink); } return; } @@ -1062,6 +1135,13 @@ handle_declaration(CPPDeclaration *decl, CPPScope *global_scope) { if (inst != NULL) { inst->check_for_constructor(this, global_scope); + if (inst->_ident != NULL) { + // Not sure if this is the best place to assign this. However, + // this fixes a bug with variables in expressions not having + // the proper scoping prefix. ~rdb + inst->_ident->_native_scope = this; + } + string name = inst->get_simple_name(); if (!name.empty() && inst->get_scope(this, global_scope) == this) { if (inst->_type->as_function_type()) { @@ -1108,6 +1188,6 @@ handle_declaration(CPPDeclaration *decl, CPPScope *global_scope) { CPPExtensionType *et = decl->as_extension_type(); if (et != NULL) { - define_extension_type(et); + define_extension_type(et, error_sink); } } diff --git a/dtool/src/cppparser/cppScope.h b/dtool/src/cppparser/cppScope.h index 324eb91766..e7bccbd7fe 100644 --- a/dtool/src/cppparser/cppScope.h +++ b/dtool/src/cppparser/cppScope.h @@ -34,7 +34,7 @@ class CPPExtensionType; class CPPStructType; class CPPNamespace; class CPPUsing; -class CPPTypedef; +class CPPTypedefType; class CPPInstance; class CPPFunctionGroup; class CPPTemplateScope; @@ -66,7 +66,8 @@ public: virtual void add_enum_value(CPPInstance *inst, CPPPreprocessor *preprocessor, const cppyyltype &pos); - virtual void define_extension_type(CPPExtensionType *type); + virtual void define_extension_type(CPPExtensionType *type, + CPPPreprocessor *error_sink = NULL); virtual void define_namespace(CPPNamespace *scope); virtual void add_using(CPPUsing *using_decl, CPPScope *global_scope, CPPPreprocessor *error_sink = NULL); @@ -113,7 +114,8 @@ private: copy_substitute_decl(CPPScope *to_scope, CPPDeclaration::SubstDecl &subst, CPPScope *global_scope) const; - void handle_declaration(CPPDeclaration *decl, CPPScope *global_scope); + void handle_declaration(CPPDeclaration *decl, CPPScope *global_scope, + CPPPreprocessor *error_sink = NULL); public: typedef vector Declarations; @@ -128,8 +130,8 @@ public: typedef map Namespaces; Namespaces _namespaces; - typedef map Typedefs; - Typedefs _typedefs; + typedef map Types; + Types _types; typedef map Variables; Variables _variables; Variables _enum_values; diff --git a/dtool/src/cppparser/cppSimpleType.cxx b/dtool/src/cppparser/cppSimpleType.cxx index b7ee4b4da7..7ce7048848 100644 --- a/dtool/src/cppparser/cppSimpleType.cxx +++ b/dtool/src/cppparser/cppSimpleType.cxx @@ -99,6 +99,10 @@ output(ostream &out, int, CPPScope *, bool) const { } switch (_type) { + case T_unknown: + out << "unknown"; + break; + case T_bool: out << "bool"; break; @@ -111,6 +115,14 @@ output(ostream &out, int, CPPScope *, bool) const { out << "wchar_t"; break; + case T_char16_t: + out << "char16_t"; + break; + + case T_char32_t: + out << "char32_t"; + break; + case T_int: out << "int"; break; @@ -127,10 +139,6 @@ output(ostream &out, int, CPPScope *, bool) const { out << "void"; break; - case T_unknown: - out << "unknown"; - break; - case T_parameter: out << "parameter"; break; diff --git a/dtool/src/cppparser/cppSimpleType.h b/dtool/src/cppparser/cppSimpleType.h index e8b9a4c6dc..6ac032456b 100644 --- a/dtool/src/cppparser/cppSimpleType.h +++ b/dtool/src/cppparser/cppSimpleType.h @@ -26,14 +26,16 @@ class CPPSimpleType : public CPPType { public: enum Type { + T_unknown, T_bool, T_char, - T_wchar_t, // Not strictly a builtin type, but we pretend. + T_wchar_t, + T_char16_t, + T_char32_t, T_int, T_float, T_double, T_void, - T_unknown, // T_parameter is a special type which is assigned to expressions // that are discovered where a formal parameter was expected. diff --git a/dtool/src/cppparser/cppStructType.cxx b/dtool/src/cppparser/cppStructType.cxx index 48460ea87f..0c138f586d 100644 --- a/dtool/src/cppparser/cppStructType.cxx +++ b/dtool/src/cppparser/cppStructType.cxx @@ -14,7 +14,7 @@ #include "cppStructType.h" -#include "cppTypedef.h" +#include "cppTypedefType.h" #include "cppScope.h" #include "cppTypeProxy.h" #include "cppTemplateScope.h" @@ -91,6 +91,13 @@ operator = (const CPPStructType ©) { void CPPStructType:: append_derivation(CPPType *base, CPPVisibility vis, bool is_virtual) { if (base != NULL) { + // Unwrap any typedefs, since we can't inherit from a typedef. + CPPTypedefType *def = base->as_typedef_type(); + while (def != NULL) { + base = def->_type; + def = base->as_typedef_type(); + } + Base b; b._base = base; b._vis = vis; @@ -110,7 +117,6 @@ get_scope() const { return _scope; } - //////////////////////////////////////////////////////////////////// // Function: CPPStructType::is_abstract // Access: Public @@ -246,7 +252,7 @@ instantiate(const CPPTemplateParameterList *actual_params, // don't yet know what its associated struct type will be. // Postpone the evaluation of this type. - CPPIdentifier *ident = new CPPIdentifier(get_fully_scoped_name()); + CPPIdentifier *ident = new CPPIdentifier(get_fully_scoped_name(), _file); return CPPType::new_type(new CPPTBDType(ident)); } @@ -318,7 +324,7 @@ substitute_decl(CPPDeclaration::SubstDecl &subst, rep->_template_scope = (CPPTemplateScope *)NULL; CPPNameComponent nc(get_simple_name()); nc.set_templ(pscope->_name.get_templ()); - rep->_ident = new CPPIdentifier(nc); + rep->_ident = new CPPIdentifier(nc, _file); } } } @@ -356,7 +362,6 @@ substitute_decl(CPPDeclaration::SubstDecl &subst, assert(rep != NULL); if (rep != this) { _instantiations.insert(rep); - // cerr << "Subst for " << *this << " is " << *rep << "\n"; } return rep; } @@ -377,15 +382,9 @@ output(ostream &out, int indent_level, CPPScope *scope, bool complete) const { if (is_template()) { CPPTemplateScope *tscope = get_template_scope(); - out << "< "; tscope->_parameters.output(out, scope); - out << " >"; } - } else if (!complete && !_typedefs.empty()) { - // If we have a typedef name, use it. - out << _typedefs.front()->get_local_name(scope); - } else { if (is_template()) { get_template_scope()->_parameters.write_formal(out, scope); diff --git a/dtool/src/cppparser/cppTemplateParameterList.cxx b/dtool/src/cppparser/cppTemplateParameterList.cxx index 2a2dcad7db..ebb40308b2 100644 --- a/dtool/src/cppparser/cppTemplateParameterList.cxx +++ b/dtool/src/cppparser/cppTemplateParameterList.cxx @@ -52,6 +52,7 @@ void CPPTemplateParameterList:: build_subst_decl(const CPPTemplateParameterList &formal_params, CPPDeclaration::SubstDecl &subst, CPPScope *current_scope, CPPScope *global_scope) const { + Parameters::const_iterator pfi, pai; for (pfi = formal_params._parameters.begin(), pai = _parameters.begin(); pfi != formal_params._parameters.end() && pai != _parameters.end(); diff --git a/dtool/src/cppparser/cppTemplateScope.cxx b/dtool/src/cppparser/cppTemplateScope.cxx index e4b6f5a922..7f5fdbb089 100644 --- a/dtool/src/cppparser/cppTemplateScope.cxx +++ b/dtool/src/cppparser/cppTemplateScope.cxx @@ -17,7 +17,7 @@ #include "cppExtensionType.h" #include "cppClassTemplateParameter.h" #include "cppIdentifier.h" -#include "cppTypedef.h" +#include "cppTypedefType.h" //////////////////////////////////////////////////////////////////// // Function: CPPTemplateScope::Constructor @@ -64,10 +64,10 @@ add_enum_value(CPPInstance *inst, CPPPreprocessor *preprocessor, // Description: //////////////////////////////////////////////////////////////////// void CPPTemplateScope:: -define_extension_type(CPPExtensionType *type) { +define_extension_type(CPPExtensionType *type, CPPPreprocessor *error_sink) { type->_template_scope = this; assert(_parent_scope != NULL); - _parent_scope->define_extension_type(type); + _parent_scope->define_extension_type(type, error_sink); } //////////////////////////////////////////////////////////////////// @@ -105,8 +105,9 @@ add_template_parameter(CPPDeclaration *param) { if (cl != NULL) { // Create an implicit typedef for this class parameter. string name = cl->_ident->get_local_name(); - _typedefs[name] = new CPPTypedef(new CPPInstance(cl, cl->_ident), false); + _types[name] = cl; } + CPPInstance *inst = param->as_instance(); if (inst != NULL) { // Register the variable for this value parameter. diff --git a/dtool/src/cppparser/cppTemplateScope.h b/dtool/src/cppparser/cppTemplateScope.h index 40a7feb74c..b8fa313e23 100644 --- a/dtool/src/cppparser/cppTemplateScope.h +++ b/dtool/src/cppparser/cppTemplateScope.h @@ -39,7 +39,8 @@ public: virtual void add_enum_value(CPPInstance *inst, CPPPreprocessor *preprocessor, const cppyyltype &pos); - virtual void define_extension_type(CPPExtensionType *type); + virtual void define_extension_type(CPPExtensionType *type, + CPPPreprocessor *error_sink = NULL); virtual void define_namespace(CPPNamespace *scope); virtual void add_using(CPPUsing *using_decl, CPPScope *global_scope, CPPPreprocessor *error_sink = NULL); diff --git a/dtool/src/cppparser/cppType.cxx b/dtool/src/cppparser/cppType.cxx index 45f37f2dc7..bad935dba6 100644 --- a/dtool/src/cppparser/cppType.cxx +++ b/dtool/src/cppparser/cppType.cxx @@ -14,7 +14,7 @@ #include "cppType.h" -#include "cppTypedef.h" +#include "cppTypedefType.h" #include CPPType::Types CPPType::_types; diff --git a/dtool/src/cppparser/cppType.h b/dtool/src/cppparser/cppType.h index dfc2364e76..1d962e8d69 100644 --- a/dtool/src/cppparser/cppType.h +++ b/dtool/src/cppparser/cppType.h @@ -22,7 +22,7 @@ #include class CPPType; -class CPPTypedef; +class CPPTypedefType; class CPPTypeDeclaration; @@ -39,7 +39,7 @@ public: //////////////////////////////////////////////////////////////////// class CPPType : public CPPDeclaration { public: - typedef vector Typedefs; + typedef vector Typedefs; Typedefs _typedefs; CPPType(const CPPFile &file); diff --git a/dtool/src/cppparser/cppTypeProxy.cxx b/dtool/src/cppparser/cppTypeProxy.cxx index 88e22dd494..89fde2473a 100644 --- a/dtool/src/cppparser/cppTypeProxy.cxx +++ b/dtool/src/cppparser/cppTypeProxy.cxx @@ -357,6 +357,19 @@ as_tbd_type() { return _actual_type->as_tbd_type(); } +//////////////////////////////////////////////////////////////////// +// Function: CPPTypeProxy::as_typedef_type +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPTypedefType *CPPTypeProxy:: +as_typedef_type() { + if (_actual_type == (CPPType *)NULL) { + return (CPPTypedefType *)NULL; + } + return _actual_type->as_typedef_type(); +} + //////////////////////////////////////////////////////////////////// // Function: CPPTypeProxy::as_type_proxy // Access: Public, Virtual @@ -366,4 +379,3 @@ CPPTypeProxy *CPPTypeProxy:: as_type_proxy() { return this; } - diff --git a/dtool/src/cppparser/cppTypeProxy.h b/dtool/src/cppparser/cppTypeProxy.h index f7f38558e4..c49ad008a0 100644 --- a/dtool/src/cppparser/cppTypeProxy.h +++ b/dtool/src/cppparser/cppTypeProxy.h @@ -19,7 +19,6 @@ #include "cppType.h" - /////////////////////////////////////////////////////////////////// // Class : CPPTypeProxy // Description : This is a special kind of type that is a placeholder @@ -66,6 +65,7 @@ public: virtual CPPStructType *as_struct_type(); virtual CPPEnumType *as_enum_type(); virtual CPPTBDType *as_tbd_type(); + virtual CPPTypedefType *as_typedef_type(); virtual CPPTypeProxy *as_type_proxy(); CPPType *_actual_type; diff --git a/dtool/src/cppparser/cppTypedef.cxx b/dtool/src/cppparser/cppTypedef.cxx deleted file mode 100644 index 7bb8002f61..0000000000 --- a/dtool/src/cppparser/cppTypedef.cxx +++ /dev/null @@ -1,91 +0,0 @@ -// Filename: cppTypedef.cxx -// Created by: drose (19Oct99) -// -//////////////////////////////////////////////////////////////////// -// -// PANDA 3D SOFTWARE -// Copyright (c) Carnegie Mellon University. All rights reserved. -// -// All use of this software is subject to the terms of the revised BSD -// license. You should have received a copy of this license along -// with this source code in a file named "LICENSE." -// -//////////////////////////////////////////////////////////////////// - - -#include "cppTypedef.h" - -//////////////////////////////////////////////////////////////////// -// Function: CPPTypedef::Constructor -// Access: Public -// Description: Constructs a new CPPTypedef object based on the -// indicated CPPInstance object. The CPPInstance is -// deallocated. -// -// If global is true, the typedef is defined at the -// global scope, and hence it's worth telling the type -// itself about. Otherwise, it's just a locally-scoped -// typedef. -//////////////////////////////////////////////////////////////////// -CPPTypedef:: -CPPTypedef(CPPInstance *inst, bool global) : CPPInstance(*inst) -{ - // Actually, we'll avoid deleting this for now. It causes problems - // for some reason to be determined later. - // delete inst; - - assert(_type != NULL); - if (global) { - _type->_typedefs.push_back(this); - CPPType::record_alt_name_for(_type, inst->get_local_name()); - } -} - -//////////////////////////////////////////////////////////////////// -// Function: CPPTypedef::substitute_decl -// Access: Public, Virtual -// Description: -//////////////////////////////////////////////////////////////////// -CPPDeclaration *CPPTypedef:: -substitute_decl(CPPDeclaration::SubstDecl &subst, - CPPScope *current_scope, CPPScope *global_scope) { - CPPDeclaration *decl = - CPPInstance::substitute_decl(subst, current_scope, global_scope); - assert(decl != NULL); - if (decl->as_typedef()) { - return decl; - } - assert(decl->as_instance() != NULL); - return new CPPTypedef(new CPPInstance(*decl->as_instance()), false); -} - -//////////////////////////////////////////////////////////////////// -// Function: CPPTypedef::output -// Access: Public, Virtual -// Description: -//////////////////////////////////////////////////////////////////// -void CPPTypedef:: -output(ostream &out, int indent_level, CPPScope *scope, bool) const { - out << "typedef "; - CPPInstance::output(out, indent_level, scope, false); -} - -//////////////////////////////////////////////////////////////////// -// Function: CPPTypedef::get_subtype -// Access: Public, Virtual -// Description: -//////////////////////////////////////////////////////////////////// -CPPDeclaration::SubType CPPTypedef:: -get_subtype() const { - return ST_typedef; -} - -//////////////////////////////////////////////////////////////////// -// Function: CPPTypedef::as_typedef -// Access: Public, Virtual -// Description: -//////////////////////////////////////////////////////////////////// -CPPTypedef *CPPTypedef:: -as_typedef() { - return this; -} diff --git a/dtool/src/cppparser/cppTypedef.h b/dtool/src/cppparser/cppTypedef.h deleted file mode 100644 index 6e8bfa5d53..0000000000 --- a/dtool/src/cppparser/cppTypedef.h +++ /dev/null @@ -1,42 +0,0 @@ -// Filename: cppTypedef.h -// Created by: drose (19Oct99) -// -//////////////////////////////////////////////////////////////////// -// -// PANDA 3D SOFTWARE -// Copyright (c) Carnegie Mellon University. All rights reserved. -// -// All use of this software is subject to the terms of the revised BSD -// license. You should have received a copy of this license along -// with this source code in a file named "LICENSE." -// -//////////////////////////////////////////////////////////////////// - -#ifndef CPPTYPEDEF_H -#define CPPTYPEDEF_H - -#include "dtoolbase.h" - -#include "cppInstance.h" - -/////////////////////////////////////////////////////////////////// -// Class : CPPTypedef -// Description : -//////////////////////////////////////////////////////////////////// -class CPPTypedef : public CPPInstance { -public: - CPPTypedef(CPPInstance *instance, bool global); - - virtual CPPDeclaration *substitute_decl(SubstDecl &subst, - CPPScope *current_scope, - CPPScope *global_scope); - - virtual void output(ostream &out, int indent_level, CPPScope *scope, - bool complete) const; - virtual SubType get_subtype() const; - - virtual CPPTypedef *as_typedef(); -}; - -#endif - diff --git a/dtool/src/cppparser/cppTypedefType.cxx b/dtool/src/cppparser/cppTypedefType.cxx new file mode 100644 index 0000000000..b1c86d9da2 --- /dev/null +++ b/dtool/src/cppparser/cppTypedefType.cxx @@ -0,0 +1,388 @@ +// Filename: cppTypedefType.cxx +// Created by: rdb (01Aug14) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#include "cppTypedefType.h" +#include "cppIdentifier.h" +#include "cppInstanceIdentifier.h" + +//////////////////////////////////////////////////////////////////// +// Function: CPPTypedefType::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPTypedefType:: +CPPTypedefType(CPPType *type, const string &name, CPPScope *current_scope) : + CPPType(CPPFile()), + _type(type), + _ident(new CPPIdentifier(name)) +{ + if (_ident != NULL) { + _ident->_native_scope = current_scope; + } + + _subst_decl_recursive_protect = false; + + //assert(_type != NULL); + //if (global) { + // _type->_typedefs.push_back(this); + // CPPType::record_alt_name_for(_type, inst->get_local_name()); + //} +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTypedefType::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPTypedefType:: +CPPTypedefType(CPPType *type, CPPIdentifier *ident, CPPScope *current_scope) : + CPPType(CPPFile()), + _type(type), + _ident(ident) +{ + if (_ident != NULL) { + _ident->_native_scope = current_scope; + } + _subst_decl_recursive_protect = false; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTypedefType::Constructor +// Access: Public +// Description: Constructs a new CPPTypedefType object that defines a +// typedef to the indicated type according to the type +// and the InstanceIdentifier. The InstanceIdentifier +// pointer is deallocated. +//////////////////////////////////////////////////////////////////// +CPPTypedefType:: +CPPTypedefType(CPPType *type, CPPInstanceIdentifier *ii, + CPPScope *current_scope, const CPPFile &file) : + CPPType(file) +{ + _type = ii->unroll_type(type); + _ident = ii->_ident; + ii->_ident = NULL; + delete ii; + + if (_ident != NULL) { + _ident->_native_scope = current_scope; + } + + _subst_decl_recursive_protect = false; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTypedefType::is_scoped +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool CPPTypedefType:: +is_scoped() const { + if (_ident == NULL) { + return false; + } else { + return _ident->is_scoped(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTypedefType::get_scope +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPScope *CPPTypedefType:: +get_scope(CPPScope *current_scope, CPPScope *global_scope, + CPPPreprocessor *error_sink) const { + if (_ident == NULL) { + return current_scope; + } else { + return _ident->get_scope(current_scope, global_scope, error_sink); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTypedefType::get_simple_name +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +string CPPTypedefType:: +get_simple_name() const { + if (_ident == NULL) { + return ""; + } + return _ident->get_simple_name(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTypedefType::get_local_name +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +string CPPTypedefType:: +get_local_name(CPPScope *scope) const { + if (_ident == NULL) { + return ""; + } + return _ident->get_local_name(scope); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTypedefType::get_fully_scoped_name +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +string CPPTypedefType:: +get_fully_scoped_name() const { + if (_ident == NULL) { + return ""; + } + return _ident->get_fully_scoped_name(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTypedefType::is_incomplete +// Access: Public, Virtual +// Description: Returns true if the type has not yet been fully +// specified, false if it has. +//////////////////////////////////////////////////////////////////// +bool CPPTypedefType:: +is_incomplete() const { + return false; + //return _type->is_incomplete(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTypedefType::is_tbd +// Access: Public, Virtual +// Description: Returns true if the type, or any nested type within +// the type, is a CPPTBDType and thus isn't fully +// determined right now. In this case, calling +// resolve_type() may or may not resolve the type. +//////////////////////////////////////////////////////////////////// +bool CPPTypedefType:: +is_tbd() const { + if (_ident != NULL && _ident->is_tbd()) { + return true; + } + return _type->is_tbd(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTypedefType::is_fully_specified +// Access: Public, Virtual +// Description: Returns true if this declaration is an actual, +// factual declaration, or false if some part of the +// declaration depends on a template parameter which has +// not yet been instantiated. +//////////////////////////////////////////////////////////////////// +bool CPPTypedefType:: +is_fully_specified() const { + if (_ident != NULL && !_ident->is_fully_specified()) { + return false; + } + return CPPDeclaration::is_fully_specified() && + _type->is_fully_specified(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTypedefType::substitute_decl +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPDeclaration *CPPTypedefType:: +substitute_decl(CPPDeclaration::SubstDecl &subst, + CPPScope *current_scope, CPPScope *global_scope) { + + return _type->substitute_decl(subst, current_scope, global_scope); + + // Bah, this doesn't seem to work, and I can't figure out why. + // Well, for now, let's just substitute it with the type we're + // pointing to. This is not a huge deal for now, until we find + // that we need to preserve these typedefs. + + /* + CPPDeclaration *top = + CPPType::substitute_decl(subst, current_scope, global_scope); + if (top != this) { + return top; + } + + if (_subst_decl_recursive_protect) { + // We're already executing this block; we'll have to return a + // proxy to the type which we'll define later. + CPPTypeProxy *proxy = new CPPTypeProxy; + _proxies.push_back(proxy); + assert(proxy != NULL); + return proxy; + } + _subst_decl_recursive_protect = true; + + CPPTypedefType *rep = new CPPTypedefType(*this); + CPPDeclaration *new_type = + _type->substitute_decl(subst, current_scope, global_scope); + rep->_type = new_type->as_type(); + + if (rep->_type == NULL) { + rep->_type = _type; + } + + if (_ident != NULL) { + rep->_ident = + _ident->substitute_decl(subst, current_scope, global_scope); + } + + if (rep->_type == _type && rep->_ident == _ident) { + delete rep; + rep = this; + } + + rep = CPPType::new_type(rep)->as_typedef_type(); + subst.insert(SubstDecl::value_type(this, rep)); + + _subst_decl_recursive_protect = false; + // Now fill in all the proxies we created for our recursive + // references. + Proxies::iterator pi; + for (pi = _proxies.begin(); pi != _proxies.end(); ++pi) { + (*pi)->_actual_type = rep; + } + + return rep; */ +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPFunctionType::resolve_type +// Access: Public, Virtual +// Description: If this CPPType object is a forward reference or +// other nonspecified reference to a type that might now +// be known a real type, returns the real type. +// Otherwise returns the type itself. +//////////////////////////////////////////////////////////////////// +CPPType *CPPTypedefType:: +resolve_type(CPPScope *current_scope, CPPScope *global_scope) { + CPPType *ptype = _type->resolve_type(current_scope, global_scope); + + if (ptype != _type) { + CPPTypedefType *rep = new CPPTypedefType(*this); + rep->_type = ptype; + return CPPType::new_type(rep); + } + + return this; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTypedefType::is_equivalent_type +// Access: Public, Virtual +// Description: This is a little more forgiving than is_equal(): it +// returns true if the types appear to be referring to +// the same thing, even if they may have different +// pointers or somewhat different definitions. It's +// useful for parameter matching, etc. +//////////////////////////////////////////////////////////////////// +bool CPPTypedefType:: +is_equivalent(const CPPType &other) const { + CPPType *ot = (CPPType *)&other; + + // Unwrap all the typedefs to get to where it is pointing. + while (ot->get_subtype() == ST_typedef) { + ot = ot->as_typedef_type()->_type; + } + + // Compare the unwrapped type to what we are pointing to. + // If we are pointing to a typedef ourselves, then this will + // automatically recurse. + return _type->is_equivalent(*ot); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTypedefType::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void CPPTypedefType:: +output(ostream &out, int indent_level, CPPScope *scope, bool complete) const { + string name; + if (_ident != NULL) { + name = _ident->get_local_name(scope); + } + + if (complete) { + out << "typedef "; + _type->output_instance(out, indent_level, scope, false, "", name); + } else { + out << name; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTypedefType::get_subtype +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPDeclaration::SubType CPPTypedefType:: +get_subtype() const { + return ST_typedef; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTypedefType::as_typedef_type +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPTypedefType *CPPTypedefType:: +as_typedef_type() { + return this; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTypedefType::is_equal +// Access: Protected, Virtual +// Description: Called by CPPDeclaration() to determine whether this type is +// equivalent to another type of the same type. +//////////////////////////////////////////////////////////////////// +bool CPPTypedefType:: +is_equal(const CPPDeclaration *other) const { + const CPPTypedefType *ot = ((CPPDeclaration *)other)->as_typedef_type(); + assert(ot != NULL); + + return (_type == ot->_type) && (*_ident == *ot->_ident); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTypedefType::is_less +// Access: Protected, Virtual +// Description: Called by CPPDeclaration() to determine whether this type +// should be ordered before another type of the same +// type, in an arbitrary but fixed ordering. +//////////////////////////////////////////////////////////////////// +bool CPPTypedefType:: +is_less(const CPPDeclaration *other) const { + return CPPDeclaration::is_less(other); + + // The below code causes a crash for unknown reasons. + /* + const CPPTypedefType *ot = ((CPPDeclaration *)other)->as_typedef_type(); + assert(ot != NULL); + + if (_type != ot->_type) { + return _type < ot->_type; + } + + if (*_ident != *ot->_ident) { + return *_ident < *ot->_ident; + } + + return false; */ +} diff --git a/dtool/src/cppparser/cppTypedefType.h b/dtool/src/cppparser/cppTypedefType.h new file mode 100644 index 0000000000..eb6ab016cf --- /dev/null +++ b/dtool/src/cppparser/cppTypedefType.h @@ -0,0 +1,74 @@ +// Filename: cppTypedefType.h +// Created by: rdb (01Aug14) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#ifndef CPPTYPEDEFTYPE_H +#define CPPTYPEDEFTYPE_H + +#include "dtoolbase.h" +#include "cppType.h" + +class CPPIdentifier; + +/////////////////////////////////////////////////////////////////// +// Class : CPPTypedefType +// Description : +//////////////////////////////////////////////////////////////////// +class CPPTypedefType : public CPPType { +public: + CPPTypedefType(CPPType *type, const string &name, CPPScope *current_scope); + CPPTypedefType(CPPType *type, CPPIdentifier *ident, CPPScope *current_scope); + CPPTypedefType(CPPType *type, CPPInstanceIdentifier *ii, + CPPScope *current_scope, const CPPFile &file); + + bool is_scoped() const; + CPPScope *get_scope(CPPScope *current_scope, CPPScope *global_scope, + CPPPreprocessor *error_sink = NULL) const; + + virtual string get_simple_name() const; + virtual string get_local_name(CPPScope *scope = NULL) const; + virtual string get_fully_scoped_name() const; + + virtual bool is_incomplete() const; + virtual bool is_tbd() const; + + virtual bool is_fully_specified() const; + + virtual CPPDeclaration *substitute_decl(SubstDecl &subst, + CPPScope *current_scope, + CPPScope *global_scope); + + virtual CPPType *resolve_type(CPPScope *current_scope, + CPPScope *global_scope); + + virtual bool is_equivalent(const CPPType &other) const; + + virtual void output(ostream &out, int indent_level, CPPScope *scope, + bool complete) const; + virtual SubType get_subtype() const; + + virtual CPPTypedefType *as_typedef_type(); + + CPPType *_type; + CPPIdentifier *_ident; + +protected: + virtual bool is_equal(const CPPDeclaration *other) const; + virtual bool is_less(const CPPDeclaration *other) const; + + bool _subst_decl_recursive_protect; + typedef vector Proxies; + Proxies _proxies; +}; + +#endif diff --git a/dtool/src/cppparser/p3cppParser_composite1.cxx b/dtool/src/cppparser/p3cppParser_composite1.cxx index b8a7452e98..8b60e60a17 100644 --- a/dtool/src/cppparser/p3cppParser_composite1.cxx +++ b/dtool/src/cppparser/p3cppParser_composite1.cxx @@ -18,13 +18,8 @@ #include "cppTypeDeclaration.cxx" #include "cppTypeParser.cxx" #include "cppTypeProxy.cxx" +#include "cppTypedefType.cxx" #include "cppSimpleType.cxx" #include "cppTBDType.cxx" -#include "cppTypedef.cxx" #include "cppUsing.cxx" #include "cppVisibility.cxx" - - - - - diff --git a/dtool/src/dtoolbase/atomicAdjust.h b/dtool/src/dtoolbase/atomicAdjust.h index 9dc7832e46..bd709fce20 100644 --- a/dtool/src/dtoolbase/atomicAdjust.h +++ b/dtool/src/dtoolbase/atomicAdjust.h @@ -18,7 +18,15 @@ #include "dtoolbase.h" #include "selectThreadImpl.h" -#if defined(THREAD_DUMMY_IMPL)||defined(THREAD_SIMPLE_IMPL) +#if defined(CPPPARSER) + +struct AtomicAdjust { + typedef long Integer; + typedef void *UnalignedPointer; + typedef UnalignedPointer Pointer; +}; + +#elif defined(THREAD_DUMMY_IMPL) || defined(THREAD_SIMPLE_IMPL) #include "atomicAdjustDummyImpl.h" typedef AtomicAdjustDummyImpl AtomicAdjust; diff --git a/dtool/src/dtoolbase/typeHandle.cxx b/dtool/src/dtoolbase/typeHandle.cxx index 773cca79ec..b1d50fffaa 100644 --- a/dtool/src/dtoolbase/typeHandle.cxx +++ b/dtool/src/dtoolbase/typeHandle.cxx @@ -16,33 +16,9 @@ #include "typeRegistryNode.h" #include "atomicAdjust.h" -#ifdef HAVE_PYTHON -#include "Python.h" -#endif - // This is initialized to zero by static initialization. TypeHandle TypeHandle::_none; -#ifdef HAVE_PYTHON -//////////////////////////////////////////////////////////////////// -// Function: TypeHandle::make -// Access: Published -// Description: This special method allows coercion to a TypeHandle -// from a Python class object or instance. It simply -// attempts to call classobj.get_class_type(), and -// returns that value (or raises an exception if that -// method doesn't work). -// -// This method allows a Python class object to be used -// anywhere a TypeHandle is expected by the C++ -// interface. -//////////////////////////////////////////////////////////////////// -PyObject *TypeHandle:: -make(PyObject *classobj) { - return PyObject_CallMethod(classobj, (char *)"get_class_type", (char *)""); -} -#endif // HAVE_PYTHON - #ifdef DO_MEMORY_USAGE //////////////////////////////////////////////////////////////////// // Function: TypeHandle::get_memory_usage @@ -80,7 +56,10 @@ inc_memory_usage(MemoryClass memory_class, int size) { assert(rnode != (TypeRegistryNode *)NULL); AtomicAdjust::add(rnode->_memory_usage[memory_class], (AtomicAdjust::Integer)size); //cerr << *this << ".inc(" << memory_class << ", " << size << ") -> " << rnode->_memory_usage[memory_class] << "\n"; - assert(rnode->_memory_usage[memory_class] >= 0); + if (rnode->_memory_usage[memory_class] < 0) { + cerr << "Memory usage overflow for type " << *this << ".\n"; + abort(); + } } } #endif // DO_MEMORY_USAGE diff --git a/dtool/src/dtoolbase/typeHandle.h b/dtool/src/dtoolbase/typeHandle.h index 464fab66d6..b060689c01 100644 --- a/dtool/src/dtoolbase/typeHandle.h +++ b/dtool/src/dtoolbase/typeHandle.h @@ -64,13 +64,6 @@ class TypedObject; -#ifdef HAVE_PYTHON -#ifndef PyObject_HEAD -struct _object; -typedef _object PyObject; -#endif -#endif - //////////////////////////////////////////////////////////////////// // Class : TypeHandle // Description : TypeHandle is the identifier used to differentiate @@ -104,9 +97,7 @@ PUBLISHED: INLINE TypeHandle(); INLINE TypeHandle(const TypeHandle ©); -#ifdef HAVE_PYTHON - static PyObject *make(PyObject *classobj); -#endif // HAVE_PYTHON + EXTENSION(static TypeHandle make(PyTypeObject *classobj)); INLINE bool operator == (const TypeHandle &other) const; INLINE bool operator != (const TypeHandle &other) const; diff --git a/dtool/src/dtoolutil/Sources.pp b/dtool/src/dtoolutil/Sources.pp index 7c2638caa6..a75ae352ae 100644 --- a/dtool/src/dtoolutil/Sources.pp +++ b/dtool/src/dtoolutil/Sources.pp @@ -17,6 +17,8 @@ filename.h \ $[if $[IS_OSX],filename_assist.mm filename_assist.h,] \ globPattern.I globPattern.h \ + lineStream.I lineStream.h \ + lineStreamBuf.I lineStreamBuf.h \ load_dso.h \ pandaFileStream.h pandaFileStream.I \ pandaFileStreamBuf.h \ @@ -38,6 +40,7 @@ dSearchPath.cxx \ executionEnvironment.cxx filename.cxx \ globPattern.cxx \ + lineStream.cxx lineStreamBuf.cxx \ load_dso.cxx \ pandaFileStream.cxx pandaFileStreamBuf.cxx \ pandaSystem.cxx \ @@ -58,6 +61,8 @@ executionEnvironment.I executionEnvironment.h filename.I \ filename.h \ globPattern.I globPattern.h \ + lineStream.I lineStream.h \ + lineStreamBuf.I lineStreamBuf.h \ load_dso.h \ pandaFileStream.h pandaFileStream.I \ pandaFileStreamBuf.h \ diff --git a/dtool/src/dtoolutil/filename.I b/dtool/src/dtoolutil/filename.I index 22c49f39bf..3d91bb987a 100644 --- a/dtool/src/dtoolutil/filename.I +++ b/dtool/src/dtoolutil/filename.I @@ -375,6 +375,17 @@ operator + (const string &other) const { return a; } +//////////////////////////////////////////////////////////////////// +// Function: Filename::operator / +// Access: Published +// Description: Returns a new Filename that is composed of the +// other filename added to the end of this filename, +// with an intervening slash added if necessary. +//////////////////////////////////////////////////////////////////// +INLINE Filename Filename:: +operator / (const Filename &other) const { + return Filename(*this, other); +} //////////////////////////////////////////////////////////////////// // Function: Filename::get_fullpath diff --git a/dtool/src/dtoolutil/filename.cxx b/dtool/src/dtoolutil/filename.cxx index d15dc738c3..a4152d5db1 100644 --- a/dtool/src/dtoolutil/filename.cxx +++ b/dtool/src/dtoolutil/filename.cxx @@ -308,30 +308,6 @@ Filename(const Filename &dirname, const Filename &basename) { } } -#ifdef HAVE_PYTHON -//////////////////////////////////////////////////////////////////// -// Function: Filename::__reduce__ -// Access: Published -// Description: This special Python method is implement to provide -// support for the pickle module. -//////////////////////////////////////////////////////////////////// -PyObject *Filename:: -__reduce__(PyObject *self) const { - // We should return at least a 2-tuple, (Class, (args)): the - // necessary class object whose constructor we should call - // (e.g. this), and the arguments necessary to reconstruct this - // object. - PyObject *this_class = PyObject_Type(self); - if (this_class == NULL) { - return NULL; - } - - PyObject *result = Py_BuildValue("(O(s))", this_class, c_str()); - Py_DECREF(this_class); - return result; -} -#endif // HAVE_PYTHON - //////////////////////////////////////////////////////////////////// // Function: Filename::from_os_specific // Access: Published, Static @@ -2005,49 +1981,13 @@ scan_directory(vector_string &contents) const { globfree(&globbuf); return true; - + #else // Don't know how to scan directories! return false; #endif } -#ifdef HAVE_PYTHON -//////////////////////////////////////////////////////////////////// -// Function: Filename::scan_directory -// Access: Published -// Description: This variant on scan_directory returns a Python list -// of strings on success, or None on failure. -//////////////////////////////////////////////////////////////////// -PyObject *Filename:: -scan_directory() const { - vector_string contents; - if (!scan_directory(contents)) { - PyObject *result = Py_None; - Py_INCREF(result); - return result; - } - - PyObject *result = PyList_New(contents.size()); - for (size_t i = 0; i < contents.size(); ++i) { - const string &filename = contents[i]; -#if PY_MAJOR_VERSION >= 3 - // This function expects UTF-8. - PyObject *str = PyUnicode_FromStringAndSize(filename.data(), filename.size()); -#else - PyObject *str = PyString_FromStringAndSize(filename.data(), filename.size()); -#endif -#ifdef Py_LIMITED_API - PyList_SetItem(result, i, str); -#else - PyList_SET_ITEM(result, i, str); -#endif - } - - return result; -} -#endif // HAVE_PYTHON - //////////////////////////////////////////////////////////////////// // Function: Filename::open_read // Access: Published diff --git a/dtool/src/dtoolutil/filename.h b/dtool/src/dtoolutil/filename.h index 45d975eac6..a391407a4e 100644 --- a/dtool/src/dtoolutil/filename.h +++ b/dtool/src/dtoolutil/filename.h @@ -74,7 +74,7 @@ PUBLISHED: #endif #ifdef HAVE_PYTHON - PyObject *__reduce__(PyObject *self) const; + EXTENSION(PyObject *__reduce__(PyObject *self) const); #endif // Static constructors to explicitly create a filename that refers @@ -126,6 +126,8 @@ PUBLISHED: INLINE void operator += (const string &other); INLINE Filename operator + (const string &other) const; + INLINE Filename operator / (const Filename &other) const; + // Or, you can use any of these. INLINE string get_fullpath() const; INLINE wstring get_fullpath_w() const; @@ -200,11 +202,11 @@ PUBLISHED: const string &default_extension = string()); bool make_relative_to(Filename directory, bool allow_backups = true); int find_on_searchpath(const DSearchPath &searchpath); - + bool scan_directory(vector_string &contents) const; #ifdef HAVE_PYTHON - PyObject *scan_directory() const; -#endif + EXTENSION(PyObject *scan_directory() const); +#endif bool open_read(ifstream &stream) const; bool open_write(ofstream &stream, bool truncate = true) const; diff --git a/dtool/src/dtoolutil/globPattern.cxx b/dtool/src/dtoolutil/globPattern.cxx index a8cbf7552f..7f7d23a53b 100644 --- a/dtool/src/dtoolutil/globPattern.cxx +++ b/dtool/src/dtoolutil/globPattern.cxx @@ -113,44 +113,12 @@ match_files(vector_string &results, const Filename &cwd) const { pattern = source.substr(0, slash); suffix = source.substr(slash + 1); } - + GlobPattern glob(pattern); glob.set_case_sensitive(_case_sensitive); return glob.r_match_files(prefix, suffix, results, cwd); } -#ifdef HAVE_PYTHON -//////////////////////////////////////////////////////////////////// -// Function: GlobPattern::match_files -// Access: Published -// Description: This variant on match_files returns a Python list -// of strings. -//////////////////////////////////////////////////////////////////// -PyObject *GlobPattern:: -match_files(const Filename &cwd) const { - vector_string contents; - match_files(contents, cwd); - - PyObject *result = PyList_New(contents.size()); - for (size_t i = 0; i < contents.size(); ++i) { - const string &filename = contents[i]; -#if PY_MAJOR_VERSION >= 3 - // This function expects UTF-8. - PyObject *str = PyUnicode_FromStringAndSize(filename.data(), filename.size()); -#else - PyObject *str = PyString_FromStringAndSize(filename.data(), filename.size()); -#endif -#ifdef Py_LIMITED_API - PyList_SetItem(result, i, str); -#else - PyList_SET_ITEM(result, i, str); -#endif - } - - return result; -} -#endif // HAVE_PYTHON - //////////////////////////////////////////////////////////////////// // Function: GlobPattern::r_match_files // Access: Private @@ -195,21 +163,21 @@ r_match_files(const Filename &prefix, const string &suffix, return next_glob.r_match_files(Filename(prefix, _pattern), next_suffix, results, cwd); - } + } // If there *are* special glob characters, we must attempt to // match the pattern against the files in this directory. - + vector_string dir_files; if (!parent_dir.scan_directory(dir_files)) { // Not a directory, or unable to read directory; stop here. return 0; } - + // Now go through each file in the directory looking for one that // matches the pattern. int num_matched = 0; - + vector_string::const_iterator fi; for (fi = dir_files.begin(); fi != dir_files.end(); ++fi) { const string &local_file = (*fi); @@ -218,7 +186,7 @@ r_match_files(const Filename &prefix, const string &suffix, // We have a match; continue. if (suffix.empty()) { results.push_back(Filename(prefix, local_file)); - num_matched++; + num_matched++; } else { num_matched += next_glob.r_match_files(Filename(prefix, local_file), next_suffix, results, cwd); @@ -226,7 +194,7 @@ r_match_files(const Filename &prefix, const string &suffix, } } } - + return num_matched; } diff --git a/dtool/src/dtoolutil/globPattern.h b/dtool/src/dtoolutil/globPattern.h index f79a3f0cb1..f7983021b7 100644 --- a/dtool/src/dtoolutil/globPattern.h +++ b/dtool/src/dtoolutil/globPattern.h @@ -61,8 +61,8 @@ PUBLISHED: string get_const_prefix() const; int match_files(vector_string &results, const Filename &cwd = Filename()) const; #ifdef HAVE_PYTHON - PyObject *match_files(const Filename &cwd = Filename()) const; -#endif + EXTENSION(PyObject *match_files(const Filename &cwd = Filename()) const); +#endif private: bool matches_substr(string::const_iterator pi, diff --git a/panda/src/putil/lineStream.I b/dtool/src/dtoolutil/lineStream.I similarity index 100% rename from panda/src/putil/lineStream.I rename to dtool/src/dtoolutil/lineStream.I diff --git a/panda/src/putil/lineStream.cxx b/dtool/src/dtoolutil/lineStream.cxx similarity index 100% rename from panda/src/putil/lineStream.cxx rename to dtool/src/dtoolutil/lineStream.cxx diff --git a/panda/src/putil/lineStream.h b/dtool/src/dtoolutil/lineStream.h similarity index 95% rename from panda/src/putil/lineStream.h rename to dtool/src/dtoolutil/lineStream.h index 4b81ab1569..63358a18ac 100644 --- a/panda/src/putil/lineStream.h +++ b/dtool/src/dtoolutil/lineStream.h @@ -15,7 +15,7 @@ #ifndef LINESTREAM_H #define LINESTREAM_H -#include "pandabase.h" +#include "dtoolbase.h" #include "lineStreamBuf.h" @@ -33,7 +33,7 @@ // More text can still be written to it and continuously // extracted. //////////////////////////////////////////////////////////////////// -class EXPCL_PANDA_PUTIL LineStream : public ostream { +class EXPCL_DTOOL LineStream : public ostream { PUBLISHED: INLINE LineStream(); diff --git a/panda/src/putil/lineStreamBuf.I b/dtool/src/dtoolutil/lineStreamBuf.I similarity index 100% rename from panda/src/putil/lineStreamBuf.I rename to dtool/src/dtoolutil/lineStreamBuf.I diff --git a/panda/src/putil/lineStreamBuf.cxx b/dtool/src/dtoolutil/lineStreamBuf.cxx similarity index 100% rename from panda/src/putil/lineStreamBuf.cxx rename to dtool/src/dtoolutil/lineStreamBuf.cxx diff --git a/panda/src/putil/lineStreamBuf.h b/dtool/src/dtoolutil/lineStreamBuf.h similarity index 94% rename from panda/src/putil/lineStreamBuf.h rename to dtool/src/dtoolutil/lineStreamBuf.h index d7c7847292..78106ce525 100644 --- a/panda/src/putil/lineStreamBuf.h +++ b/dtool/src/dtoolutil/lineStreamBuf.h @@ -15,7 +15,7 @@ #ifndef LINESTREAMBUF_H #define LINESTREAMBUF_H -#include "pandabase.h" +#include "dtoolbase.h" #include @@ -26,7 +26,7 @@ // continuously extracted as a sequence of lines of // text. //////////////////////////////////////////////////////////////////// -class EXPCL_PANDA_PUTIL LineStreamBuf : public streambuf { +class EXPCL_DTOOL LineStreamBuf : public streambuf { public: LineStreamBuf(); virtual ~LineStreamBuf(); diff --git a/dtool/src/dtoolutil/p3dtoolutil_composite1.cxx b/dtool/src/dtoolutil/p3dtoolutil_composite1.cxx index 0cd4ec6cd8..cd2db6edc0 100644 --- a/dtool/src/dtoolutil/p3dtoolutil_composite1.cxx +++ b/dtool/src/dtoolutil/p3dtoolutil_composite1.cxx @@ -4,6 +4,8 @@ #include "executionEnvironment.cxx" #include "filename.cxx" #include "globPattern.cxx" +#include "lineStream.cxx" +#include "lineStreamBuf.cxx" #include "load_dso.cxx" #include "pandaSystem.cxx" diff --git a/dtool/src/interrogate/functionRemap.cxx b/dtool/src/interrogate/functionRemap.cxx index f30edfd2e5..ea09c472e7 100644 --- a/dtool/src/interrogate/functionRemap.cxx +++ b/dtool/src/interrogate/functionRemap.cxx @@ -17,6 +17,7 @@ #include "interrogate.h" #include "parameterRemap.h" #include "parameterRemapThis.h" +#include "parameterRemapUnchanged.h" #include "interfaceMaker.h" #include "interrogateBuilder.h" @@ -200,7 +201,7 @@ call_function(ostream &out, int indent_level, bool convert_result, // Now a simple special-case test. Often, we will have converted // the reference-returning assignment operator to a pointer. In - // this case, we might inadventent generate code like "return + // this case, we might inadvertently generate code like "return // &(*this)", when "return this" would do. We check for this here // and undo it as a special case. @@ -226,7 +227,8 @@ call_function(ostream &out, int indent_level, bool convert_result, return_expr = get_call_str(container, pexprs); } else { - if (_return_type->return_value_should_be_simple()) { + //if (_return_type->return_value_should_be_simple()) { + if (false) { // We have to assign the result to a temporary first; this makes // it a bit easier on poor old VC++. InterfaceMaker::indent(out, indent_level); @@ -234,8 +236,14 @@ call_function(ostream &out, int indent_level, bool convert_result, &parser); out << " = " << call << ";\n"; + // MOVE() expands to std::move() when we are compiling with a + // compiler that supports rvalue references. It basically turns + // an lvalue into an rvalue, allowing a move constructor to be + // called instead of a copy constructor (since we won't be using + // the return value any more), which is usually more efficient if + // it exists. If it doesn't, it shouldn't do any harm. string new_str = - _return_type->prepare_return_expr(out, indent_level, "result"); + _return_type->prepare_return_expr(out, indent_level, "MOVE(result)"); return_expr = _return_type->get_return_expr(new_str); } else { @@ -258,11 +266,11 @@ call_function(ostream &out, int indent_level, bool convert_result, // comment. //////////////////////////////////////////////////////////////////// void FunctionRemap:: -write_orig_prototype(ostream &out, int indent_level, bool local) const { +write_orig_prototype(ostream &out, int indent_level, bool local, int num_default_args) const { if (local) { - _cppfunc->output(out, indent_level, NULL, false, _num_default_parameters); + _cppfunc->output(out, indent_level, NULL, false, num_default_args); } else { - _cppfunc->output(out, indent_level, &parser, false, _num_default_parameters); + _cppfunc->output(out, indent_level, &parser, false, num_default_args); } } @@ -287,7 +295,6 @@ make_wrapper_entry(FunctionIndex function_index) { iwrapper._comment = InterrogateBuilder::trim_blanks(_cppfunc->_leading_comment->_comment); } - if (output_function_names) { // If we're keeping the function names, record that the wrapper is // callable. @@ -356,7 +363,7 @@ make_wrapper_entry(FunctionIndex function_index) { //////////////////////////////////////////////////////////////////// // Function: FunctionRemap::get_call_str -// Access: Private +// Access: Public // Description: Returns a string suitable for calling the wrapped // function. If pexprs is nonempty, it represents // the list of expressions that will evaluate to each @@ -412,7 +419,12 @@ get_call_str(const string &container, const vector_string &pexprs) const { } else if (_has_this && !container.empty()) { // If we have a "this" parameter, the calling convention is also // a bit different. - call << "(" << container << ")->" << _cppfunc->get_local_name(); + if (container == "local_this") { + // This isn't important, it just looks a bit prettier. + call << container << "->" << _cppfunc->get_local_name(); + } else { + call << "(" << container << ")->" << _cppfunc->get_local_name(); + } } else { call << _cppfunc->get_local_name(&parser); @@ -426,14 +438,27 @@ get_call_str(const string &container, const vector_string &pexprs) const { separator = ", "; } - for (int pn = _first_true_parameter; - pn < (int)_parameters.size(); - ++pn) { + int pn = _first_true_parameter; + int num_parameters = pexprs.size(); + + if (_type == T_item_assignment_operator) { + // The last parameter is the value to set. + --num_parameters; + } + + for (pn = _first_true_parameter; + pn < num_parameters; ++pn) { + nassertd(pn < _parameters.size()) break; call << separator; _parameters[pn]._remap->pass_parameter(call, get_parameter_expr(pn, pexprs)); separator = ", "; } call << ")"; + + if (_type == T_item_assignment_operator) { + call << " = "; + _parameters[pn]._remap->pass_parameter(call, get_parameter_expr(pn, pexprs)); + } } return call.str(); @@ -497,6 +522,7 @@ setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface_mak } string fname = _cppfunc->get_simple_name(); + CPPType *rtype = _ftype->_return_type->resolve_type(&parser, _cppscope); if (_cpptype != (CPPType *)NULL && ((_cppfunc->_storage_class & CPPInstance::SC_static) == 0) && @@ -533,13 +559,22 @@ setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface_mak fname == "operator <<=" || fname == "operator >>=") { _type = T_assignment_method; + + } else if (fname == "operator []" && !_const_method && rtype != NULL) { + // Check if this is an item-assignment operator. + CPPReferenceType *reftype = rtype->as_reference_type(); + if (reftype != NULL && reftype->_pointing_at->as_const_type() == NULL) { + // It returns a mutable reference. + _type = T_item_assignment_operator; + } } } const CPPParameterList::Parameters ¶ms = _ftype->_parameters->_parameters; for (int i = 0; i < (int)params.size() - _num_default_parameters; i++) { - CPPType *type = params[i]->_type->resolve_type(&parser, _cppscope); + //CPPType *type = params[i]->_type->resolve_type(&parser, _cppscope); + CPPType *type = params[i]->_type; Parameter param; param._has_name = true; param._name = params[i]->get_simple_name(); @@ -557,10 +592,16 @@ setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface_mak if (param._remap == (ParameterRemap *)NULL) { // If we can't handle one of the parameter types, we can't call // the function. - //nout << "Can't handle parameter " << i << " of method " << *_cppfunc << "\n"; - return false; + if (fname == "__traverse__") { + // Hack to record this even though we can't wrap visitproc. + param._remap = new ParameterRemapUnchanged(type); + } else { + //nout << "Can't handle parameter " << i << " of method " << *_cppfunc << "\n"; + return false; + } + } else { + param._remap->set_default_value(params[i]->_initializer); } - param._remap->set_default_value(params[i]->_initializer); if (!param._remap->is_valid()) { nout << "Invalid remap for parameter " << i << " of method " << *_cppfunc << "\n"; @@ -601,9 +642,39 @@ setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface_mak } } + } else if (_type == T_item_assignment_operator) { + // An item-assignment method isn't really a thing in C++, but it is + // in scripting languages, so we use this to denote item-access operators + // that return a non-const reference. + + if (_cpptype == (CPPType *)NULL) { + nout << "Method " << *_cppfunc << " has no struct type\n"; + return false; + } else { + // Synthesize a const reference parameter for the assignment. + CPPType *bare_type = TypeManager::unwrap_reference(rtype); + CPPType *const_type = CPPType::new_type(new CPPConstType(bare_type)); + CPPType *ref_type = CPPType::new_type(new CPPReferenceType(const_type)); + + Parameter param; + param._has_name = true; + param._name = "assign_val"; + param._remap = interface_maker->remap_parameter(_cpptype, ref_type); + + if (param._remap == NULL || !param._remap->is_valid()) { + nout << "Invalid remap for assignment type of method " << *_cppfunc << "\n"; + return false; + } + _parameters.push_back(param); + + // Pretend we don't return anything at all. + CPPType *void_type = TypeManager::get_void_type(); + _return_type = interface_maker->remap_parameter(_cpptype, void_type); + _void_return = true; + } + } else { // The normal case. - CPPType *rtype = _ftype->_return_type->resolve_type(&parser, _cppscope); _return_type = interface_maker->remap_parameter(_cpptype, rtype); if (_return_type != (ParameterRemap *)NULL) { _void_return = TypeManager::is_void(rtype); @@ -663,9 +734,10 @@ setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface_mak } } - if (_parameters.size() == first_param) { + if ((int)_parameters.size() == first_param) { _args_type = InterfaceMaker::AT_no_args; - } else if (_parameters.size() == first_param + 1) { + } else if ((int)_parameters.size() == first_param + 1 && + _parameters[first_param]._remap->get_default_value() == NULL) { _args_type = InterfaceMaker::AT_single_arg; } else { _args_type = InterfaceMaker::AT_varargs; @@ -682,8 +754,8 @@ setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface_mak } } else if (fname == "__setitem__") { - _flags |= F_setitem; if (_has_this && _parameters.size() > 2) { + _flags |= F_setitem; if (TypeManager::is_integer(_parameters[1]._remap->get_new_type())) { // Its first parameter is an int parameter, presumably an index. _flags |= F_setitem_int; @@ -691,6 +763,16 @@ setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface_mak } } + } else if (fname == "__delitem__") { + if (_has_this && _parameters.size() == 2) { + _flags |= F_delitem; + if (TypeManager::is_integer(_parameters[1]._remap->get_new_type())) { + // Its first parameter is an int parameter, presumably an index. + _flags |= F_delitem_int; + _args_type = InterfaceMaker::AT_single_arg; + } + } + } else if (fname == "size" || fname == "__len__") { if ((int)_parameters.size() == first_param && TypeManager::is_integer(_return_type->get_new_type())) { @@ -735,11 +817,20 @@ setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface_mak _flags |= F_compare_to; } + } else if (fname == "make") { + if (!_has_this && _parameters.size() >= 1 && + TypeManager::is_pointer(_return_type->get_new_type())) { + // We can use this for coercion. + _flags |= F_coerce_constructor; + } + } else if (fname == "operator ()" || fname == "__call__") { // Call operators always take keyword arguments. _args_type = InterfaceMaker::AT_keyword_args; - } else if (fname == "__setattr__" || fname == "__getattr__") { + } else if (fname == "__setattr__" + || fname == "__getattr__" + || fname == "__delattr__") { // Just to prevent these from getting keyword arguments. } else { @@ -750,6 +841,19 @@ setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface_mak } } + } else if (_type == T_item_assignment_operator) { + // The concept of "item assignment operator" doesn't really exist in C++, + // but it does in scripting languages, and this allows us to wrap cases + // where the C++ getitem returns an assignable reference. + _flags |= F_setitem; + if (_has_this && _parameters.size() > 2) { + if (TypeManager::is_integer(_parameters[1]._remap->get_new_type())) { + // Its first parameter is an int parameter, presumably an index. + _flags |= F_setitem_int; + } + } + _args_type = InterfaceMaker::AT_varargs; + } else if (_type == T_constructor) { if (!_has_this && _parameters.size() == 1 && TypeManager::unwrap(_parameters[0]._remap->get_orig_type()) == @@ -758,7 +862,12 @@ setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface_mak // "this" type, this is a copy constructor. _flags |= F_copy_constructor; + } else if (!_has_this && _parameters.size() > 0 && + (_cppfunc->_storage_class & CPPInstance::SC_explicit) == 0) { + // A non-explicit non-copy constructor might be eligible for coercion. + _flags |= F_coerce_constructor; } + // Constructors always take varargs and keyword args. _args_type = InterfaceMaker::AT_keyword_args; } diff --git a/dtool/src/interrogate/functionRemap.h b/dtool/src/interrogate/functionRemap.h index ed0ef49507..c9d4f3bb94 100644 --- a/dtool/src/interrogate/functionRemap.h +++ b/dtool/src/interrogate/functionRemap.h @@ -52,14 +52,17 @@ public: ~FunctionRemap(); string get_parameter_name(int n) const; - string call_function(ostream &out, int indent_level, + string call_function(ostream &out, int indent_level, bool convert_result, const string &container, const vector_string &pexprs = vector_string()) const; - void write_orig_prototype(ostream &out, int indent_level, bool local=false) const; + void write_orig_prototype(ostream &out, int indent_level, bool local=false, + int num_default_args=0) const; FunctionWrapperIndex make_wrapper_entry(FunctionIndex function_index); + string get_call_str(const string &container, const vector_string &pexprs) const; + class Parameter { public: bool _has_name; @@ -76,21 +79,25 @@ public: T_typecast, T_getter, T_setter, + T_item_assignment_operator, }; enum Flags { - F_getitem = 0x0001, - F_getitem_int = 0x0002, - F_size = 0x0004, - F_setitem = 0x0008, - F_setitem_int = 0x0010, - F_make_copy = 0x0020, - F_copy_constructor = 0x0040, - F_explicit_self = 0x0080, - F_iter = 0x0100, - F_getbuffer = 0x0200, - F_releasebuffer = 0x0400, - F_compare_to = 0x0800, + F_getitem = 0x0001, + F_getitem_int = 0x0002, + F_size = 0x0004, + F_setitem = 0x0008, + F_setitem_int = 0x0010, + F_delitem = 0x0020, + F_delitem_int = 0x0040, + F_make_copy = 0x0080, + F_copy_constructor = 0x0100, + F_explicit_self = 0x0200, + F_iter = 0x0400, + F_getbuffer = 0x0800, + F_releasebuffer = 0x1000, + F_compare_to = 0x2000, + F_coerce_constructor = 0x4000, }; typedef vector Parameters; @@ -126,8 +133,8 @@ public: CPPFunctionType *_ftype; bool _is_valid; + private: - string get_call_str(const string &container, const vector_string &pexprs) const; string get_parameter_expr(int n, const vector_string &pexprs) const; bool setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface_maker); }; diff --git a/dtool/src/interrogate/interfaceMaker.cxx b/dtool/src/interrogate/interfaceMaker.cxx index a5b62ac27b..03398895b8 100644 --- a/dtool/src/interrogate/interfaceMaker.cxx +++ b/dtool/src/interrogate/interfaceMaker.cxx @@ -41,7 +41,7 @@ #include "pnotify.h" InterrogateType dummy_type; - + //////////////////////////////////////////////////////////////////// // Function: InterfaceMaker::Function::Constructor // Access: Public @@ -59,7 +59,7 @@ Function(const string &name, _flags = 0; _args_type = AT_unknown; } - + //////////////////////////////////////////////////////////////////// // Function: InterfaceMaker::Function::Destructor // Access: Public @@ -72,7 +72,7 @@ InterfaceMaker::Function:: delete (*ri); } } - + //////////////////////////////////////////////////////////////////// // Function: InterfaceMaker::MakeSeq::Constructor // Access: Public @@ -141,9 +141,15 @@ check_protocols() { for (fi = _methods.begin(); fi != _methods.end(); ++fi) { Function *func = (*fi); flags |= func->_flags; + + if (func->_ifunc.get_name() == "__traverse__") { + // If we have a method named __traverse__, we implement Python's + // cyclic garbage collection protocol. + _protocol_types |= PT_python_gc; + } } - if ((flags & (FunctionRemap::F_getitem_int | FunctionRemap::F_size)) == + if ((flags & (FunctionRemap::F_getitem_int | FunctionRemap::F_size)) == (FunctionRemap::F_getitem_int | FunctionRemap::F_size)) { // If we have both a getitem that receives an int, and a size, // then we implement the sequence protocol: you can iterate @@ -169,8 +175,6 @@ check_protocols() { _protocol_types |= PT_iter; } - InterrogateDatabase *idb = InterrogateDatabase::get_ptr(); - // Now are there any make_seq requests within this class? if (_itype._cpptype != NULL) { CPPStructType *stype = _itype._cpptype->as_struct_type(); @@ -269,18 +273,8 @@ generate_wrappers() { // traversal. int ti = 0; while (ti < idb->get_num_all_types()) { - TypeIndex type_index = idb->get_all_type(ti); + TypeIndex type_index = idb->get_all_type(ti++); record_object(type_index); - - if (interrogate_type_is_enum(ti)) { - int enum_count = interrogate_type_number_of_enum_values(ti); - for (int xx = 0; xx < enum_count; ++xx) { -// printf(" PyModule_AddIntConstant(module,\"%s\",%d)\n",interrogate_type_enum_value_name(ti,xx),interrogate_type_enum_value(ti,xx)); - } - } - - ++ti; -// printf(" New Type %d\n",ti); } int num_global_elements = idb->get_num_global_elements(); @@ -321,7 +315,7 @@ generate_wrappers() { FunctionIndex func_index = ielement.get_setter(); record_function(dummy_type, func_index); } - } + } } //////////////////////////////////////////////////////////////////// @@ -396,7 +390,7 @@ remap_parameter(CPPType *struct_type, CPPType *param_type) { // convert basic_string's to atomic strings. if (struct_type == (CPPType *)NULL || - !(TypeManager::is_basic_string_char(struct_type) || + !(TypeManager::is_basic_string_char(struct_type) || TypeManager::is_basic_string_wchar(struct_type))) { if (TypeManager::is_basic_string_char(param_type)) { return new ParameterRemapBasicStringToString(param_type); @@ -454,7 +448,10 @@ remap_parameter(CPPType *struct_type, CPPType *param_type) { } else if (TypeManager::is_const_ref_to_simple(param_type)) { return new ParameterRemapReferenceToConcrete(param_type); - } else if (TypeManager::is_pointer(param_type) || TypeManager::is_simple(param_type)) { + } else if (TypeManager::is_pointer(param_type) || + TypeManager::is_void(param_type) || + TypeManager::is_simple(param_type) || + TypeManager::is_simple_array(param_type)) { return new ParameterRemapUnchanged(param_type); } else { @@ -550,21 +547,19 @@ FunctionRemap *InterfaceMaker:: make_function_remap(const InterrogateType &itype, const InterrogateFunction &ifunc, CPPInstance *cppfunc, int num_default_parameters) { - FunctionRemap *remap = + FunctionRemap *remap = new FunctionRemap(itype, ifunc, cppfunc, num_default_parameters, this); - if (remap->_is_valid) - { - if (separate_overloading()) - { + if (remap->_is_valid) { + if (separate_overloading()) { hash_function_signature(remap); remap->_unique_name = get_unique_prefix() + _def->library_hash_name + remap->_hash; - remap->_wrapper_name = + remap->_wrapper_name = get_wrapper_prefix() + _def->library_hash_name + remap->_hash; remap->_reported_name = remap->_wrapper_name; - if (true_wrapper_names) - { - remap->_reported_name = + + if (true_wrapper_names) { + remap->_reported_name = InterrogateBuilder::clean_identifier(remap->_cppfunc->get_local_name(&parser)); } } @@ -589,7 +584,7 @@ make_function_remap(const InterrogateType &itype, // an overloaded function) need not define a name here. //////////////////////////////////////////////////////////////////// string InterfaceMaker:: -get_wrapper_name(const InterrogateType &itype, +get_wrapper_name(const InterrogateType &itype, const InterrogateFunction &ifunc, FunctionIndex func_index) { string func_name = ifunc.get_scoped_name(); @@ -677,12 +672,13 @@ record_function(const InterrogateType &itype, FunctionIndex func_index) { // parameters. This will happen only if separate_overloading(), // tested above, returned true; otherwise, max_default_parameters // will be 0 and the loop will only be traversed once. - for (int num_default_parameters = 0; + for (int num_default_parameters = 0; num_default_parameters <= max_default_parameters; num_default_parameters++) { - FunctionRemap *remap = + FunctionRemap *remap = make_function_remap(itype, ifunc, cppfunc, num_default_parameters); if (remap != (FunctionRemap *)NULL) { + func->_remaps.push_back(remap); // If *any* of the variants of this function has a "this" @@ -741,7 +737,8 @@ record_object(TypeIndex type_index) { } InterrogateDatabase *idb = InterrogateDatabase::get_ptr(); - const InterrogateType &itype = idb->get_type(type_index); + const InterrogateType &itype = idb->get_type(type_index); + assert(&itype != NULL); Object *object = new Object(itype); bool inserted = _objects.insert(Objects::value_type(type_index, object)).second; @@ -754,29 +751,26 @@ record_object(TypeIndex type_index) { function = record_function(itype, itype.get_constructor(ci)); object->_constructors.push_back(function); } - + int num_methods = itype.number_of_methods(); int mi; for (mi = 0; mi < num_methods; mi++) { function = record_function(itype, itype.get_method(mi)); object->_methods.push_back(function); } - + int num_casts = itype.number_of_casts(); for (mi = 0; mi < num_casts; mi++) { function = record_function(itype, itype.get_cast(mi)); object->_methods.push_back(function); } - + int num_derivations = itype.number_of_derivations(); - for (int di = 0; di < num_derivations; di++) - { - if (itype.derivation_has_upcast(di)) - { + for (int di = 0; di < num_derivations; di++) { + if (itype.derivation_has_upcast(di)) { record_function(itype, itype.derivation_get_upcast(di)); } - if (itype.derivation_has_downcast(di)) - { + if (itype.derivation_has_downcast(di)) { // Downcasts are methods of the base class, not the child class. TypeIndex base_type_index = itype.get_derivation(di); const InterrogateType &base_type = idb->get_type(base_type_index); @@ -785,27 +779,23 @@ record_object(TypeIndex type_index) { } int num_elements = itype.number_of_elements(); - for (int ei = 0; ei < num_elements; ei++) - { + for (int ei = 0; ei < num_elements; ei++) { ElementIndex element_index = itype.get_element(ei); const InterrogateElement &ielement = idb->get_element(element_index); - if (ielement.has_getter()) - { + if (ielement.has_getter()) { FunctionIndex func_index = ielement.get_getter(); record_function(itype, func_index); } - if (ielement.has_setter()) - { + if (ielement.has_setter()) { FunctionIndex func_index = ielement.get_setter(); record_function(itype, func_index); } - } + } object->check_protocols(); int num_nested = itype.number_of_nested_types(); - for (int ni = 0; ni < num_nested; ni++) - { + for (int ni = 0; ni < num_nested; ni++) { TypeIndex nested_index = itype.get_nested_type(ni); record_object(nested_index); } @@ -885,8 +875,14 @@ delete_return_value(ostream &out, int indent_level, // the indicated variable name. //////////////////////////////////////////////////////////////////// void InterfaceMaker:: -output_ref(ostream &out, int indent_level, FunctionRemap *remap, +output_ref(ostream &out, int indent_level, FunctionRemap *remap, const string &varname) const { + + if (TypeManager::is_pointer_to_base(remap->_return_type->get_temporary_type())) { + // Actually, we have it stored in a PointerTo. No need to do anything. + return; + } + if (remap->_type == FunctionRemap::T_constructor || remap->_type == FunctionRemap::T_typecast) { // In either of these cases, we can safely assume the pointer will @@ -915,8 +911,14 @@ output_ref(ostream &out, int indent_level, FunctionRemap *remap, // the indicated variable name. //////////////////////////////////////////////////////////////////// void InterfaceMaker:: -output_unref(ostream &out, int indent_level, FunctionRemap *remap, +output_unref(ostream &out, int indent_level, FunctionRemap *remap, const string &varname) const { + + if (TypeManager::is_pointer_to_base(remap->_return_type->get_temporary_type())) { + // Actually, we have it stored in a PointerTo. No need to do anything. + return; + } + if (remap->_type == FunctionRemap::T_constructor || remap->_type == FunctionRemap::T_typecast) { // In either of these cases, we can safely assume the pointer will @@ -931,8 +933,17 @@ output_unref(ostream &out, int indent_level, FunctionRemap *remap, indent(out, indent_level) << "if (" << varname << " != (" << remap->_return_type->get_new_type()->get_local_name(&parser) << ")NULL) {\n"; - indent(out, indent_level + 2) - << "unref_delete(" << varname << ");\n"; + + if (TypeManager::is_pointer_to_base(remap->_return_type->get_temporary_type())) { + // We're sure the reference count won't reach zero since we have it + // stored in a PointerTo, so call the unref() method directly. + indent(out, indent_level + 2) + << varname << "->unref();\n"; + } else { + indent(out, indent_level + 2) + << "unref_delete(" << varname << ");\n"; + } + indent(out, indent_level) << "}\n"; } @@ -967,7 +978,7 @@ hash_function_signature(FunctionRemap *remap) { nout << "Internal error! Function signature " << remap->_function_signature << " repeated!\n"; remap->_hash = hash; - abort(); + abort(); return; } diff --git a/dtool/src/interrogate/interfaceMaker.h b/dtool/src/interrogate/interfaceMaker.h index 021cb608bf..71a1f0eb6d 100644 --- a/dtool/src/interrogate/interfaceMaker.h +++ b/dtool/src/interrogate/interfaceMaker.h @@ -155,6 +155,7 @@ public: PT_make_copy = 0x0004, PT_copy_constructor = 0x0008, PT_iter = 0x0010, + PT_python_gc = 0x0020, }; int _protocol_types; }; diff --git a/dtool/src/interrogate/interfaceMakerPythonNative.cxx b/dtool/src/interrogate/interfaceMakerPythonNative.cxx index db63d2bc9d..971a5ef24d 100644 --- a/dtool/src/interrogate/interfaceMakerPythonNative.cxx +++ b/dtool/src/interrogate/interfaceMakerPythonNative.cxx @@ -28,6 +28,7 @@ #include "vector" #include "cppParameterList.h" #include "algorithm" +#include "lineStream.h" #include #include @@ -37,8 +38,6 @@ extern InterrogateType dummy_type; extern std::string EXPORT_IMPORT_PREFIX; #define CLASS_PREFIX "Dtool_" -#define INSTANCE_PREFIX "Dtool_" -#define BASE_INSTANCE_NAME "Dtool_PyInstDef" ///////////////////////////////////////////////////////// // Name Remapper... @@ -49,10 +48,6 @@ struct RenameSet { const char *_to; int function_type; }; -struct FlagSet { - const char *_to; - int function_type; -}; /////////////////////////////////////////////////////////////////////////////////////// RenameSet methodRenameDictionary[] = { @@ -104,31 +99,9 @@ RenameSet methodRenameDictionary[] = { { "__deepcopy__" , "__deepcopy__", 0 }, { "print" , "Cprint", 0 }, { "CInterval.set_t", "_priv__cSetT", 0 }, - { "__bool__" , "__bool__", 0 }, - { "__bytes__" , "__bytes__", 0 }, - { "__iter__" , "__iter__", 0 }, - { "__getbuffer__" , "__getbuffer__", 0 }, - { "__releasebuffer__", "__releasebuffer__", 0 }, { NULL, NULL, -1 } }; -const char *InPlaceSet[] = { - "__iadd__", - "__isub__", - "__imul__", - "__idiv__", - "__ior__", - "__iand__", - "__ixor__", - "__ilshift__", - "__irshift__", - "__itruediv__", - "__ifloordiv__", - "__imod__", - "__ipow__", - NULL, -}; - /////////////////////////////////////////////////////////////////////////////////////// RenameSet classRenameDictionary[] = { // No longer used, now empty. @@ -188,6 +161,10 @@ checkKeyword(std::string &cppName) { /////////////////////////////////////////////////////////////////////////////////////// std::string classNameFromCppName(const std::string &cppName, bool mangle) { + if (!mangle_names) { + mangle = false; + } + //# initialize to empty string std::string className = ""; @@ -195,26 +172,29 @@ classNameFromCppName(const std::string &cppName, bool mangle) { const std::string badChars("!@#$%^&*()<>,.-=+~{}? "); bool nextCap = false; + bool nextUscore = false; bool firstChar = true && mangle; for (std::string::const_iterator chr = cppName.begin(); - chr != cppName.end(); - chr++) { + chr != cppName.end(); ++chr) { if ((*chr == '_' || *chr == ' ') && mangle) { nextCap = true; } else if (badChars.find(*chr) != std::string::npos) { - if (!mangle) { - className += '_'; - } + nextUscore = !mangle; } else if (nextCap || firstChar) { className += toupper(*chr); nextCap = false; firstChar = false; + } else if (nextUscore) { + className += '_'; + nextUscore = false; + className += *chr; + } else { - className += * chr; + className += *chr; } } @@ -238,6 +218,10 @@ classNameFromCppName(const std::string &cppName, bool mangle) { /////////////////////////////////////////////////////////////////////////////////////// std::string methodNameFromCppName(const std::string &cppName, const std::string &className, bool mangle) { + if (!mangle_names) { + mangle = false; + } + std::string origName = cppName; if (origName.substr(0, 6) == "__py__") { @@ -297,19 +281,12 @@ std::string methodNameFromCppName(InterfaceMaker::Function *func, const std::str return methodNameFromCppName(cppName, className, mangle); } -/////////////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////////////// - -bool isInplaceFunction(InterfaceMaker::Function *func) { - std::string wname = methodNameFromCppName(func, "", false); - - for (int x = 0; InPlaceSet[x] != NULL; x++) { - if (InPlaceSet[x] == wname) { - return true; - } +std::string methodNameFromCppName(FunctionRemap *remap, const std::string &className, bool mangle) { + std::string cppName = remap->_cppfunc->get_local_name(); + if (remap->_ftype->_flags & CPPFunctionType::F_unary_op) { + cppName += "unary"; } - - return false; + return methodNameFromCppName(cppName, className, mangle); } //////////////////////////////////////////////////////////////////// @@ -327,7 +304,14 @@ bool isInplaceFunction(InterfaceMaker::Function *func) { // important details. //////////////////////////////////////////////////////////////////// bool InterfaceMakerPythonNative:: -get_slotted_function_def(Object *obj, Function *func, SlottedFunctionDef &def) { +get_slotted_function_def(Object *obj, Function *func, FunctionRemap *remap, + SlottedFunctionDef &def) { + if (obj == NULL) { + // Only methods may be slotted. + return false; + } + + def._func = func; def._answer_location = string(); def._wrapper_type = WT_none; def._min_version = 0; @@ -336,179 +320,173 @@ get_slotted_function_def(Object *obj, Function *func, SlottedFunctionDef &def) { bool is_unary_op = func->_ifunc.is_unary_op(); if (method_name == "operator +") { - def._answer_location = "tp_as_number->nb_add"; - def._wrapper_type = WT_numeric_operator; + def._answer_location = "nb_add"; + def._wrapper_type = WT_binary_operator; return true; } if (method_name == "operator -" && is_unary_op) { - def._answer_location = "tp_as_number->nb_negative"; + def._answer_location = "nb_negative"; def._wrapper_type = WT_no_params; return true; } if (method_name == "operator -") { - def._answer_location = "tp_as_number->nb_subtract"; - def._wrapper_type = WT_numeric_operator; + def._answer_location = "nb_subtract"; + def._wrapper_type = WT_binary_operator; return true; } if (method_name == "operator *") { - def._answer_location = "tp_as_number->nb_multiply"; - def._wrapper_type = WT_numeric_operator; + def._answer_location = "nb_multiply"; + def._wrapper_type = WT_binary_operator; return true; } if (method_name == "operator /") { - def._answer_location = "tp_as_number->nb_divide"; - def._wrapper_type = WT_numeric_operator; + // Note: nb_divide does not exist in Python 3. We will probably need some + // smart mechanism for dispatching to either floor_divide or true_divide. + def._answer_location = "nb_divide"; + def._wrapper_type = WT_binary_operator; return true; } if (method_name == "operator %") { - def._answer_location = "tp_as_number->nb_remainder"; - def._wrapper_type = WT_numeric_operator; + def._answer_location = "nb_remainder"; + def._wrapper_type = WT_binary_operator; return true; } if (method_name == "operator <<") { - def._answer_location = "tp_as_number->nb_lshift"; - def._wrapper_type = WT_numeric_operator; + def._answer_location = "nb_lshift"; + def._wrapper_type = WT_binary_operator; return true; } if (method_name == "operator >>") { - def._answer_location = "tp_as_number->nb_rshift"; - def._wrapper_type = WT_numeric_operator; + def._answer_location = "nb_rshift"; + def._wrapper_type = WT_binary_operator; return true; } if (method_name == "operator ^") { - def._answer_location = "tp_as_number->nb_xor"; - def._wrapper_type = WT_numeric_operator; + def._answer_location = "nb_xor"; + def._wrapper_type = WT_binary_operator; return true; } if (method_name == "operator ~" && is_unary_op) { - def._answer_location = "tp_as_number->nb_invert"; + def._answer_location = "nb_invert"; def._wrapper_type = WT_no_params; return true; } if (method_name == "operator &") { - def._answer_location = "tp_as_number->nb_and"; - def._wrapper_type = WT_numeric_operator; + def._answer_location = "nb_and"; + def._wrapper_type = WT_binary_operator; return true; } if (method_name == "operator |") { - def._answer_location = "tp_as_number->nb_or"; - def._wrapper_type = WT_numeric_operator; + def._answer_location = "nb_or"; + def._wrapper_type = WT_binary_operator; return true; } if (method_name == "__pow__") { - def._answer_location = "tp_as_number->nb_power"; + def._answer_location = "nb_power"; def._wrapper_type = WT_ternary_operator; return true; } if (method_name == "operator +=") { - def._answer_location = "tp_as_number->nb_inplace_add"; - def._wrapper_type = WT_one_param; - def._min_version = 0x02000000; + def._answer_location = "nb_inplace_add"; + def._wrapper_type = WT_inplace_binary_operator; return true; } if (method_name == "operator -=") { - def._answer_location = "tp_as_number->nb_inplace_subtract"; - def._wrapper_type = WT_one_param; - def._min_version = 0x02000000; + def._answer_location = "nb_inplace_subtract"; + def._wrapper_type = WT_inplace_binary_operator; return true; } if (method_name == "operator *=") { - def._answer_location = "tp_as_number->nb_inplace_multiply"; - def._wrapper_type = WT_one_param; - def._min_version = 0x02000000; + def._answer_location = "nb_inplace_multiply"; + def._wrapper_type = WT_inplace_binary_operator; return true; } if (method_name == "operator /=") { - def._answer_location = "tp_as_number->nb_inplace_divide"; - def._wrapper_type = WT_one_param; - def._min_version = 0x02000000; + def._answer_location = "nb_inplace_divide"; + def._wrapper_type = WT_inplace_binary_operator; return true; } if (method_name == "operator %=") { - def._answer_location = ".tp_as_number->nb_inplace_remainder"; - def._wrapper_type = WT_one_param; - def._min_version = 0x02000000; + def._answer_location = "nb_inplace_remainder"; + def._wrapper_type = WT_inplace_binary_operator; return true; } if (method_name == "operator <<=") { - def._answer_location = "tp_as_number->nb_inplace_lshift"; - def._wrapper_type = WT_one_param; - def._min_version = 0x02000000; + def._answer_location = "nb_inplace_lshift"; + def._wrapper_type = WT_inplace_binary_operator; return true; } if (method_name == "operator >>=") { - def._answer_location = "tp_as_number->nb_inplace_rshift"; - def._wrapper_type = WT_one_param; - def._min_version = 0x02000000; + def._answer_location = "nb_inplace_rshift"; + def._wrapper_type = WT_inplace_binary_operator; return true; } if (method_name == "operator &=") { - def._answer_location = "tp_as_number->nb_inplace_and"; - def._wrapper_type = WT_one_param; - def._min_version = 0x02000000; + def._answer_location = "nb_inplace_and"; + def._wrapper_type = WT_inplace_binary_operator; return true; } if (method_name == "operator ^=") { - def._answer_location = "tp_as_number->nb_inplace_xor"; - def._wrapper_type = WT_one_param; - def._min_version = 0x02000000; + def._answer_location = "nb_inplace_xor"; + def._wrapper_type = WT_inplace_binary_operator; return true; } if (method_name == "__ipow__") { - def._answer_location = "tp_as_number->nb_inplace_power"; - def._wrapper_type = WT_one_or_two_params; - def._min_version = 0x02000000; + def._answer_location = "nb_inplace_power"; + def._wrapper_type = WT_inplace_ternary_operator; return true; } if (obj->_protocol_types & Object::PT_sequence) { - if (func->_flags & FunctionRemap::F_getitem_int) { - def._answer_location = "tp_as_sequence->sq_item"; + if (remap->_flags & FunctionRemap::F_getitem_int) { + def._answer_location = "sq_item"; def._wrapper_type = WT_sequence_getitem; return true; } - if (func->_flags & FunctionRemap::F_setitem_int) { - def._answer_location = "tp_as_sequence->sq_ass_item"; + if (remap->_flags & FunctionRemap::F_setitem_int || + remap->_flags & FunctionRemap::F_delitem_int) { + def._answer_location = "sq_ass_item"; def._wrapper_type = WT_sequence_setitem; return true; } - if (func->_flags & FunctionRemap::F_size) { - def._answer_location = "tp_as_sequence->sq_length"; + if (remap->_flags & FunctionRemap::F_size) { + def._answer_location = "sq_length"; def._wrapper_type = WT_sequence_size; return true; } } if (obj->_protocol_types & Object::PT_mapping) { - if (func->_flags & FunctionRemap::F_getitem) { - def._answer_location = "tp_as_mapping->mp_subscript"; + if (remap->_flags & FunctionRemap::F_getitem) { + def._answer_location = "mp_subscript"; def._wrapper_type = WT_one_param; return true; } - if (func->_flags & FunctionRemap::F_setitem) { - def._answer_location = "tp_as_mapping->mp_ass_subscript"; + if (remap->_flags & FunctionRemap::F_setitem || + remap->_flags & FunctionRemap::F_delitem) { + def._answer_location = "mp_ass_subscript"; def._wrapper_type = WT_mapping_setitem; return true; } @@ -534,6 +512,14 @@ get_slotted_function_def(Object *obj, Function *func, SlottedFunctionDef &def) { return true; } + if (method_name == "__getattribute__") { + // Like __getattr__, but is called unconditionally, ie. + // does not try PyObject_GenericGetAttr first. + def._answer_location = "tp_getattro"; + def._wrapper_type = WT_one_param; + return true; + } + if (method_name == "__getattr__") { def._answer_location = "tp_getattro"; def._wrapper_type = WT_getattr; @@ -546,62 +532,110 @@ get_slotted_function_def(Object *obj, Function *func, SlottedFunctionDef &def) { return true; } - if (method_name == "__nonzero__") { - // Python 2 style. - def._answer_location = "tp_as_number->nb_nonzero"; - def._wrapper_type = WT_inquiry; + if (method_name == "__delattr__") { + // __delattr__ shares the slot with __setattr__, except + // that it takes only one argument. + def._answer_location = "tp_setattro"; + def._wrapper_type = WT_setattr; return true; } - if (method_name == "__bool__") { - // Python 3 style. - def._answer_location = "tp_as_number->nb_nonzero"; + if (method_name == "__nonzero__" || method_name == "__bool__") { + // Python 2 named it nb_nonzero, Python 3 nb_bool. We refer to it just + // as nb_bool. + def._answer_location = "nb_bool"; def._wrapper_type = WT_inquiry; return true; } if (method_name == "__getbuffer__") { - def._answer_location = "tp_as_buffer->bf_getbuffer"; + def._answer_location = "bf_getbuffer"; def._wrapper_type = WT_getbuffer; - def._min_version = 0x02060000; return true; } if (method_name == "__releasebuffer__") { - def._answer_location = "tp_as_buffer->bf_releasebuffer"; + def._answer_location = "bf_releasebuffer"; def._wrapper_type = WT_releasebuffer; - def._min_version = 0x02060000; return true; } - if (func->_ifunc.is_operator_typecast()) { + if (method_name == "__traverse__") { + def._answer_location = "tp_traverse"; + def._wrapper_type = WT_traverse; + return true; + } + + if (method_name == "__clear__") { + def._answer_location = "tp_clear"; + def._wrapper_type = WT_inquiry; + return true; + } + + if (remap->_type == FunctionRemap::T_typecast_method) { // A typecast operator. Check for a supported low-level typecast type. - if (!func->_remaps.empty()) { - if (TypeManager::is_bool(func->_remaps[0]->_return_type->get_orig_type())) { - // If it's a bool type, then we wrap it with the __nonzero__ - // slot method. - def._answer_location = "tp_as_number->nb_nonzero"; - def._wrapper_type = WT_inquiry; - return true; + if (TypeManager::is_bool(remap->_return_type->get_orig_type())) { + // If it's a bool type, then we wrap it with the __nonzero__ + // slot method. + def._answer_location = "nb_bool"; + def._wrapper_type = WT_inquiry; + return true; - } else if (TypeManager::is_integer(func->_remaps[0]->_return_type->get_orig_type())) { - // An integer type. - def._answer_location = "tp_as_number->nb_int"; - def._wrapper_type = WT_no_params; - return true; + } else if (TypeManager::is_integer(remap->_return_type->get_orig_type())) { + // An integer type. + def._answer_location = "nb_int"; + def._wrapper_type = WT_no_params; + return true; - } else if (TypeManager::is_float(func->_remaps[0]->_return_type->get_orig_type())) { - // A floating-point (or double) type. - def._answer_location = "tp_as_number->nb_float"; - def._wrapper_type = WT_no_params; - return true; - } + } else if (TypeManager::is_float(remap->_return_type->get_orig_type())) { + // A floating-point (or double) type. + def._answer_location = "nb_float"; + def._wrapper_type = WT_no_params; + return true; } } return false; } +//////////////////////////////////////////////////////////////////// +// Function: InterfaceMakerPythonNative::write_function_slot +// Access: Private, Static +// Description: Determines whether the slot occurs in the map of +// slotted functions, and if so, writes out a pointer +// to its wrapper. If not, writes out def (usually 0). +//////////////////////////////////////////////////////////////////// +void InterfaceMakerPythonNative:: +write_function_slot(ostream &out, int indent_level, const SlottedFunctions &slots, + const string &slot, const string &default_) { + + SlottedFunctions::const_iterator rfi = slots.find(slot); + if (rfi == slots.end()) { + indent(out, indent_level) << default_ << ","; + if (default_ == "0") { + out << " // " << slot; + } + out << "\n"; + return; + } + + const SlottedFunctionDef &def = rfi->second; + Function *func = def._func; + + // Add an #ifdef if there is a specific version requirement on this function. + if (def._min_version > 0) { + out << "#if PY_VERSION_HEX >= 0x" << hex << def._min_version << dec << "\n"; + } + + indent(out, indent_level) << "&" << def._wrapper_name << ",\n"; + + if (def._min_version > 0) { + out << "#else\n"; + indent(out, indent_level) << default_ << ",\n"; + out << "#endif\n"; + } +} + /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// void InterfaceMakerPythonNative:: @@ -649,33 +683,40 @@ get_valid_child_classes(std::map &answer, CPPStructTyp // /////////////////////////////////////////////////////////////////////////////// void InterfaceMakerPythonNative:: -write_python_instance(ostream &out, int indent_level, const std::string &return_expr, const std::string &assign_to, std::string &owns_memory_flag, const std::string &class_name, CPPType *ctype, bool inplace, const std::string &const_flag) { - string assign_stmt("return "); - if (!assign_to.empty()) { - assign_stmt = assign_to + " = "; - } +write_python_instance(ostream &out, int indent_level, const string &return_expr, + bool owns_memory, const string &class_name, + CPPType *ctype, bool is_const) { + out << boolalpha; - if (inplace) { - indent(out, indent_level) << "Py_INCREF(self);\n"; - indent(out, indent_level) << assign_stmt << "self;\n"; + if (IsPandaTypedObject(ctype->as_struct_type())) { + // We can't let DTool_CreatePyInstanceTyped do the NULL check since we + // will be grabbing the type index (which would obviously crash when called + // on a NULL pointer), so we do it here. + indent(out, indent_level) + << "if (" << return_expr << " == NULL) {\n"; + indent(out, indent_level) + << " Py_INCREF(Py_None);\n"; + indent(out, indent_level) + << " return Py_None;\n"; + indent(out, indent_level) + << "} else {\n"; + indent(out, indent_level) + << " return DTool_CreatePyInstanceTyped((void *)" << return_expr + << ", " << CLASS_PREFIX << make_safe_name(class_name) << ", " + << owns_memory << ", " << is_const << ", " + << return_expr << "->as_typed_object()->get_type_index());\n"; + indent(out, indent_level) + << "}\n"; } else { - indent(out, indent_level) << "if (" << return_expr << " == NULL) {\n"; - indent(out, indent_level) << " Py_INCREF(Py_None);\n"; - indent(out, indent_level+2) << assign_stmt << "Py_None;\n"; - indent(out, indent_level) << "} else {\n"; - - if (IsPandaTypedObject(ctype->as_struct_type())) { - std::string typestr = "(" + return_expr + ")->as_typed_object()->get_type_index()"; - indent(out, indent_level+2) << assign_stmt - << "DTool_CreatePyInstanceTyped((void *)" << return_expr << ", " << CLASS_PREFIX << make_safe_name(class_name) << ", " << owns_memory_flag << ", " << const_flag << ", " << typestr << ");\n"; - } else { - // indent(out, indent_level) << "if (" << return_expr << "!= NULL)\n"; - indent(out, indent_level+2) << assign_stmt - << "DTool_CreatePyInstance((void *)" << return_expr << ", " << CLASS_PREFIX << make_safe_name(class_name) << ", " << owns_memory_flag << ", " << const_flag << ");\n"; - } - indent(out, indent_level) << "}\n"; + // DTool_CreatePyInstance will do the NULL check. + indent(out, indent_level) + << "return " + << "DTool_CreatePyInstance((void *)" << return_expr << ", " + << CLASS_PREFIX << make_safe_name(class_name) << ", " + << owns_memory << ", " << is_const << ");\n"; } } + //////////////////////////////////////////////////////////////////// // Function: InterfaceMakerPythonNative::Constructor // Access: Public @@ -683,7 +724,7 @@ write_python_instance(ostream &out, int indent_level, const std::string &return_ //////////////////////////////////////////////////////////////////// InterfaceMakerPythonNative:: InterfaceMakerPythonNative(InterrogateModuleDef *def) : - InterfaceMakerPython(def) + InterfaceMakerPython(def) { } @@ -718,7 +759,7 @@ write_prototypes(ostream &out_code, ostream *out_h) { out_code << "//********************************************************************\n"; /* - for (fi = _functions.begin(); fi != _functions.end(); ++fi) + for (fi = _functions.begin(); fi != _functions.end(); ++fi) { Function *func = (*fi); if (!func->_itype.is_global() && is_function_legal(func)) @@ -735,7 +776,7 @@ write_prototypes(ostream &out_code, ostream *out_h) { write_prototypes_class(out_code, out_h, object); } else { //write_prototypes_class_external(out_code, object); - _external_imports.insert(make_safe_name(object->_itype.get_scoped_name())); + _external_imports.insert(object->_itype._cpptype); } } } @@ -745,8 +786,30 @@ write_prototypes(ostream &out_code, ostream *out_h) { out_code << "//*** prototypes for .. External Objects\n"; out_code << "//********************************************************************\n"; - for (std::set::iterator ii = _external_imports.begin(); ii != _external_imports.end(); ii++) { - out_code << "IMPORT_THIS struct Dtool_PyTypedObject Dtool_" << *ii << ";\n"; + for (std::set::iterator ii = _external_imports.begin(); ii != _external_imports.end(); ii++) { + CPPType *type = (*ii); + string class_name = type->get_local_name(&parser); + string safe_name = make_safe_name(class_name); + + out_code << "IMPORT_THIS struct Dtool_PyTypedObject Dtool_" << safe_name << ";\n"; + out_code << "IMPORT_THIS void Dtool_PyModuleClassInit_" << safe_name << "(PyObject *module);\n"; + + int has_coerce = has_coerce_constructor(type->as_struct_type()); + if (TypeManager::is_reference_count(type)) { + if (has_coerce > 0) { + out_code << "IMPORT_THIS bool Dtool_Coerce_" << safe_name << "(PyObject *args, CPT(" << class_name << ") &coerced);\n"; + if (has_coerce > 1) { + out_code << "IMPORT_THIS bool Dtool_Coerce_" << safe_name << "(PyObject *args, PT(" << class_name << ") &coerced);\n"; + } + } + } else { + if (has_coerce > 0) { + out_code << "IMPORT_THIS bool Dtool_Coerce_" << safe_name << "(PyObject *args, " << class_name << " const *&coerced, bool &manage);\n"; + if (has_coerce > 1) { + out_code << "IMPORT_THIS bool Dtool_Coerce_" << safe_name << "(PyObject *args, " << class_name << " *&coerced, bool &manage);\n"; + } + } + } } inside_python_native = false; @@ -754,8 +817,8 @@ write_prototypes(ostream &out_code, ostream *out_h) { ///////////////////////////////////////////////////////////////////////////////////////////// // Function : write_prototypes_class_external -// -// Description : Output enough enformation to a declartion of a externally +// +// Description : Output enough enformation to a declartion of a externally // generated dtool type object ///////////////////////////////////////////////////////////////////////////////////////////// void InterfaceMakerPythonNative:: @@ -769,6 +832,8 @@ write_prototypes_class_external(ostream &out, Object *obj) { out << "//*** prototypes for external.. " << class_name << "\n"; out << "//********************************************************************\n"; + // This typedef is necessary for class templates since we can't pass + // a comma to a macro function. out << "typedef " << c_class_name << " " << class_name << "_localtype;\n"; out << "Define_Module_Class_Forward(" << _def->module_name << ", " << class_name << ", " << class_name << "_localtype, " << classNameFromCppName(preferred_name, false) << ");\n"; } @@ -781,7 +846,7 @@ void InterfaceMakerPythonNative:: write_prototypes_class(ostream &out_code, ostream *out_h, Object *obj) { std::string ClassName = make_safe_name(obj->_itype.get_scoped_name()); Functions::iterator fi; - + out_code << "//********************************************************************\n"; out_code << "//*** prototypes for .. " << ClassName << "\n"; out_code << "//********************************************************************\n"; @@ -814,7 +879,7 @@ write_prototypes_class(ostream &out_code, ostream *out_h, Object *obj) { void InterfaceMakerPythonNative:: write_functions(ostream &out) { inside_python_native = true; - + out << "//********************************************************************\n"; out << "//*** Functions for .. Global\n" ; out << "//********************************************************************\n"; @@ -837,7 +902,7 @@ write_functions(ostream &out) { } } } - + //Objects::iterator oi; for (oi = _objects.begin(); oi != _objects.end(); ++oi) { Object *object = (*oi).second; @@ -855,111 +920,119 @@ write_functions(ostream &out) { inside_python_native = true; } -//////////////////////////////////////////////////////////// -// Function : write_class_details -//////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////// +// Function: InterfaceMakerPythonNative::write_class_details +// Access: Private +// Description: Writes out all of the wrapper methods necessary to +// export the given object. This is called by +// write_functions. +//////////////////////////////////////////////////////////////////// void InterfaceMakerPythonNative:: write_class_details(ostream &out, Object *obj) { Functions::iterator fi; - + Function::Remaps::const_iterator ri; + //std::string cClassName = obj->_itype.get_scoped_name(); std::string ClassName = make_safe_name(obj->_itype.get_scoped_name()); std::string cClassName = obj->_itype.get_true_name(); - + out << "//********************************************************************\n"; out << "//*** Functions for .. " << cClassName << "\n" ; out << "//********************************************************************\n"; + // First write out all the wrapper functions for the methods. for (fi = obj->_methods.begin(); fi != obj->_methods.end(); ++fi) { Function *func = (*fi); if (func) { + // Write the definition of the generic wrapper function for this function. write_function_for_top(out, obj, func); } } + // Now write out generated getters and setters for the properties. Properties::const_iterator pit; for (pit = obj->_properties.begin(); pit != obj->_properties.end(); ++pit) { Property *property = (*pit); const InterrogateElement &ielem = property->_ielement; - bool coercion_attempted = false; + string expected_params; if (property->_getter != NULL) { std::string fname = "PyObject *Dtool_" + ClassName + "_" + ielem.get_name() + "_Getter(PyObject *self, void *)"; - write_function_for_name(out, obj, property->_getter, fname, true, coercion_attempted, AT_no_args, false, false); + write_function_for_name(out, obj, property->_getter->_remaps, + fname, expected_params, true, + AT_no_args, RF_pyobject | RF_err_null); } if (property->_setter != NULL) { std::string fname = "int Dtool_" + ClassName + "_" + ielem.get_name() + "_Setter(PyObject *self, PyObject *arg, void *)"; - write_function_for_name(out, obj, property->_setter, fname, true, coercion_attempted, AT_single_arg, true, false); + write_function_for_name(out, obj, property->_setter->_remaps, + fname, expected_params, true, + AT_single_arg, RF_int); } } + // Write the constructors. if (obj->_constructors.size() == 0) { - out << "int Dtool_Init_" + ClassName + "(PyObject *self, PyObject *args, PyObject *kwds) {\n" - << " PyErr_SetString(PyExc_TypeError, \"cannot init constant class (" << cClassName << ")\");\n" - << " return -1;\n" - << "}\n\n"; - - out << "int Dtool_InitNoCoerce_" << ClassName << "(PyObject *self, PyObject *args) {\n" - << " PyErr_SetString(PyExc_TypeError, \"cannot init constant class (" << cClassName << ")\");\n" + // There is no constructor - write one that simply outputs an error. + out << "int Dtool_Init_" + ClassName + "(PyObject *, PyObject *, PyObject *) {\n" + << "#ifdef NDEBUG\n" + << " Dtool_Raise_TypeError(\"cannot init constant class\");\n" + << "#else\n" + << " Dtool_Raise_TypeError(\"cannot init constant class " << cClassName << "\");\n" + << "#endif\n" << " return -1;\n" << "}\n\n"; } else { - bool coercion_attempted = false; for (fi = obj->_constructors.begin(); fi != obj->_constructors.end(); ++fi) { Function *func = (*fi); std::string fname = "int Dtool_Init_" + ClassName + "(PyObject *self, PyObject *args, PyObject *kwds)"; - write_function_for_name(out, obj, func, fname, true, coercion_attempted, AT_keyword_args, true, false); + string expected_params; + write_function_for_name(out, obj, func->_remaps, fname, expected_params, true, AT_keyword_args, RF_int); } - if (coercion_attempted) { - // If a coercion attempt was written into the above constructor, - // then write a secondary constructor, that won't attempt any - // coercion. We'll need this for nested coercion calls. - for (fi = obj->_constructors.begin(); fi != obj->_constructors.end(); ++fi) { - Function *func = (*fi); - std::string fname = "int Dtool_InitNoCoerce_" + ClassName + "(PyObject *self, PyObject *args)"; - - write_function_for_name(out, obj, func, fname, false, coercion_attempted, AT_varargs, true, false); - } - } else { - // Otherwise, since the above constructor didn't involve any - // coercion anyway, we can use the same function for both - // purposes. Construct a trivial wrapper. - out << "int Dtool_InitNoCoerce_" << ClassName << "(PyObject *self, PyObject *args) {\n" - << " return Dtool_Init_" << ClassName << "(self, args, NULL);\n" - << "}\n\n"; - } - } - - MakeSeqs::iterator msi; - for (msi = obj->_make_seqs.begin(); msi != obj->_make_seqs.end(); ++msi) { - write_make_seq(out, obj, ClassName, *msi); } CPPType *cpptype = TypeManager::resolve_type(obj->_itype._cpptype); + + // If we have "coercion constructors", write a single wrapper to consolidate those. + int has_coerce = has_coerce_constructor(cpptype->as_struct_type()); + if (has_coerce > 0) { + write_coerce_constructor(out, obj, true); + if (has_coerce > 1) { + write_coerce_constructor(out, obj, false); + } + } + + // Write make seqs: generated methods that return a sequence of items. + MakeSeqs::iterator msi; + for (msi = obj->_make_seqs.begin(); msi != obj->_make_seqs.end(); ++msi) { + write_make_seq(out, obj, ClassName, cClassName, *msi); + } + + // Determine which external imports we will need. std::map details; std::map::iterator di; builder.get_type(TypeManager::unwrap(cpptype), false); get_valid_child_classes(details, cpptype->as_struct_type()); for (di = details.begin(); di != details.end(); di++) { //InterrogateType ptype =idb->get_type(di->first); - if (di->second._is_legal_py_class && !isExportThisRun(di->second._structType)) - _external_imports.insert(make_safe_name(di->second._to_class_name)); + if (di->second._is_legal_py_class && !isExportThisRun(di->second._structType)) { + _external_imports.insert(di->second._structType); + } //out << "IMPORT_THIS struct Dtool_PyTypedObject Dtool_" << make_safe_name(di->second._to_class_name) << ";\n"; } - { // the Cast Converter - + // Write support methods to cast from and to pointers of this type. + { out << "inline void *Dtool_UpcastInterface_" << ClassName << "(PyObject *self, Dtool_PyTypedObject *requested_type) {\n"; out << " Dtool_PyTypedObject *SelfType = ((Dtool_PyInstDef *)self)->_My_Type;\n"; out << " if (SelfType != &Dtool_" << ClassName << ") {\n"; - out << " printf(\"" << ClassName << " ** Bad Source Type-- Requesting Conversion from %s to %s\\n\", ((Dtool_PyInstDef *)self)->_My_Type->_name, requested_type->_name); fflush(NULL);\n";; + out << " printf(\"" << ClassName << " ** Bad Source Type-- Requesting Conversion from %s to %s\\n\", Py_TYPE(self)->tp_name, requested_type->_PyType.tp_name); fflush(NULL);\n";; out << " return NULL;\n"; out << " }\n"; out << "\n"; - out << " " << cClassName << " *local_this = (" << cClassName << " *)((Dtool_PyInstDef *)self)->_ptr_to_object;\n"; + out << " " << cClassName << " *local_this = (" << cClassName << " *)((Dtool_PyInstDef *)self)->_ptr_to_object;\n"; out << " if (requested_type == &Dtool_" << ClassName << ") {\n"; out << " return local_this;\n"; out << " }\n"; @@ -1008,22 +1081,47 @@ write_class_declarations(ostream &out, ostream *out_h, Object *obj) { std::string preferred_name = itype.get_name(); std::string class_struct_name = std::string(CLASS_PREFIX) + class_name; + CPPType *type = obj->_itype._cpptype; + + // This typedef is necessary for class templates since we can't pass + // a comma to a macro function. out << "typedef " << c_class_name << " " << class_name << "_localtype;\n"; if (obj->_itype.has_destructor() || obj->_itype.destructor_is_inherited()) { - if (TypeManager::is_reference_count(obj->_itype._cpptype)) { - out << "Define_Module_ClassRef(" << _def->module_name << ", " << class_name << ", " << class_name << "_localtype, " << classNameFromCppName(preferred_name, false) << ");\n"; + if (TypeManager::is_reference_count(type)) { + out << "Define_Module_ClassRef"; } else { - out << "Define_Module_Class(" << _def->module_name << ", " << class_name << ", " << class_name << "_localtype, " << classNameFromCppName(preferred_name, false) << ");\n"; + out << "Define_Module_Class"; } } else { - if (TypeManager::is_reference_count(obj->_itype._cpptype)) { - out << "Define_Module_ClassRef_Private(" << _def->module_name << ", " << class_name << ", " << class_name << "_localtype, " << classNameFromCppName(preferred_name, false) << ");\n"; + if (TypeManager::is_reference_count(type)) { + out << "Define_Module_ClassRef_Private"; } else { - out << "Define_Module_Class_Private(" << _def->module_name << ", " << class_name << ", " << class_name << "_localtype, " << classNameFromCppName(preferred_name, false) << ");\n"; + out << "Define_Module_Class_Private"; } } + out << "(" << _def->module_name << ", " << class_name << ", " << class_name << "_localtype, " << classNameFromCppName(preferred_name, false) << ");\n"; + + out << "EXPORT_THIS void Dtool_PyModuleClassInit_" << class_name << "(PyObject *module);\n"; + + int has_coerce = has_coerce_constructor(type->as_struct_type()); + if (TypeManager::is_reference_count(type)) { + if (has_coerce > 0) { + out << "EXPORT_THIS bool Dtool_Coerce_" << class_name << "(PyObject *args, CPT(" << c_class_name << ") &coerced);\n"; + if (has_coerce > 1) { + out << "EXPORT_THIS bool Dtool_Coerce_" << class_name << "(PyObject *args, PT(" << c_class_name << ") &coerced);\n"; + } + } + } else { + if (has_coerce > 0) { + out << "EXPORT_THIS bool Dtool_Coerce_" << class_name << "(PyObject *args, " << c_class_name << " const *&coerced, bool &manage);\n"; + if (has_coerce > 1) { + out << "EXPORT_THIS bool Dtool_Coerce_" << class_name << "(PyObject *args, " << c_class_name << " *&coerced, bool &manage);\n"; + } + } + } + out << "\n"; if (out_h != NULL) { @@ -1040,9 +1138,41 @@ write_class_declarations(ostream &out, ostream *out_h, Object *obj) { void InterfaceMakerPythonNative:: write_sub_module(ostream &out, Object *obj) { //Object * obj = _objects[_embeded_index] ; - std::string class_name = make_safe_name(obj->_itype.get_scoped_name()); + string class_name = make_safe_name(obj->_itype.get_scoped_name()); out << " // Module init upcall for " << obj->_itype.get_scoped_name() << "\n"; - out << " Dtool_PyModuleClassInit_" << class_name << "(module);\n"; + + if (!obj->_itype.is_typedef()) { + out << " // " << *(obj->_itype._cpptype) << "\n"; + out << " Dtool_PyModuleClassInit_" << class_name << "(module);\n"; + + } else { + // Unwrap typedefs. + TypeIndex wrapped = obj->_itype._wrapped_type; + while (interrogate_type_is_typedef(wrapped)) { + wrapped = interrogate_type_wrapped_type(wrapped); + } + + InterrogateDatabase *idb = InterrogateDatabase::get_ptr(); + const InterrogateType &wrapped_itype = idb->get_type(wrapped); + + class_name = make_safe_name(wrapped_itype.get_scoped_name()); + + out << " // typedef " << wrapped_itype.get_scoped_name() + << " " << *(obj->_itype._cpptype) << "\n"; + + if (!isExportThisRun(wrapped_itype._cpptype)) { + _external_imports.insert(wrapped_itype._cpptype); + } + } + + std::string export_class_name = classNameFromCppName(obj->_itype.get_name(), false); + std::string export_class_name2 = classNameFromCppName(obj->_itype.get_name(), true); + +// out << " Py_INCREF(&Dtool_" << class_name << ".As_PyTypeObject());\n"; + out << " PyModule_AddObject(module, \"" << export_class_name << "\", (PyObject *)&Dtool_" << class_name << ".As_PyTypeObject());\n"; + if (export_class_name != export_class_name2) { + out << " PyModule_AddObject(module, \"" << export_class_name2 << "\", (PyObject *)&Dtool_" << class_name << ".As_PyTypeObject());\n"; + } } ///////////////////////////////////////////////////////////////////////////// @@ -1054,27 +1184,27 @@ write_module_support(ostream &out, ostream *out_h, InterrogateModuleDef *def) { out << "//*** Module Object Linker ..\n"; out << "//********************************************************************\n"; - out << "static void BuildInstants(PyObject * module) {\n"; + out << "static void BuildInstants(PyObject *module) {\n"; + out << " (void) module; // Unused\n"; + Objects::iterator oi; for (oi = _objects.begin(); oi != _objects.end(); ++oi) { Object *object = (*oi).second; - if (!object->_itype.get_outer_class()) { - if (object->_itype.is_enum()) { - int enum_count = object->_itype.number_of_enum_values(); - if (enum_count > 0) { - out << "//********************************************************************\n"; - out << "//*** Module Enums .." << object->_itype.get_scoped_name() << "\n"; - out << "//********************************************************************\n"; - } - for (int xx = 0; xx< enum_count; xx++) { - string name1 = classNameFromCppName(object->_itype.get_enum_value_name(xx), false); - string name2 = classNameFromCppName(object->_itype.get_enum_value_name(xx), true); - int enum_value = object->_itype.get_enum_value(xx); - out << " PyModule_AddIntConstant(module, \"" << name1 << "\", " << enum_value << ");\n"; - if (name1 != name2) { - // Also write the mangled name, for historical purposes. - out << " PyModule_AddIntConstant(module, \"" << name2 << "\", " << enum_value << ");\n"; - } + if (object->_itype.is_enum() && !object->_itype.is_nested()) { + int enum_count = object->_itype.number_of_enum_values(); + if (enum_count > 0) { + out << "//********************************************************************\n"; + out << "//*** Module Enums .." << object->_itype.get_scoped_name() << "\n"; + out << "//********************************************************************\n"; + } + for (int xx = 0; xx < enum_count; xx++) { + string name1 = classNameFromCppName(object->_itype.get_enum_value_name(xx), false); + string name2 = classNameFromCppName(object->_itype.get_enum_value_name(xx), true); + string enum_value = "::" + object->_itype.get_enum_value_name(xx); + out << " PyModule_AddIntConstant(module, \"" << name1 << "\", " << enum_value << ");\n"; + if (name1 != name2) { + // Also write the mangled name, for historical purposes. + out << " PyModule_AddIntConstant(module, \"" << name2 << "\", " << enum_value << ");\n"; } } } @@ -1094,16 +1224,16 @@ write_module_support(ostream &out, ostream *out_h, InterrogateModuleDef *def) { string name2 = classNameFromCppName(iman.get_name(), true); if (iman.has_int_value()) { int value = iman.get_int_value(); - out << " PyModule_AddIntConstant(module, \"" << name1 << "\", " << value << ");\n"; + out << " PyModule_AddIntConstant(module, \"" << name1 << "\", " << value << ");\n"; if (name1 != name2) { // Also write the mangled name, for historical purposes. - out << " PyModule_AddIntConstant(module, \"" << name2 << "\", " << value << ");\n"; + out << " PyModule_AddIntConstant(module, \"" << name2 << "\", " << value << ");\n"; } } else { string value = iman.get_definition(); - out << " PyModule_AddStringConstant(module, \"" << name1 << "\", \"" << value << "\");\n"; + out << " PyModule_AddStringConstant(module, \"" << name1 << "\", \"" << value << "\");\n"; if (name1 != name2) { - out << " PyModule_AddStringConstant(module, \"" << name2 << "\", \"" << value << "\");\n"; + out << " PyModule_AddStringConstant(module, \"" << name2 << "\", \"" << value << "\");\n"; } } } @@ -1111,7 +1241,9 @@ write_module_support(ostream &out, ostream *out_h, InterrogateModuleDef *def) { for (oi = _objects.begin(); oi != _objects.end(); ++oi) { Object *object = (*oi).second; if (!object->_itype.get_outer_class()) { - if (object->_itype.is_class() ||object->_itype.is_struct()) { + if (object->_itype.is_class() || + object->_itype.is_struct() || + object->_itype.is_typedef()) { if (is_cpp_type_legal(object->_itype._cpptype)) { if (isExportThisRun(object->_itype._cpptype)) { write_sub_module(out, object); @@ -1172,8 +1304,8 @@ write_module_support(ostream &out, ostream *out_h, InterrogateModuleDef *def) { if (force_base_functions) { out << " // Support Function For Dtool_types ... for now in each module ??\n"; - out << " {\"Dtool_BorrowThisReference\", &Dtool_BorrowThisReference, METH_VARARGS, \"Used to borrow 'this' pointer (to, from)\\nAssumes no ownership.\"},\n"; - out << " {\"Dtool_AddToDictionary\", &Dtool_AddToDictionary, METH_VARARGS, \"Used to add items into a tp_dict\"},\n"; + out << " {\"Dtool_BorrowThisReference\", &Dtool_BorrowThisReference, METH_VARARGS, \"Used to borrow 'this' pointer (to, from)\\nAssumes no ownership.\"},\n"; + out << " {\"Dtool_AddToDictionary\", &Dtool_AddToDictionary, METH_VARARGS, \"Used to add items into a tp_dict\"},\n"; } out << " {NULL, NULL, 0, NULL}\n" << "};\n\n"; @@ -1248,7 +1380,14 @@ write_module_class(ostream &out, Object *obj) { int num_nested = obj->_itype.number_of_nested_types(); for (int ni = 0; ni < num_nested; ni++) { TypeIndex nested_index = obj->_itype.get_nested_type(ni); - Object * nested_obj = _objects[nested_index]; + if (_objects.count(nested_index) == 0) { + // Illegal type. + continue; + } + + Object *nested_obj = _objects[nested_index]; + assert(nested_obj != (Object *)NULL); + if (nested_obj->_itype.is_class() || nested_obj->_itype.is_struct()) { write_module_class(out, nested_obj); } @@ -1260,15 +1399,14 @@ write_module_class(ostream &out, Object *obj) { std::string ClassName = make_safe_name(obj->_itype.get_scoped_name()); std::string cClassName = obj->_itype.get_true_name(); std::string export_class_name = classNameFromCppName(obj->_itype.get_name(), false); - std::string export_class_name2 = classNameFromCppName(obj->_itype.get_name(), true); Functions::iterator fi; out << "//********************************************************************\n"; out << "//*** Py Init Code For .. " << ClassName << " | " << export_class_name << "\n" ; out << "//********************************************************************\n"; - out << "PyMethodDef Dtool_Methods_" << ClassName << "[] = {\n"; + out << "static PyMethodDef Dtool_Methods_" << ClassName << "[] = {\n"; - std::map slotted_functions; + SlottedFunctions slots; // function Table bool got_copy = false; bool got_deepcopy = false; @@ -1307,16 +1445,56 @@ write_module_class(ostream &out, Object *obj) { flags += " | METH_STATIC"; } - out << " { \"" << name1 << "\", (PyCFunction) &" - << func->_name << ", " << flags << ", (char *) " << func->_name << "_comment},\n"; - if (name1 != name2) { - out << " { \"" << name2 << "\", (PyCFunction) &" - << func->_name << ", " << flags << ", (char *) " << func->_name << "_comment},\n"; + bool has_nonslotted = false; + + Function::Remaps::const_iterator ri; + for (ri = func->_remaps.begin(); ri != func->_remaps.end(); ++ri) { + FunctionRemap *remap = (*ri); + if (!is_remap_legal(remap)) { + continue; + } + + SlottedFunctionDef slotted_def; + + if (get_slotted_function_def(obj, func, remap, slotted_def)) { + const string &key = slotted_def._answer_location; + if (slotted_def._wrapper_type == WT_none) { + slotted_def._wrapper_name = func->_name; + } else { + slotted_def._wrapper_name = func->_name + "_" + key; + } + if (slots.count(key)) { + slots[key]._remaps.insert(remap); + } else { + slots[key] = slotted_def; + slots[key]._remaps.insert(remap); + } + } else { + has_nonslotted = true; + } } - SlottedFunctionDef slotted_def; - if (get_slotted_function_def(obj, func, slotted_def)) { - slotted_functions[func] = slotted_def; + if (has_nonslotted) { + // This is a bit of a hack, as these methods should probably be + // going through the slotted function system. But it's kind of + // pointless to write these out, and a waste of space. + string fname = func->_ifunc.get_name(); + if (fname == "operator <" || + fname == "operator <=" || + fname == "operator ==" || + fname == "operator !=" || + fname == "operator >" || + fname == "operator >=") { + continue; + } + + // This method has non-slotted remaps, so write it out into the function table. + out << " { \"" << name1 << "\", (PyCFunction) &" + << func->_name << ", " << flags << ", (char *) " << func->_name << "_comment},\n"; + if (name1 != name2) { + out << " { \"" << name2 << "\", (PyCFunction) &" + << func->_name << ", " << flags << ", (char *) " << func->_name << "_comment},\n"; + } } } @@ -1352,7 +1530,7 @@ write_module_class(ostream &out, Object *obj) { } } - out << " { NULL, NULL }\n" + out << " { NULL, NULL, 0, NULL }\n" << "};\n\n"; int num_derivations = obj->_itype.number_of_derivations(); @@ -1363,7 +1541,7 @@ write_module_class(ostream &out, Object *obj) { const InterrogateType &d_itype = idb->get_type(d_type_Index); if (is_cpp_type_legal(d_itype._cpptype)) { if (!isExportThisRun(d_itype._cpptype)) { - _external_imports.insert(make_safe_name(d_itype.get_scoped_name().c_str())); + _external_imports.insert(d_itype._cpptype); //out << "IMPORT_THIS struct Dtool_PyTypedObject Dtool_" << make_safe_name(d_itype.get_scoped_name().c_str()) << ";\n"; } @@ -1387,68 +1565,88 @@ write_module_class(ostream &out, Object *obj) { } { - std::map::iterator rfi; // slotted_functions; - for (rfi = slotted_functions.begin(); rfi != slotted_functions.end(); rfi++) { - Function *func = rfi->first; - bool func_varargs = true; - string call_func; + Function *getitem_func; - switch (func->_args_type) { - case AT_keyword_args: - call_func = func->_name + "(self, args, NULL)"; - break; - - case AT_varargs: - call_func = func->_name + "(self, args)"; - break; - - case AT_single_arg: - func_varargs = false; - call_func = func->_name + "(self, arg)"; - break; - - default: - func_varargs = false; - call_func = func->_name + "(self)"; - } + SlottedFunctions::iterator rfi; + for (rfi = slots.begin(); rfi != slots.end(); rfi++) { + const SlottedFunctionDef &def = rfi->second; + Function *func = def._func; switch (rfi->second._wrapper_type) { case WT_no_params: + case WT_iter_next: // TODO: fix iter_next to return NULL instead of None // PyObject *func(PyObject *self) { out << "//////////////////\n"; out << "// A wrapper function to satisfy Python's internal calling conventions.\n"; out << "// " << ClassName << " ..." << rfi->second._answer_location << " = " << methodNameFromCppName(func, export_class_name, false) << "\n"; out << "//////////////////\n"; - out << "static PyObject *" << func->_name << methodNameFromCppName(func, export_class_name, false) << "(PyObject *self) {\n"; - if (func_varargs) { - out << " PyObject *args = PyTuple_New(0);\n"; - out << " PyObject *result = " << call_func << ";\n"; - out << " Py_DECREF(args);\n"; - out << " return result;\n"; - } else { - out << " return " << call_func << ";\n"; - } + out << "static PyObject *" << def._wrapper_name << "(PyObject *self) {\n"; + out << " " << cClassName << " *local_this = NULL;\n"; + out << " if (!Dtool_Call_ExtractThisPointer(self, Dtool_" << ClassName << ", (void **)&local_this)) {\n"; + out << " return NULL;\n"; + out << " }\n\n"; + + string expected_params; + write_function_forset(out, def._remaps, 0, 0, expected_params, 2, true, true, + AT_no_args, RF_pyobject | RF_err_null, false); + + out << " if (!_PyErr_OCCURRED()) {\n"; + out << " return Dtool_Raise_BadArgumentsError(\n"; + output_quoted(out, 6, expected_params); + out << ");\n"; + out << " }\n"; + out << " return NULL;\n"; out << "}\n\n"; } break; case WT_one_param: - case WT_numeric_operator: + case WT_binary_operator: + case WT_inplace_binary_operator: // PyObject *func(PyObject *self, PyObject *one) { + int return_flags = RF_err_null; + if (rfi->second._wrapper_type == WT_inplace_binary_operator) { + return_flags |= RF_self; + } else { + return_flags |= RF_pyobject; + } out << "//////////////////\n"; out << "// A wrapper function to satisfy Python's internal calling conventions.\n"; out << "// " << ClassName << " ..." << rfi->second._answer_location << " = " << methodNameFromCppName(func, export_class_name, false) << "\n"; out << "//////////////////\n"; - out << "static PyObject *" << func->_name << methodNameFromCppName(func, export_class_name, false) << "(PyObject *self, PyObject *arg) {\n"; - if (func_varargs) { - out << " PyObject *args = PyTuple_Pack(1, arg);\n"; - out << " PyObject *result = " << call_func << ";\n"; - out << " Py_DECREF(args);\n"; - out << " return result;\n"; + out << "static PyObject *" << def._wrapper_name << "(PyObject *self, PyObject *arg) {\n"; + out << " " << cClassName << " *local_this = NULL;\n"; + if (rfi->second._wrapper_type != WT_one_param) { + // WT_binary_operator means we must return NotImplemented, instead + // of raising an exception, if the this pointer doesn't + // match. This is for things like __sub__, which Python + // likes to call on the wrong-type objects. + out << " DTOOL_Call_ExtractThisPointerForType(self, &Dtool_" << ClassName << ", (void **)&local_this);\n"; + out << " if (local_this == NULL) {\n"; + out << " Py_INCREF(Py_NotImplemented);\n"; + out << " return Py_NotImplemented;\n"; } else { - out << " return " << call_func << ";\n"; + out << " if (!Dtool_Call_ExtractThisPointer(self, Dtool_" << ClassName << ", (void **)&local_this)) {\n"; + out << " return NULL;\n"; + } + out << " }\n"; + + string expected_params; + write_function_forset(out, def._remaps, 1, 1, expected_params, 2, true, true, + AT_single_arg, return_flags, false); + + if (rfi->second._wrapper_type != WT_one_param) { + out << " Py_INCREF(Py_NotImplemented);\n"; + out << " return Py_NotImplemented;\n"; + } else { + out << " if (!_PyErr_OCCURRED()) {\n"; + out << " return Dtool_Raise_BadArgumentsError(\n"; + output_quoted(out, 6, expected_params); + out << ");\n"; + out << " }\n"; + out << " return NULL;\n"; } out << "}\n\n"; } @@ -1461,27 +1659,71 @@ write_module_class(ostream &out, Object *obj) { out << "// A wrapper function to satisfy Python's internal calling conventions.\n"; out << "// " << ClassName << " ..." << rfi->second._answer_location << " = " << methodNameFromCppName(func, export_class_name, false) << "\n"; out << "//////////////////\n"; - out << "static int " << func->_name << methodNameFromCppName(func, export_class_name, false) << "(PyObject *self, PyObject *arg, PyObject *arg2) {\n"; - if (func_varargs) { - out << " PyObject *args;\n"; - out << " if (arg2 == NULL) {\n"; - out << " args = PyTuple_Pack(1, arg);\n"; - out << " } else {\n"; - out << " args = PyTuple_Pack(2, arg, arg2);\n"; - out << " }\n"; - out << " PyObject *py_result = " << call_func << ";\n"; - out << " Py_DECREF(args);\n"; - } else { - out << " PyObject *py_result = " << call_func << ";\n"; + out << "static int " << def._wrapper_name << "(PyObject *self, PyObject *arg, PyObject *arg2) {\n"; + out << " " << cClassName << " *local_this = NULL;\n"; + out << " if (!Dtool_Call_ExtractThisPointer(self, Dtool_" << ClassName << ", (void **)&local_this)) {\n"; + out << " return -1;\n"; + out << " }\n\n"; + + set setattr_remaps; + set delattr_remaps; + + // This function handles both delattr and setattr. Fish out + // the remaps for both types. + set::const_iterator ri; + for (ri = def._remaps.begin(); ri != def._remaps.end(); ++ri) { + FunctionRemap *remap = (*ri); + + if (remap->_cppfunc->get_simple_name() == "__delattr__" && remap->_parameters.size() == 2) { + delattr_remaps.insert(remap); + + } else if (remap->_cppfunc->get_simple_name() == "__setattr__" && remap->_parameters.size() == 3) { + setattr_remaps.insert(remap); + } } - out << " if (py_result == NULL) return -1;\n"; - out << "#if PY_MAJOR_VERSION >= 3\n"; - out << " int result = PyLong_AsLong(py_result);\n"; - out << "#else\n"; - out << " int result = PyInt_AsLong(py_result);\n"; - out << "#endif\n"; - out << " Py_DECREF(py_result);\n"; - out << " return result;\n"; + + out << " // Determine whether to call __setattr__ or __delattr__.\n"; + out << " if (arg2 != (PyObject *)NULL) { // __setattr__\n"; + + if (!setattr_remaps.empty()) { + out << " PyObject *args = PyTuple_Pack(2, arg, arg2);\n"; + string expected_params; + write_function_forset(out, setattr_remaps, 2, 2, expected_params, 4, + true, true, AT_varargs, RF_int | RF_decref_args, true); + + out << " Py_DECREF(args);\n"; + out << " if (!_PyErr_OCCURRED()) {\n"; + out << " Dtool_Raise_BadArgumentsError(\n"; + output_quoted(out, 8, expected_params); + out << ");\n"; + out << " }\n"; + } else { + out << " PyErr_Format(PyExc_TypeError,\n"; + out << " \"can't set attributes of built-in/extension type '%s'\",\n"; + out << " Py_TYPE(self)->tp_name);\n"; + } + out << " return -1;\n\n"; + + out << " } else { // __delattr__\n"; + + if (!delattr_remaps.empty()) { + string expected_params; + write_function_forset(out, delattr_remaps, 1, 1, expected_params, 4, + true, true, AT_single_arg, RF_int, true); + + out << " if (!_PyErr_OCCURRED()) {\n"; + out << " Dtool_Raise_BadArgumentsError(\n"; + output_quoted(out, 8, expected_params); + out << ");\n"; + out << " }\n"; + } else { + out << " PyErr_Format(PyExc_TypeError,\n"; + out << " \"can't delete attributes of built-in/extension type '%s'\",\n"; + out << " Py_TYPE(self)->tp_name);\n"; + } + out << " return -1;\n"; + out << " }\n"; + out << "}\n\n"; } break; @@ -1489,26 +1731,35 @@ write_module_class(ostream &out, Object *obj) { case WT_getattr: // PyObject *func(PyObject *self, PyObject *one) // Specifically to implement __getattr__. - // With special handling to pass up to - // PyObject_GenericGetAttr() if it returns NULL. + // First calls PyObject_GenericGetAttr(), and only calls the wrapper if it returns NULL. + // If one wants to override this completely, one should define __getattribute__ instead. { out << "//////////////////\n"; out << "// A wrapper function to satisfy Python's internal calling conventions.\n"; out << "// " << ClassName << " ..." << rfi->second._answer_location << " = " << methodNameFromCppName(func, export_class_name, false) << "\n"; out << "//////////////////\n"; - out << "static PyObject *" << func->_name << methodNameFromCppName(func, export_class_name, false) << "(PyObject *self, PyObject *arg) {\n"; - if (func_varargs) { - out << " PyObject *args = PyTuple_Pack(1, arg);\n"; - out << " PyObject *result = " << call_func << ";\n"; - out << " Py_DECREF(args);\n"; - } else { - out << " PyObject *result = " << call_func << ";\n"; - } - out << " if (result == NULL) {\n"; - out << " PyErr_Clear();\n"; - out << " return PyObject_GenericGetAttr(self, arg);\n"; + out << "static PyObject *" << def._wrapper_name << "(PyObject *self, PyObject *arg) {\n"; + out << " PyObject *res = PyObject_GenericGetAttr(self, arg);\n"; + out << " if (res != NULL) {\n"; + out << " return res;\n"; out << " }\n"; - out << " return result;\n"; + out << " if (!PyErr_ExceptionMatches(PyExc_AttributeError)) {\n"; + out << " return NULL;\n"; + out << " }\n"; + out << " PyErr_Clear();\n\n"; + + out << " " << cClassName << " *local_this = NULL;\n"; + out << " if (!Dtool_Call_ExtractThisPointer(self, Dtool_" << ClassName << ", (void **)&local_this)) {\n"; + out << " return NULL;\n"; + out << " }\n\n"; + + string expected_params; + write_function_forset(out, def._remaps, 1, 1, expected_params, 2, + true, true, AT_single_arg, + RF_pyobject | RF_err_null, true); + + //out << " PyErr_Clear();\n"; + out << " return NULL;\n"; out << "}\n\n"; } break; @@ -1520,11 +1771,9 @@ write_module_class(ostream &out, Object *obj) { out << "// A wrapper function to satisfy Python's internal calling conventions.\n"; out << "// " << ClassName << " ..." << rfi->second._answer_location << " = " << methodNameFromCppName(func, export_class_name, false) << "\n"; out << "//////////////////\n"; - out << "static PyObject *" << func->_name << methodNameFromCppName(func, export_class_name, false) << "(PyObject *self, Py_ssize_t index) {\n"; + out << "static PyObject *" << def._wrapper_name << "(PyObject *self, Py_ssize_t index) {\n"; out << " " << cClassName << " *local_this = NULL;\n"; - out << " DTOOL_Call_ExtractThisPointerForType(self, &Dtool_" << ClassName << ", (void **)&local_this);\n"; - out << " if (local_this == NULL) {\n"; - out << " PyErr_SetString(PyExc_AttributeError, \"C++ object is not yet constructed, or already destructed.\");\n"; + out << " if (!Dtool_Call_ExtractThisPointer(self, Dtool_" << ClassName << ", (void **)&local_this)) {\n"; out << " return NULL;\n"; out << " }\n\n"; @@ -1537,23 +1786,19 @@ write_module_class(ostream &out, Object *obj) { out << " return NULL;\n"; out << " }\n"; - // Gather the remaps with the F_getitem_int flag. - std::set remaps; - Function::Remaps::const_iterator ri; - for (ri = func->_remaps.begin(); ri != func->_remaps.end(); ++ri) { - FunctionRemap *remap = (*ri); - if (is_remap_legal(remap) && (remap->_flags & FunctionRemap::F_getitem_int)) { - remaps.insert(remap); - } - } - string expected_params; - bool coercion_attempted = false; - write_function_forset(out, obj, func, remaps, expected_params, 2, false, false, - coercion_attempted, AT_no_args, false, "index"); + write_function_forset(out, def._remaps, 1, 1, expected_params, 2, true, true, + AT_no_args, RF_pyobject | RF_err_null, false, true, "index"); + out << " if (!_PyErr_OCCURRED()) {\n"; + out << " return Dtool_Raise_BadArgumentsError(\n"; + output_quoted(out, 6, expected_params); + out << ");\n"; + out << " }\n"; out << " return NULL;\n"; out << "}\n\n"; + + getitem_func = func; } break; @@ -1564,11 +1809,9 @@ write_module_class(ostream &out, Object *obj) { out << "// A wrapper function to satisfy Python's internal calling conventions.\n"; out << "// " << ClassName << " ..." << rfi->second._answer_location << " = " << methodNameFromCppName(func, export_class_name, false) << "\n"; out << "//////////////////\n"; - out << "static int " << func->_name << methodNameFromCppName(func, export_class_name, false) << "(PyObject *self, Py_ssize_t index, PyObject *arg) {\n"; + out << "static int " << def._wrapper_name << "(PyObject *self, Py_ssize_t index, PyObject *arg) {\n"; out << " " << cClassName << " *local_this = NULL;\n"; - out << " DTOOL_Call_ExtractThisPointerForType(self, &Dtool_" << ClassName << ", (void **)&local_this);\n"; - out << " if (local_this == NULL) {\n"; - out << " PyErr_SetString(PyExc_AttributeError, \"C++ object is not yet constructed, or already destructed.\");\n"; + out << " if (!Dtool_Call_ExtractThisPointer(self, Dtool_" << ClassName << ", (void **)&local_this)) {\n"; out << " return -1;\n"; out << " }\n\n"; @@ -1577,27 +1820,34 @@ write_module_class(ostream &out, Object *obj) { out << " return -1;\n"; out << " }\n"; - // Gather the remaps with the F_getitem_int flag. - std::set remaps; - Function::Remaps::const_iterator ri; - for (ri = func->_remaps.begin(); ri != func->_remaps.end(); ++ri) { + set setitem_remaps; + set delitem_remaps; + + // This function handles both delitem and setitem. Fish out + // the remaps for either one. + set::const_iterator ri; + for (ri = def._remaps.begin(); ri != def._remaps.end(); ++ri) { FunctionRemap *remap = (*ri); - if (is_remap_legal(remap) && (remap->_flags & FunctionRemap::F_setitem_int)) { - remaps.insert(remap); + + if (remap->_flags & FunctionRemap::F_setitem_int) { + setitem_remaps.insert(remap); + + } else if (remap->_flags & FunctionRemap::F_delitem_int) { + delitem_remaps.insert(remap); } } - // Note: we disallow parameter coercion for setitem. It's not clear if anybody - // uses it in this case, and since some people may need to call this function - // very often, it's probably best to disable it for performance. string expected_params; - bool coercion_attempted = false; - write_function_forset(out, obj, func, remaps, expected_params, 2, false, false, - coercion_attempted, AT_single_arg, true, "index"); + out << " if (arg != (PyObject *)NULL) { // __setitem__\n"; + write_function_forset(out, setitem_remaps, 2, 2, expected_params, 4, + true, true, AT_single_arg, RF_int, false, true, "index"); + out << " } else { // __delitem__\n"; + write_function_forset(out, delitem_remaps, 1, 1, expected_params, 4, + true, true, AT_single_arg, RF_int, false, true, "index"); + out << " }\n\n"; - out << " if (!PyErr_Occurred()) {\n"; - out << " PyErr_SetString(PyExc_TypeError,\n"; - out << " \"Arguments must match:\\n\"\n"; + out << " if (!_PyErr_OCCURRED()) {\n"; + out << " Dtool_Raise_BadArgumentsError(\n"; output_quoted(out, 6, expected_params); out << ");\n"; out << " }\n"; @@ -1613,11 +1863,9 @@ write_module_class(ostream &out, Object *obj) { out << "// A wrapper function to satisfy Python's internal calling conventions.\n"; out << "// " << ClassName << " ..." << rfi->second._answer_location << " = " << methodNameFromCppName(func, export_class_name, false) << "\n"; out << "//////////////////\n"; - out << "static Py_ssize_t " << func->_name << methodNameFromCppName(func, export_class_name, false) << "(PyObject *self) {\n"; + out << "static Py_ssize_t " << def._wrapper_name << "(PyObject *self) {\n"; out << " " << cClassName << " *local_this = NULL;\n"; - out << " DTOOL_Call_ExtractThisPointerForType(self, &Dtool_" << ClassName << ", (void **)&local_this);\n"; - out << " if (local_this == NULL) {\n"; - out << " PyErr_SetString(PyExc_AttributeError, \"C++ object is not yet constructed, or already destructed.\");\n"; + out << " if (!Dtool_Call_ExtractThisPointer(self, Dtool_" << ClassName << ", (void **)&local_this)) {\n"; out << " return -1;\n"; out << " }\n\n"; @@ -1634,19 +1882,46 @@ write_module_class(ostream &out, Object *obj) { out << "// A wrapper function to satisfy Python's internal calling conventions.\n"; out << "// " << ClassName << " ..." << rfi->second._answer_location << " = " << methodNameFromCppName(func, export_class_name, false) << "\n"; out << "//////////////////\n"; - out << "static int " << func->_name << methodNameFromCppName(func, export_class_name, false) << "(PyObject *self, PyObject *arg, PyObject *arg2) {\n"; - if (func_varargs) { - out << " PyObject *args = PyTuple_Pack(2, arg, arg2);\n"; - out << " PyObject *result = " << call_func << ";\n"; - out << " Py_DECREF(args);\n"; - } else { - out << " PyObject *result = " << call_func << ";\n"; - } - out << " if (result == NULL) {\n"; + out << "static int " << def._wrapper_name << "(PyObject *self, PyObject *arg, PyObject *arg2) {\n"; + out << " " << cClassName << " *local_this = NULL;\n"; + out << " if (!Dtool_Call_ExtractThisPointer(self, Dtool_" << ClassName << ", (void **)&local_this)) {\n"; out << " return -1;\n"; + out << " }\n\n"; + + set setitem_remaps; + set delitem_remaps; + + // This function handles both delitem and setitem. Fish out + // the remaps for either one. + set::const_iterator ri; + for (ri = def._remaps.begin(); ri != def._remaps.end(); ++ri) { + FunctionRemap *remap = (*ri); + + if (remap->_flags & FunctionRemap::F_setitem_int) { + setitem_remaps.insert(remap); + + } else if (remap->_flags & FunctionRemap::F_delitem_int) { + delitem_remaps.insert(remap); + } + } + + string expected_params; + out << " if (arg2 != (PyObject *)NULL) { // __setitem__\n"; + out << " PyObject *args = PyTuple_Pack(2, arg, arg2);\n"; + write_function_forset(out, setitem_remaps, 2, 2, expected_params, 4, + true, true, AT_varargs, RF_int | RF_decref_args, false); + out << " Py_DECREF(args);\n"; + out << " } else { // __delitem__\n"; + write_function_forset(out, delitem_remaps, 1, 1, expected_params, 4, + true, true, AT_single_arg, RF_int, false); + out << " }\n\n"; + + out << " if (!_PyErr_OCCURRED()) {\n"; + out << " Dtool_Raise_BadArgumentsError(\n"; + output_quoted(out, 6, expected_params); + out << ");\n"; out << " }\n"; - out << " Py_DECREF(result);\n"; - out << " return 0;\n"; + out << " return -1;\n"; out << "}\n\n"; } break; @@ -1658,24 +1933,15 @@ write_module_class(ostream &out, Object *obj) { out << "// A wrapper function to satisfy Python's internal calling conventions.\n"; out << "// " << ClassName << " ..." << rfi->second._answer_location << " = " << methodNameFromCppName(func, export_class_name, false) << "\n"; out << "//////////////////\n"; - out << "static int " << func->_name << methodNameFromCppName(func, export_class_name, false) << "(PyObject *self) {\n"; - if (func_varargs) { - out << " PyObject *args = PyTuple_New(0);\n"; - out << " PyObject *result = " << call_func << ";\n"; - out << " Py_DECREF(args);\n"; - } else { - out << " PyObject *result = " << call_func << ";\n"; - } - out << " if (result == NULL) {\n"; + out << "static int " << def._wrapper_name << "(PyObject *self) {\n"; + out << " " << cClassName << " *local_this = NULL;\n"; + out << " if (!Dtool_Call_ExtractThisPointer(self, Dtool_" << ClassName << ", (void **)&local_this)) {\n"; out << " return -1;\n"; - out << " }\n"; - out << "#if PY_MAJOR_VERSION >= 3\n"; - out << " int iresult = PyLong_AsLong(result);\n"; - out << "#else\n"; - out << " int iresult = PyInt_AsLong(result);\n"; - out << "#endif\n"; - out << " Py_DECREF(result);\n"; - out << " return iresult;\n"; + out << " }\n\n"; + + FunctionRemap *remap = *def._remaps.begin(); + vector_string params; + out << " return (int) " << remap->call_function(out, 4, false, "local_this", params) << ";\n"; out << "}\n\n"; } break; @@ -1693,11 +1959,9 @@ write_module_class(ostream &out, Object *obj) { out << "// A wrapper function to satisfy Python's internal calling conventions.\n"; out << "// " << ClassName << " ..." << rfi->second._answer_location << " = " << methodNameFromCppName(func, export_class_name, false) << "\n"; out << "//////////////////\n"; - out << "static int " << func->_name << methodNameFromCppName(func, export_class_name, false) << "(PyObject *self, Py_buffer *buffer, int flags) {\n"; + out << "static int " << def._wrapper_name << "(PyObject *self, Py_buffer *buffer, int flags) {\n"; out << " " << cClassName << " *local_this = NULL;\n"; - out << " DTOOL_Call_ExtractThisPointerForType(self, &Dtool_" << ClassName << ", (void **) &local_this);\n"; - out << " if (local_this == NULL) {\n"; - out << " PyErr_SetString(PyExc_AttributeError, \"C++ object is not yet constructed, or already destructed.\");\n"; + out << " if (!Dtool_Call_ExtractThisPointer(self, Dtool_" << ClassName << ", (void **)&local_this)) {\n"; out << " return -1;\n"; out << " }\n\n"; @@ -1707,21 +1971,19 @@ write_module_class(ostream &out, Object *obj) { FunctionRemap *remap_nonconst = NULL; // Iterate through the remaps to find the one that matches our parameters. - Function::Remaps::const_iterator ri; - for (ri = func->_remaps.begin(); ri != func->_remaps.end(); ++ri) { + set::const_iterator ri; + for (ri = def._remaps.begin(); ri != def._remaps.end(); ++ri) { FunctionRemap *remap = (*ri); - if (remap->_flags & FunctionRemap::F_getbuffer) { - if (remap->_const_method) { - if ((remap->_flags & FunctionRemap::F_explicit_self) == 0) { - params_const.push_back("self"); - } - remap_const = remap; - } else { - if ((remap->_flags & FunctionRemap::F_explicit_self) == 0) { - params_nonconst.push_back("self"); - } - remap_nonconst = remap; + if (remap->_const_method) { + if ((remap->_flags & FunctionRemap::F_explicit_self) == 0) { + params_const.push_back("self"); } + remap_const = remap; + } else { + if ((remap->_flags & FunctionRemap::F_explicit_self) == 0) { + params_nonconst.push_back("self"); + } + remap_nonconst = remap; } } params_const.push_back("buffer"); @@ -1742,8 +2004,7 @@ write_module_class(ostream &out, Object *obj) { out << " if (!((Dtool_PyInstDef *)self)->_is_const) {\n"; out << " return " << remap_nonconst->call_function(out, 4, false, "local_this", params_nonconst) << ";\n"; out << " } else {\n"; - out << " PyErr_SetString(PyExc_TypeError,\n"; - out << " \"Cannot call " << ClassName << ".__getbuffer__() on a const object.\");\n"; + out << " Dtool_Raise_TypeError(\"Cannot call " << ClassName << ".__getbuffer__() on a const object.\");\n"; out << " return -1;\n"; out << " }\n"; } else if (remap_const != NULL) { @@ -1765,11 +2026,9 @@ write_module_class(ostream &out, Object *obj) { out << "// A wrapper function to satisfy Python's internal calling conventions.\n"; out << "// " << ClassName << " ..." << rfi->second._answer_location << " = " << methodNameFromCppName(func, export_class_name, false) << "\n"; out << "//////////////////\n"; - out << "static void " << func->_name << methodNameFromCppName(func, export_class_name, false) << "(PyObject *self, Py_buffer *buffer) {\n"; + out << "static void " << def._wrapper_name << "(PyObject *self, Py_buffer *buffer) {\n"; out << " " << cClassName << " *local_this = NULL;\n"; - out << " DTOOL_Call_ExtractThisPointerForType(self, &Dtool_" << ClassName << ", (void **) &local_this);\n"; - out << " if (local_this == NULL) {\n"; - out << " PyErr_SetString(PyExc_AttributeError, \"C++ object is not yet constructed, or already destructed.\");\n"; + out << " if (!Dtool_Call_ExtractThisPointer(self, Dtool_" << ClassName << ", (void **)&local_this)) {\n"; out << " return;\n"; out << " }\n\n"; @@ -1779,21 +2038,19 @@ write_module_class(ostream &out, Object *obj) { FunctionRemap *remap_nonconst = NULL; // Iterate through the remaps to find the one that matches our parameters. - Function::Remaps::const_iterator ri; - for (ri = func->_remaps.begin(); ri != func->_remaps.end(); ++ri) { + set::const_iterator ri; + for (ri = def._remaps.begin(); ri != def._remaps.end(); ++ri) { FunctionRemap *remap = (*ri); - if (remap->_flags & FunctionRemap::F_releasebuffer) { - if (remap->_const_method) { - if ((remap->_flags & FunctionRemap::F_explicit_self) == 0) { - params_const.push_back("self"); - } - remap_const = remap; - } else { - if ((remap->_flags & FunctionRemap::F_explicit_self) == 0) { - params_nonconst.push_back("self"); - } - remap_nonconst = remap; + if (remap->_const_method) { + if ((remap->_flags & FunctionRemap::F_explicit_self) == 0) { + params_const.push_back("self"); } + remap_const = remap; + } else { + if ((remap->_flags & FunctionRemap::F_explicit_self) == 0) { + params_nonconst.push_back("self"); + } + remap_nonconst = remap; } } params_const.push_back("buffer"); @@ -1809,21 +2066,25 @@ write_module_class(ostream &out, Object *obj) { } out << " } else {\n"; return_expr = remap_const->call_function(out, 4, false, const_this, params_const); + if (!return_expr.empty()) { out << " " << return_expr << ";\n"; } out << " }\n"; + } else if (remap_nonconst != NULL) { // Doesn't matter if there's no const version. We *have* to call it or else we could leak memory. return_expr = remap_nonconst->call_function(out, 2, false, "local_this", params_nonconst); if (!return_expr.empty()) { out << " " << return_expr << ";\n"; } + } else if (remap_const != NULL) { return_expr = remap_const->call_function(out, 2, false, const_this, params_const); if (!return_expr.empty()) { out << " " << return_expr << ";\n"; } + } else { nout << ClassName << "::__releasebuffer__ does not match the required signature.\n"; out << " return;\n"; @@ -1833,59 +2094,105 @@ write_module_class(ostream &out, Object *obj) { } break; - case WT_iter_next: - // PyObject *func(PyObject *self) - // However, returns NULL instead of None + case WT_ternary_operator: + case WT_inplace_ternary_operator: + // PyObject *func(PyObject *self, PyObject *one, PyObject *two) { + int return_flags = RF_err_null; + if (rfi->second._wrapper_type == WT_inplace_ternary_operator) { + return_flags |= RF_self; + } else { + return_flags |= RF_pyobject; + } out << "//////////////////\n"; out << "// A wrapper function to satisfy Python's internal calling conventions.\n"; out << "// " << ClassName << " ..." << rfi->second._answer_location << " = " << methodNameFromCppName(func, export_class_name, false) << "\n"; out << "//////////////////\n"; - out << "static PyObject *" << func->_name << methodNameFromCppName(func, export_class_name, false) << "(PyObject *self) {\n"; - if (func_varargs) { - out << " PyObject *args = PyTuple_New(0);\n"; - out << " PyObject *result = " << call_func << ";\n"; - out << " Py_DECREF(args);\n"; - } else { - out << " PyObject *result = " << call_func << ";\n"; - } - out << " if (result == Py_None) {\n"; - out << " Py_DECREF(Py_None);\n"; - out << " return NULL;\n"; - out << " } else {\n"; - out << " return result;\n"; + out << "static PyObject *" << def._wrapper_name << "(PyObject *self, PyObject *arg, PyObject *arg2) {\n"; + out << " " << cClassName << " *local_this = NULL;\n"; + out << " DTOOL_Call_ExtractThisPointerForType(self, &Dtool_" << ClassName << ", (void **)&local_this);\n"; + out << " if (local_this == NULL) {\n"; + // WT_ternary_operator means we must return NotImplemented, instead + // of raising an exception, if the this pointer doesn't + // match. This is for things like __pow__, which Python + // likes to call on the wrong-type objects. + out << " Py_INCREF(Py_NotImplemented);\n"; + out << " return Py_NotImplemented;\n"; out << " }\n"; + + set one_param_remaps; + set two_param_remaps; + + set::const_iterator ri; + for (ri = def._remaps.begin(); ri != def._remaps.end(); ++ri) { + FunctionRemap *remap = (*ri); + + if (remap->_parameters.size() == 2) { + one_param_remaps.insert(remap); + + } else if (remap->_parameters.size() == 3) { + two_param_remaps.insert(remap); + } + } + + string expected_params; + + out << " if (arg2 != (PyObject *)NULL) {\n"; + out << " PyObject *args = PyTuple_Pack(2, arg, arg2);\n"; + write_function_forset(out, two_param_remaps, 2, 2, expected_params, 4, + true, true, AT_varargs, RF_pyobject | RF_err_null | RF_decref_args, true); + out << " Py_DECREF(args);\n"; + out << " } else {\n"; + write_function_forset(out, one_param_remaps, 1, 1, expected_params, 4, + true, true, AT_single_arg, RF_pyobject | RF_err_null, true); + out << " }\n\n"; + + out << " if (!_PyErr_OCCURRED()) {\n"; + out << " return Dtool_Raise_BadArgumentsError(\n"; + output_quoted(out, 6, expected_params); + out << ");\n"; + out << " }\n"; + out << " return NULL;\n"; out << "}\n\n"; } break; - case WT_one_or_two_params: - case WT_ternary_operator: - // PyObject *func(PyObject *self, PyObject *one, PyObject *two) + case WT_traverse: + // int __traverse__(PyObject *self, visitproc visit, void *arg) + // This is a low-level function. Overloads are not supported. { out << "//////////////////\n"; out << "// A wrapper function to satisfy Python's internal calling conventions.\n"; out << "// " << ClassName << " ..." << rfi->second._answer_location << " = " << methodNameFromCppName(func, export_class_name, false) << "\n"; out << "//////////////////\n"; - out << "static PyObject *" << func->_name << methodNameFromCppName(func, export_class_name, false) << "(PyObject *self, PyObject *arg, PyObject *arg2) {\n"; - if (func_varargs) { - out << " PyObject *args;\n"; - out << " if (arg2 != Py_None) {\n"; - out << " args = PyTuple_Pack(2, arg, arg2);\n"; - out << " } else {\n"; - out << " args = PyTuple_Pack(1, arg);\n"; - out << " }\n\n"; - out << " PyObject *result = " << call_func << ";\n"; - out << " Py_DECREF(args);\n"; - out << " return result;\n"; - } else { - out << " return " << call_func << ";\n"; + out << "static int " << def._wrapper_name << "(PyObject *self, visitproc visit, void *arg) {\n"; + out << " " << cClassName << " *local_this = NULL;\n"; + out << " DTOOL_Call_ExtractThisPointerForType(self, &Dtool_" << ClassName << ", (void **) &local_this);\n"; + out << " if (local_this == NULL) {\n"; + out << " PyErr_SetString(PyExc_AttributeError, \"C++ object is not yet constructed, or already destructed.\");\n"; + out << " return -1;\n"; + out << " }\n\n"; + + // Find the remap. There should be only one. + FunctionRemap *remap = func->_remaps.front(); + + vector_string params(1); + if (remap->_flags & FunctionRemap::F_explicit_self) { + params.push_back("self"); } + params.push_back("visit"); + params.push_back("arg"); + + out << " return " << remap->call_function(out, 2, false, "local_this", params) << ";\n"; out << "}\n\n"; } break; case WT_none: + // Nothing special about the wrapper function: just write it normally. + string fname = "static PyObject *" + def._wrapper_name + "(PyObject *self, PyObject *args, PyObject *kwds)\n"; + string expected_params; + write_function_for_name(out, obj, func->_remaps, fname, expected_params, true, AT_keyword_args, RF_pyobject | RF_err_null); break; } } @@ -1898,11 +2205,9 @@ write_module_class(ostream &out, Object *obj) { out << "//////////////////\n"; out << "static Py_hash_t Dtool_HashKey_" << ClassName << "(PyObject *self) {\n"; out << " " << cClassName << " *local_this = NULL;\n"; - out << " DTOOL_Call_ExtractThisPointerForType(self, &Dtool_" << ClassName << ", (void **) &local_this);\n"; - out << " if (local_this == NULL) {\n"; - out << " PyErr_SetString(PyExc_AttributeError, \"C++ object is not yet constructed, or already destructed.\");\n"; + out << " if (!Dtool_Call_ExtractThisPointer(self, Dtool_" << ClassName << ", (void **)&local_this)) {\n"; out << " return -1;\n"; - out << " }\n"; + out << " }\n\n"; out << " return local_this->" << get_key << "();\n"; out << "}\n\n"; has_local_hash = true; @@ -1914,11 +2219,9 @@ write_module_class(ostream &out, Object *obj) { out << "//////////////////\n"; out << "static Py_hash_t Dtool_HashKey_" << ClassName << "(PyObject *self) {\n"; out << " " << cClassName << " *local_this = NULL;\n"; - out << " DTOOL_Call_ExtractThisPointerForType(self, &Dtool_" << ClassName << ", (void **) &local_this);\n"; - out << " if (local_this == NULL) {\n"; - out << " PyErr_SetString(PyExc_AttributeError, \"C++ object is not yet constructed, or already destructed.\");\n"; + out << " if (!Dtool_Call_ExtractThisPointer(self, Dtool_" << ClassName << ", (void **)&local_this)) {\n"; out << " return -1;\n"; - out << " }\n"; + out << " }\n\n"; out << " return (Py_hash_t) local_this;\n"; out << "}\n\n"; has_local_hash = true; @@ -1933,11 +2236,9 @@ write_module_class(ostream &out, Object *obj) { out << "//////////////////\n"; out << "static PyObject *Dtool_Repr_" << ClassName << "(PyObject *self) {\n"; out << " " << cClassName << " *local_this = NULL;\n"; - out << " DTOOL_Call_ExtractThisPointerForType(self, &Dtool_" << ClassName << ", (void **) &local_this);\n"; - out << " if (local_this == NULL) {\n"; - out << " PyErr_SetString(PyExc_AttributeError, \"C++ object is not yet constructed, or already destructed.\");\n"; + out << " if (!Dtool_Call_ExtractThisPointer(self, Dtool_" << ClassName << ", (void **)&local_this)) {\n"; out << " return NULL;\n"; - out << " }\n"; + out << " }\n\n"; out << " ostringstream os;\n"; if (need_repr == 3) { out << " invoke_extension(local_this).python_repr(os, \"" @@ -1966,11 +2267,9 @@ write_module_class(ostream &out, Object *obj) { out << "//////////////////\n"; out << "static PyObject *Dtool_Str_" << ClassName << "(PyObject *self) {\n"; out << " " << cClassName << " *local_this = NULL;\n"; - out << " DTOOL_Call_ExtractThisPointerForType(self, &Dtool_" << ClassName << ", (void **)&local_this);\n"; - out << " if (local_this == NULL) {\n"; - out << " PyErr_SetString(PyExc_AttributeError, \"C++ object is not yet constructed, or already destructed.\");\n"; + out << " if (!Dtool_Call_ExtractThisPointer(self, Dtool_" << ClassName << ", (void **)&local_this)) {\n"; out << " return NULL;\n"; - out << " }\n"; + out << " }\n\n"; out << " ostringstream os;\n"; if (need_str == 2) { out << " local_this->write(os, 0);\n"; @@ -1995,9 +2294,7 @@ write_module_class(ostream &out, Object *obj) { out << "//////////////////\n"; out << "static PyObject *Dtool_RichCompare_" << ClassName << "(PyObject *self, PyObject *arg, int op) {\n"; out << " " << cClassName << " *local_this = NULL;\n"; - out << " DTOOL_Call_ExtractThisPointerForType(self, &Dtool_" << ClassName << ", (void **)&local_this);\n"; - out << " if (local_this == NULL) {\n"; - out << " PyErr_SetString(PyExc_AttributeError, \"C++ object is not yet constructed, or already destructed.\");\n"; + out << " if (!Dtool_Call_ExtractThisPointer(self, Dtool_" << ClassName << ", (void **)&local_this)) {\n"; out << " return NULL;\n"; out << " }\n\n"; @@ -2038,13 +2335,9 @@ write_module_class(ostream &out, Object *obj) { } string expected_params; - bool coercion_attempted = false; - write_function_forset(out, obj, func, remaps, expected_params, 4, false, true, - coercion_attempted, AT_single_arg, false); + write_function_forset(out, remaps, 1, 1, expected_params, 4, true, false, + AT_single_arg, RF_pyobject | RF_err_null, false); - out << " if (PyErr_Occurred() && PyErr_ExceptionMatches(PyExc_TypeError)) {\n"; - out << " PyErr_Clear();\n"; - out << " }\n"; out << " break;\n"; out << " }\n"; has_local_richcompare = true; @@ -2052,7 +2345,7 @@ write_module_class(ostream &out, Object *obj) { out << " }\n\n"; - out << " if (PyErr_Occurred()) {\n"; + out << " if (_PyErr_OCCURRED()) {\n"; out << " return (PyObject *)NULL;\n"; out << " }\n\n"; @@ -2062,7 +2355,7 @@ write_module_class(ostream &out, Object *obj) { out << " PyObject *result = " << compare_to_func->_name << "(self, arg);\n"; out << " if (result != NULL) {\n"; out << " if (PyLong_Check(result)) {;\n"; - out << " long cmpval = PyLong_AsLong(result);\n"; + out << " long cmpval = PyLong_AS_LONG(result);\n"; out << " switch (op) {\n"; out << " case Py_LT:\n"; out << " return PyBool_FromLong(cmpval < 0);\n"; @@ -2081,7 +2374,7 @@ write_module_class(ostream &out, Object *obj) { out << " Py_DECREF(result);\n"; out << " }\n\n"; - out << " if (PyErr_Occurred()) {\n"; + out << " if (_PyErr_OCCURRED()) {\n"; out << " if (PyErr_ExceptionMatches(PyExc_TypeError)) {\n"; out << " PyErr_Clear();\n"; out << " } else {\n"; @@ -2096,10 +2389,12 @@ write_module_class(ostream &out, Object *obj) { out << "}\n\n"; } + int num_getset = 0; + if (obj->_properties.size() > 0) { // Write out the array of properties, telling Python which getter and setter // to call when they are assigned or queried in Python code. - out << "PyGetSetDef Dtool_Properties_" << ClassName << "[] = {\n"; + out << "static PyGetSetDef Dtool_Properties_" << ClassName << "[] = {\n"; Properties::const_iterator pit; for (pit = obj->_properties.begin(); pit != obj->_properties.end(); ++pit) { @@ -2109,69 +2404,362 @@ write_module_class(ostream &out, Object *obj) { continue; } - out << " {(char *)\"" << ielem.get_name() << "\"," - << " &Dtool_" << ClassName << "_" << ielem.get_name() << "_Getter,"; + ++num_getset; - if (property->_setter == NULL || !is_function_legal(property->_setter)) { - out << " NULL,"; - } else { - out << " &Dtool_" << ClassName << "_" << ielem.get_name() << "_Setter,"; + string name1 = methodNameFromCppName(ielem.get_name(), "", false); + string name2 = methodNameFromCppName(ielem.get_name(), "", true); + + string getter = "&Dtool_" + ClassName + "_" + ielem.get_name() + "_Getter"; + string setter = "NULL"; + if (property->_setter != NULL && is_function_legal(property->_setter)) { + setter = "&Dtool_" + ClassName + "_" + ielem.get_name() + "_Setter"; } + out << " {(char *)\"" << name1 << "\", " << getter << ", " << setter; + if (ielem.has_comment()) { - out << "(char *)\n"; + out << ", (char *)\n"; output_quoted(out, 4, ielem.get_comment()); out << ",\n "; } else { - out << " NULL, "; + out << ", NULL, "; } // Extra void* argument; we don't make use of it. out << "NULL},\n"; + + if (name1 != name2 && name1 != "__dict__") { + // Add alternative spelling. + out << " {(char *)\"" << name2 << "\", " << getter << ", " << setter + << ", (char *)\n" + << " \"Alias of " << name1 << ", for consistency with old naming conventions.\",\n" + << " NULL},\n"; + } } out << " {NULL},\n"; out << "};\n\n"; } + bool has_parent_class = (obj->_itype.number_of_derivations() != 0); + + // Output the type slot tables. + out << "static PyNumberMethods Dtool_NumberMethods_" << ClassName << " = {\n"; + write_function_slot(out, 2, slots, "nb_add"); + write_function_slot(out, 2, slots, "nb_subtract"); + write_function_slot(out, 2, slots, "nb_multiply"); + out << "#if PY_MAJOR_VERSION < 3\n"; + // Note: nb_divide does not exist in Python 3. We will probably need some + // smart mechanism for dispatching to either floor_divide or true_divide. + write_function_slot(out, 2, slots, "nb_divide"); + out << "#endif\n"; + write_function_slot(out, 2, slots, "nb_remainder"); + write_function_slot(out, 2, slots, "nb_divmod"); + write_function_slot(out, 2, slots, "nb_power"); + write_function_slot(out, 2, slots, "nb_negative"); + write_function_slot(out, 2, slots, "nb_positive"); + write_function_slot(out, 2, slots, "nb_absolute"); + write_function_slot(out, 2, slots, "nb_bool"); + write_function_slot(out, 2, slots, "nb_invert"); + write_function_slot(out, 2, slots, "nb_lshift"); + write_function_slot(out, 2, slots, "nb_rshift"); + write_function_slot(out, 2, slots, "nb_and"); + write_function_slot(out, 2, slots, "nb_xor"); + write_function_slot(out, 2, slots, "nb_or"); + write_function_slot(out, 2, slots, "nb_coerce"); + write_function_slot(out, 2, slots, "nb_int"); + out << " 0, // nb_long\n"; // removed in Python 3 + write_function_slot(out, 2, slots, "nb_float"); + out << "#if PY_MAJOR_VERSION < 3\n"; + write_function_slot(out, 2, slots, "nb_oct"); + write_function_slot(out, 2, slots, "nb_hex"); + out << "#endif\n"; + + write_function_slot(out, 2, slots, "nb_inplace_add"); + write_function_slot(out, 2, slots, "nb_inplace_subtract"); + write_function_slot(out, 2, slots, "nb_inplace_multiply"); + out << "#if PY_MAJOR_VERSION < 3\n"; + write_function_slot(out, 2, slots, "nb_inplace_divide"); + out << "#endif\n"; + write_function_slot(out, 2, slots, "nb_inplace_remainder"); + write_function_slot(out, 2, slots, "nb_inplace_power"); + write_function_slot(out, 2, slots, "nb_inplace_lshift"); + write_function_slot(out, 2, slots, "nb_inplace_rshift"); + write_function_slot(out, 2, slots, "nb_inplace_and"); + write_function_slot(out, 2, slots, "nb_inplace_xor"); + write_function_slot(out, 2, slots, "nb_inplace_or"); + + write_function_slot(out, 2, slots, "nb_floor_divide"); + write_function_slot(out, 2, slots, "nb_true_divide"); + write_function_slot(out, 2, slots, "nb_inplace_floor_divide"); + write_function_slot(out, 2, slots, "nb_inplace_true_divide"); + + out << "#if PY_VERSION_HEX >= 0x02050000\n"; + write_function_slot(out, 2, slots, "nb_index"); + out << "#endif\n"; + out << "};\n\n"; + + // NB: it's tempting not to write this table when a class doesn't have them. + // But then Python won't inherit them from base classes either! So we always + // write this table for now even if it will be full of 0's, unless this type + // has no base classes at all. + if (has_parent_class || (obj->_protocol_types & Object::PT_sequence) != 0) { + out << "static PySequenceMethods Dtool_SequenceMethods_" << ClassName << " = {\n"; + write_function_slot(out, 2, slots, "sq_length"); + write_function_slot(out, 2, slots, "sq_concat"); + write_function_slot(out, 2, slots, "sq_repeat"); + write_function_slot(out, 2, slots, "sq_item"); + out << " 0, // sq_slice\n"; // removed in Python 3 + write_function_slot(out, 2, slots, "sq_ass_item"); + out << " 0, // sq_ass_slice\n"; // removed in Python 3 + write_function_slot(out, 2, slots, "sq_contains"); + + write_function_slot(out, 2, slots, "sq_inplace_concat"); + write_function_slot(out, 2, slots, "sq_inplace_repeat"); + out << "};\n\n"; + } + + // Same note applies as for the SequenceMethods. + if (has_parent_class || (obj->_protocol_types & Object::PT_mapping) != 0) { + out << "static PyMappingMethods Dtool_MappingMethods_" << ClassName << " = {\n"; + write_function_slot(out, 2, slots, "mp_length"); + write_function_slot(out, 2, slots, "mp_subscript"); + write_function_slot(out, 2, slots, "mp_ass_subscript"); + out << "};\n\n"; + } + + // Same note applies as above. + if (has_parent_class || has_local_getbuffer) { + out << "static PyBufferProcs Dtool_BufferProcs_" << ClassName << " = {\n"; + out << "#if PY_MAJOR_VERSION < 3\n"; + write_function_slot(out, 2, slots, "bf_getreadbuffer"); + write_function_slot(out, 2, slots, "bf_getwritebuffer"); + write_function_slot(out, 2, slots, "bf_getsegcount"); + write_function_slot(out, 2, slots, "bf_getcharbuffer"); + out << "#endif\n"; + out << "#if PY_MAJOR_VERSION >= 0x02060000\n"; + write_function_slot(out, 2, slots, "bf_getbuffer"); + write_function_slot(out, 2, slots, "bf_releasebuffer"); + out << "#endif\n"; + out << "};\n\n"; + } + + // Output the actual PyTypeObject definition. + out << "EXPORT_THIS Dtool_PyTypedObject Dtool_" << ClassName << " = {\n"; + out << " {\n"; + out << " PyVarObject_HEAD_INIT(NULL, 0)\n"; + // const char *tp_name; + out << " \"" << _def->module_name << "." << export_class_name << "\",\n"; + // Py_ssize_t tp_basicsize; + out << " sizeof(Dtool_PyInstDef),\n"; + // Py_ssize_t tp_itemsize; + out << " 0, // tp_itemsize\n"; + + // destructor tp_dealloc; + out << " &Dtool_FreeInstance_" << ClassName << ",\n"; + // printfunc tp_print; + write_function_slot(out, 4, slots, "tp_print"); + // getattrfunc tp_getattr; + write_function_slot(out, 4, slots, "tp_getattr"); + // setattrfunc tp_setattr; + write_function_slot(out, 4, slots, "tp_setattr"); + + // cmpfunc tp_compare; (reserved in Python 3) + if (has_local_hash) { + out << "#if PY_MAJOR_VERSION >= 3\n"; + out << " 0,\n"; + out << "#else\n"; + out << " &DTOOL_PyObject_Compare,\n"; + out << "#endif\n"; + } else { + out << " 0, // tp_compare\n"; + } + + // reprfunc tp_repr; + if (has_local_repr) { + out << " &Dtool_Repr_" << ClassName << ",\n"; + } else { + write_function_slot(out, 4, slots, "tp_repr"); + } + + // PyNumberMethods *tp_as_number; + out << " &Dtool_NumberMethods_" << ClassName << ",\n"; + // PySequenceMethods *tp_as_sequence; + if (has_parent_class || (obj->_protocol_types & Object::PT_sequence) != 0) { + out << " &Dtool_SequenceMethods_" << ClassName << ",\n"; + } else { + out << " 0, // tp_as_sequence\n"; + } + // PyMappingMethods *tp_as_mapping; + if (has_parent_class || (obj->_protocol_types & Object::PT_mapping) != 0) { + out << " &Dtool_MappingMethods_" << ClassName << ",\n"; + } else { + out << " 0, // tp_as_mapping\n"; + } + + // hashfunc tp_hash; + if (has_local_hash) { + out << " &Dtool_HashKey_" << ClassName << ",\n"; + } else { + write_function_slot(out, 4, slots, "tp_hash"); + } + + // ternaryfunc tp_call; + write_function_slot(out, 4, slots, "tp_call"); + + // reprfunc tp_str; + if (has_local_str) { + out << " &Dtool_Str_" << ClassName << ",\n"; + } else if (has_local_repr) { + out << " &Dtool_Repr_" << ClassName << ",\n"; + } else { + write_function_slot(out, 4, slots, "tp_str"); + } + + // getattrofunc tp_getattro; + write_function_slot(out, 4, slots, "tp_getattro", + "PyObject_GenericGetAttr"); + // setattrofunc tp_setattro; + write_function_slot(out, 4, slots, "tp_setattro", + "PyObject_GenericSetAttr"); + + // PyBufferProcs *tp_as_buffer; + if (has_parent_class || has_local_getbuffer) { + out << " &Dtool_BufferProcs_" << ClassName << ",\n"; + } else { + out << " 0, // tp_as_buffer\n"; + } + + string gcflag; + if (obj->_protocol_types & Object::PT_python_gc) { + gcflag = " | Py_TPFLAGS_HAVE_GC"; + } + + // long tp_flags; + if (has_local_getbuffer) { + out << "#if PY_VERSION_HEX >= 0x02060000\n"; + out << " Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_CHECKTYPES | Py_TPFLAGS_HAVE_NEWBUFFER" << gcflag << ",\n"; + out << "#else\n"; + out << " Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_CHECKTYPES" << gcflag << ",\n"; + out << "#endif\n"; + } else { + out << " Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_CHECKTYPES" << gcflag << ",\n"; + } + + // const char *tp_doc; + if (obj->_itype.has_comment()) { + out << "#ifdef NDEBUG\n"; + out << " 0,\n"; + out << "#else\n"; + output_quoted(out, 4, obj->_itype.get_comment()); + out << ",\n"; + out << "#endif\n"; + } else { + out << " 0, // tp_doc\n"; + } + + // traverseproc tp_traverse; + write_function_slot(out, 4, slots, "tp_traverse"); + + // inquiry tp_clear; + write_function_slot(out, 4, slots, "tp_clear"); + + // richcmpfunc tp_richcompare; + if (has_local_richcompare) { + out << " &Dtool_RichCompare_" << ClassName << ",\n"; + } else if (has_local_hash) { + out << "#if PY_MAJOR_VERSION >= 3\n"; + out << " &DTOOL_PyObject_RichCompare,\n"; + out << "#else\n"; + out << " 0,\n"; + out << "#endif\n"; + } else { + write_function_slot(out, 4, slots, "tp_richcompare"); + } + + // Py_ssize_t tp_weaklistoffset; + out << " 0, // tp_weaklistoffset\n"; + + // getiterfunc tp_iter; + write_function_slot(out, 4, slots, "tp_iter"); + // iternextfunc tp_iternext; + write_function_slot(out, 4, slots, "tp_iternext"); + + // struct PyMethodDef *tp_methods; + out << " Dtool_Methods_" << ClassName << ",\n"; + // struct PyMemberDef *tp_members; + out << " standard_type_members,\n"; + + // struct PyGetSetDef *tp_getset; + if (num_getset > 0) { + out << " Dtool_Properties_" << ClassName << ",\n"; + } else { + out << " 0, // tp_getset\n"; + } + + // struct _typeobject *tp_base; + out << " 0, // tp_base\n"; + // PyObject *tp_dict; + out << " 0, // tp_dict\n"; + // descrgetfunc tp_descr_get; + write_function_slot(out, 4, slots, "tp_descr_get"); + // descrsetfunc tp_descr_set; + write_function_slot(out, 4, slots, "tp_descr_set"); + // Py_ssize_t tp_dictoffset; + out << " 0, // tp_dictoffset\n"; + // initproc tp_init; + if (obj->_constructors.size() > 0) { + out << " Dtool_Init_" << ClassName << ",\n"; + } else { + out << " 0,\n"; + } + // allocfunc tp_alloc; + out << " PyType_GenericAlloc,\n"; + // newfunc tp_new; + out << " Dtool_new_" << ClassName << ",\n"; + // freefunc tp_free; + if (obj->_protocol_types & Object::PT_python_gc) { + out << " PyObject_GC_Del,\n"; + } else { + out << " PyObject_Del,\n"; + } + // inquiry tp_is_gc; + out << " 0, // tp_is_gc\n"; + // PyObject *tp_bases; + out << " 0, // tp_bases\n"; + // PyObject *tp_mro; + out << " 0, // tp_mro\n"; + // PyObject *tp_cache; + out << " 0, // tp_cache\n"; + // PyObject *tp_subclasses; + out << " 0, // tp_subclasses\n"; + // PyObject *tp_weaklist; + out << " 0, // tp_weaklist\n"; + // destructor tp_del; + out << " 0, // tp_del\n"; + // unsigned int tp_version_tag + out << "#if PY_VERSION_HEX >= 0x02060000\n"; + out << " 0, // tp_version_tag\n"; + out << "#endif\n"; + out << " },\n"; + out << " Dtool_UpcastInterface_" << ClassName << ",\n"; + out << " Dtool_DowncastInterface_" << ClassName << ",\n"; + out << " TypeHandle::none(),\n"; + out << "};\n\n"; + out << "void Dtool_PyModuleClassInit_" << ClassName << "(PyObject *module) {\n"; + out << " (void) module; // Unused\n"; out << " static bool initdone = false;\n"; out << " if (!initdone) {\n"; out << " initdone = true;\n"; - // out << " memset(Dtool_" << ClassName << ".As_PyTypeObject().tp_as_number,0,sizeof(PyNumberMethods));\n"; - // out << " memset(Dtool_" << ClassName << ".As_PyTypeObject().tp_as_mapping,0,sizeof(PyMappingMethods));\n"; - // out << " static Dtool_PyTypedObject *InheritsFrom[] = {"; - // add doc string - if (obj->_itype.has_comment()) { - out << "#ifndef NDEBUG\n"; - out << " // Class documentation string\n"; - out << " Dtool_" << ClassName - << ".As_PyTypeObject().tp_doc =\n"; - output_quoted(out, 6, obj->_itype.get_comment()); - out << ";\n" - << "#endif\n"; - } - - // Add flags. - if (obj->_protocol_types & Object::PT_iter) { - out << "#if PY_VERSION_HEX < 0x03000000\n"; - out << " Dtool_" << ClassName << ".As_PyTypeObject().tp_flags |= Py_TPFLAGS_HAVE_ITER;\n"; - out << "#endif"; - } - if (has_local_getbuffer) { - out << "#if PY_VERSION_HEX >= 0x02060000 && PY_VERSION_HEX < 0x03000000\n"; - out << " Dtool_" << ClassName << ".As_PyTypeObject().tp_flags |= Py_TPFLAGS_HAVE_NEWBUFFER;\n"; - out << "#endif"; - } - - // add bases/// + // Add bases. if (bases.size() > 0) { out << " // Dependent objects\n"; string baseargs; for (vector::iterator bi = bases.begin(); bi != bases.end(); ++bi) { baseargs += ", &Dtool_" + *bi + ".As_PyTypeObject()"; - out << " Dtool_" << make_safe_name(*bi) << "._Dtool_ClassInit(NULL);\n"; + out << " Dtool_PyModuleClassInit_" << make_safe_name(*bi) << "(NULL);\n"; } out << " Dtool_" << ClassName << ".As_PyTypeObject().tp_bases = PyTuple_Pack(" << bases.size() << baseargs << ");\n"; @@ -2181,125 +2769,73 @@ write_module_class(ostream &out, Object *obj) { out << " Dtool_" << ClassName << ".As_PyTypeObject().tp_dict = PyDict_New();\n"; out << " PyDict_SetItemString(Dtool_" << ClassName << ".As_PyTypeObject().tp_dict, \"DtoolClassDict\", Dtool_" << ClassName << ".As_PyTypeObject().tp_dict);\n"; - // Now assign the slotted function definitions. - map::const_iterator rfi; - int prev_min_version = 0; - - for (rfi = slotted_functions.begin(); rfi != slotted_functions.end(); rfi++) { - Function *func = rfi->first; - const SlottedFunctionDef &def = rfi->second; - - // Add an #ifdef if there is a specific version requirement on this function. - if (def._min_version != prev_min_version) { - if (prev_min_version > 0) { - out << "#endif\n"; - } - prev_min_version = def._min_version; - if (def._min_version > 0) { - out << "#if PY_VERSION_HEX >= 0x" << hex << def._min_version << dec << "\n"; - } - } - - out << " // " << rfi->second._answer_location << " = " << methodNameFromCppName(func, export_class_name, false) << "\n"; - - if (def._wrapper_type == WT_none) { - // Bound directly, without wrapper. - out << " Dtool_" << ClassName << ".As_PyTypeObject()." << def._answer_location << " = &" << func->_name << ";\n"; - } else { - // Assign to the wrapper method that was generated earlier. - out << " Dtool_" << ClassName << ".As_PyTypeObject()." << def._answer_location << " = &" << func->_name << methodNameFromCppName(func, export_class_name, false) << ";\n"; - } - } - if (prev_min_version > 0) { - out << "#endif\n"; - } - - // compare and hash work together in PY inherit behavior hmm grrr - // __hash__ - if (has_local_hash) { - out << " // __hash__\n"; - out << " Dtool_" << ClassName << ".As_PyTypeObject().tp_hash = &Dtool_HashKey_" << ClassName << ";\n"; - out << "#if PY_MAJOR_VERSION >= 3\n"; - if (!has_local_richcompare) { - out << " Dtool_" << ClassName << ".As_PyTypeObject().tp_richcompare = &DTOOL_PyObject_RichCompare;\n"; - } - out << "#else\n"; - out << " Dtool_" << ClassName << ".As_PyTypeObject().tp_compare = &DTOOL_PyObject_Compare;\n"; - out << "#endif\n"; - } - - if (has_local_richcompare) { - out << " Dtool_" << ClassName << ".As_PyTypeObject().tp_richcompare = &Dtool_RichCompare_" << ClassName << ";\n"; - } - - if (has_local_repr) { - out << " // __repr__\n"; - out << " Dtool_" << ClassName << ".As_PyTypeObject().tp_repr = &Dtool_Repr_" << ClassName << ";\n"; - } - - if (has_local_str) { - out << " // __str__\n"; - out << " Dtool_" << ClassName << ".As_PyTypeObject().tp_str = &Dtool_Str_" << ClassName << ";\n"; - - } else if (has_local_repr) { - out << " // __str__ Repr Proxy\n"; - out << " Dtool_" << ClassName << ".As_PyTypeObject().tp_str = &Dtool_Repr_" << ClassName << ";\n"; - } - - if (obj->_properties.size() > 0) { - // GetSet descriptor slots. - out << " Dtool_" << ClassName << ".As_PyTypeObject().tp_getset = Dtool_Properties_" << ClassName << ";\n"; - } - int num_nested = obj->_itype.number_of_nested_types(); for (int ni = 0; ni < num_nested; ni++) { TypeIndex nested_index = obj->_itype.get_nested_type(ni); - Object * nested_obj = _objects[nested_index]; + if (_objects.count(nested_index) == 0) { + // Illegal type. + continue; + } + + Object *nested_obj = _objects[nested_index]; + assert(nested_obj != (Object *)NULL); + if (nested_obj->_itype.is_class() || nested_obj->_itype.is_struct()) { std::string ClassName1 = make_safe_name(nested_obj->_itype.get_scoped_name()); std::string ClassName2 = make_safe_name(nested_obj->_itype.get_name()); out << " // Nested Object " << ClassName1 << ";\n"; - out << " Dtool_" << ClassName1 << "._Dtool_ClassInit(NULL);\n"; + out << " Dtool_PyModuleClassInit_" << ClassName1 << "(NULL);\n"; string name1 = classNameFromCppName(ClassName2, false); string name2 = classNameFromCppName(ClassName2, true); out << " PyDict_SetItemString(Dtool_" << ClassName << ".As_PyTypeObject().tp_dict, \"" << name1 << "\", (PyObject *)&Dtool_" << ClassName1 << ".As_PyTypeObject());\n"; if (name1 != name2) { out << " PyDict_SetItemString(Dtool_" << ClassName << ".As_PyTypeObject().tp_dict, \"" << name2 << "\", (PyObject *)&Dtool_" << ClassName1 << ".As_PyTypeObject());\n"; } - } else { - if (nested_obj->_itype.is_enum()) { - out << " // Enum " << nested_obj->_itype.get_scoped_name() << ";\n"; - int enum_count = nested_obj->_itype.number_of_enum_values(); - for (int xx = 0; xx < enum_count; xx++) { - string name1 = classNameFromCppName(nested_obj->_itype.get_enum_value_name(xx), false); - string name2; - if (nested_obj->_itype.has_true_name()) { - name2 = classNameFromCppName(nested_obj->_itype.get_enum_value_name(xx), true); - } else { - // Don't generate the alternative syntax for anonymous enums, since we added support - // for those after we started deprecating the alternative syntax. - name2 = name1; - } - int enum_value = nested_obj->_itype.get_enum_value(xx); - out << "#if PY_MAJOR_VERSION >= 3\n"; - out << " PyDict_SetItemString(Dtool_" << ClassName << ".As_PyTypeObject().tp_dict, \"" << name1 << "\", PyLong_FromLong(" << enum_value << "));\n"; - if (name1 != name2) { - out << " PyDict_SetItemString(Dtool_" << ClassName << ".As_PyTypeObject().tp_dict, \"" << name2 << "\", PyLong_FromLong(" << enum_value << "));\n"; - } - out << "#else\n"; - out << " PyDict_SetItemString(Dtool_" << ClassName << ".As_PyTypeObject().tp_dict, \"" << name1 << "\", PyInt_FromLong(" << enum_value << "));\n"; - if (name1 != name2) { - out << " PyDict_SetItemString(Dtool_" << ClassName << ".As_PyTypeObject().tp_dict, \"" << name2 << "\", PyInt_FromLong(" << enum_value << "));\n"; - } - out << "#endif\n"; + + } else if (nested_obj->_itype.is_typedef()) { + // Unwrap typedefs. + TypeIndex wrapped = nested_obj->_itype._wrapped_type; + while (interrogate_type_is_typedef(wrapped)) { + wrapped = interrogate_type_wrapped_type(wrapped); + } + + // Er, we can only export typedefs to structs. + if (!interrogate_type_is_struct(wrapped)) { + continue; + } + + string ClassName1 = make_safe_name(interrogate_type_scoped_name(wrapped)); + string ClassName2 = make_safe_name(interrogate_type_name(wrapped)); + + string name1 = classNameFromCppName(ClassName2, false); + out << " PyDict_SetItemString(Dtool_" << ClassName << ".As_PyTypeObject().tp_dict, \"" << name1 << "\", (PyObject *)&Dtool_" << ClassName1 << ".As_PyTypeObject());\n"; + // No need to support mangled names for nested typedefs; we only added support recently. + + } else if (nested_obj->_itype.is_enum()) { + out << " // Enum " << nested_obj->_itype.get_scoped_name() << ";\n"; + CPPEnumType *enum_type = nested_obj->_itype._cpptype->as_enum_type(); + CPPEnumType::Elements::const_iterator ei; + for (ei = enum_type->_elements.begin(); ei != enum_type->_elements.end(); ++ei) { + string name1 = classNameFromCppName((*ei)->get_simple_name(), false); + string name2; + if (nested_obj->_itype.has_true_name()) { + name2 = classNameFromCppName((*ei)->get_simple_name(), true); + } else { + // Don't generate the alternative syntax for anonymous enums, since we added support + // for those after we started deprecating the alternative syntax. + name2 = name1; + } + string enum_value = obj->_itype.get_scoped_name() + "::" + (*ei)->get_simple_name(); + out << " PyDict_SetItemString(Dtool_" << ClassName << ".As_PyTypeObject().tp_dict, \"" << name1 << "\", PyLongOrInt_FromLong(" << enum_value << "));\n"; + if (name1 != name2) { + out << " PyDict_SetItemString(Dtool_" << ClassName << ".As_PyTypeObject().tp_dict, \"" << name2 << "\", PyLongOrInt_FromLong(" << enum_value << "));\n"; } } } } out << " if (PyType_Ready(&Dtool_" << ClassName << ".As_PyTypeObject()) < 0) {\n"; - out << " PyErr_SetString(PyExc_TypeError, \"PyType_Ready(" << ClassName << ")\");\n"; - out << " printf(\"Error in PyType_Ready(" << ClassName << ")\");\n"; + out << " Dtool_Raise_TypeError(\"PyType_Ready(" << ClassName << ")\");\n"; out << " return;\n"; out << " }\n"; @@ -2321,23 +2857,16 @@ write_module_class(ostream &out, Object *obj) { out << " }\n"; - out << " if (module != NULL) {\n"; - out << " Py_INCREF(&Dtool_" << ClassName << ".As_PyTypeObject());\n"; - out << " PyModule_AddObject(module, \"" << export_class_name << "\", (PyObject *)&Dtool_" << ClassName << ".As_PyTypeObject());\n"; - if (export_class_name != export_class_name2) { - out << " PyModule_AddObject(module, \"" << export_class_name2 << "\", (PyObject *)&Dtool_" << ClassName << ".As_PyTypeObject());\n"; - } - // Also write out the explicit alternate names. - int num_alt_names = obj->_itype.get_num_alt_names(); - for (int i = 0; i < num_alt_names; ++i) { - string alt_name = make_safe_name(obj->_itype.get_alt_name(i)); - if (export_class_name != alt_name) { - out << " PyModule_AddObject(module, \"" << alt_name << "\", (PyObject *)&Dtool_" << ClassName << ".As_PyTypeObject());\n"; - } - } + //int num_alt_names = obj->_itype.get_num_alt_names(); + //for (int i = 0; i < num_alt_names; ++i) { + // string alt_name = make_safe_name(obj->_itype.get_alt_name(i)); + // if (export_class_name != alt_name) { + // out << " PyModule_AddObject(module, \"" << alt_name << "\", (PyObject *)&Dtool_" << ClassName << ".As_PyTypeObject());\n"; + // } + //} - out << " }\n"; + //out << " }\n"; out << "}\n\n"; } @@ -2354,6 +2883,27 @@ synthesize_this_parameter() { return true; } +//////////////////////////////////////////////////////////////////// +// Function: InterfaceMakerPythonNative::separate_overloading +// Access: Public, Virtual +// Description: This method should be overridden and redefined to +// return true for interfaces that require overloaded +// instances of a function to be defined as separate +// functions (each with its own hashed name), or false +// for interfaces that can support overloading natively, +// and thus only require one wrapper function per each +// overloaded input function. +//////////////////////////////////////////////////////////////////// +bool InterfaceMakerPythonNative:: +separate_overloading() { + // We used to return true here. Nowadays, some of the default + // arguments are handled in the PyArg_ParseTuple code, and some + // are still being considered as separate overloads (this depends + // on a bunch of factors, see collapse_default_remaps). + // This is all handled elsewhere. + return false; +} + //////////////////////////////////////////////////////////////////// // Function: InterfaceMakerPythonNative::get_wrapper_prefix // Access: Protected, Virtual @@ -2420,136 +2970,211 @@ write_prototype_for_name(ostream &out, InterfaceMaker::Function *func, const std } //////////////////////////////////////////////////////////////////// -// Function: InterfaceMakerPythonNative::write_function_for +// Function: InterfaceMakerPythonNative::write_function_for_top // Access: Private // Description: Writes the definition for a function that will call // the indicated C++ function or method. //////////////////////////////////////////////////////////////////// void InterfaceMakerPythonNative:: write_function_for_top(ostream &out, InterfaceMaker::Object *obj, InterfaceMaker::Function *func) { - std::string fname; + + // First check if this function has non-slotted and legal remaps, + // ie. if we should even write it. + bool has_remaps = false; + + Function::Remaps::const_iterator ri; + for (ri = func->_remaps.begin(); ri != func->_remaps.end(); ++ri) { + FunctionRemap *remap = (*ri); + if (!is_remap_legal(remap)) { + continue; + } + + SlottedFunctionDef slotted_def; + if (!get_slotted_function_def(obj, func, remap, slotted_def)) { + has_remaps = true; + } + } + + if (!has_remaps) { + // Nope. + return; + } + + // This is a bit of a hack, as these methods should probably be + // going through the slotted function system. But it's kind of + // pointless to write these out, and a waste of space. + string fname = func->_ifunc.get_name(); + if (fname == "operator <" || + fname == "operator <=" || + fname == "operator ==" || + fname == "operator !=" || + fname == "operator >" || + fname == "operator >=") { + return; + } if (func->_ifunc.is_unary_op()) { assert(func->_args_type == AT_no_args); } - fname = "static PyObject *" + func->_name + "(PyObject *"; + string prototype = "static PyObject *" + func->_name + "(PyObject *"; // This will be NULL for static funcs, so prevent code from using it. if (func->_has_this) { - fname += "self"; + prototype += "self"; } switch (func->_args_type) { case AT_keyword_args: - fname += ", PyObject *args, PyObject *kwds"; + prototype += ", PyObject *args, PyObject *kwds"; break; case AT_varargs: - fname += ", PyObject *args"; + prototype += ", PyObject *args"; break; case AT_single_arg: - fname += ", PyObject *arg"; + prototype += ", PyObject *arg"; break; default: break; } - fname += ")"; + prototype += ")"; - bool coercion_attempted = false; - write_function_for_name(out, obj, func, fname, true, coercion_attempted, func->_args_type, false, true); + string expected_params; + write_function_for_name(out, obj, func->_remaps, prototype, expected_params, true, func->_args_type, RF_pyobject | RF_err_null); + + // Now synthesize a variable for the docstring. + ostringstream comment; + if (!expected_params.empty()) { + comment << "C++ Interface:\n" + << expected_params; + } + + if (func->_ifunc._comment.size() > 2) { + if (!expected_params.empty()) { + comment << "\n"; + } + comment << func->_ifunc._comment; + } + + out << "#ifndef NDEBUG\n"; + out << "static const char *" << func->_name << "_comment =\n"; + output_quoted(out, 2, comment.str()); + out << ";\n"; + out << "#else\n"; + out << "static const char *" << func->_name << "_comment = NULL;\n"; + out << "#endif\n\n"; } //////////////////////////////////////////////////////////////////// -/// Function : write_function_for_name -// -// Wrap a complete name override function for Py..... +// Function: InterfaceMakerPythonNative::write_function_for_name +// Access: Private +// Description: Writes the definition for a function that will call +// the indicated C++ function or method. //////////////////////////////////////////////////////////////////// void InterfaceMakerPythonNative:: -write_function_for_name(ostream &out1, InterfaceMaker::Object *obj, InterfaceMaker::Function *func, - const std::string &function_name, - bool coercion_allowed, bool &coercion_attempted, - ArgsType args_type, bool return_int, bool write_comment) { - ostringstream out; - - std::map > MapSets; +write_function_for_name(ostream &out, Object *obj, + const Function::Remaps &remaps, + const string &function_name, + string &expected_params, + bool coercion_allowed, + ArgsType args_type, int return_flags) { + std::map > map_sets; std::map >::iterator mii; std::set::iterator sii; + bool has_this = false; Function::Remaps::const_iterator ri; - out1 << "/******************************************************************\n" << " * Python type method wrapper for\n"; - for (ri = func->_remaps.begin(); ri != func->_remaps.end(); ++ri) { - FunctionRemap *remap = (*ri); + FunctionRemap *remap = NULL; + int max_required_args = 0; + bool all_nonconst = true; + + out << "/******************************************************************\n" << " * Python type method wrapper for\n"; + for (ri = remaps.begin(); ri != remaps.end(); ++ri) { + remap = (*ri); if (is_remap_legal(remap)) { - int parameter_size = remap->_parameters.size(); - if (remap->_has_this && remap->_type != FunctionRemap::T_constructor) { - parameter_size --; + int max_num_args = remap->_parameters.size(); + if (remap->_has_this) { + has_this = true; + + if (remap->_type != FunctionRemap::T_constructor) { + max_num_args--; + } } - MapSets[parameter_size].insert(remap); - out1 << " * "; - remap->write_orig_prototype(out1, 0); - out1 << "\n"; + if (!remap->_has_this || remap->_const_method) { + all_nonconst = false; + } + + int min_num_args = 0; + FunctionRemap::Parameters::const_iterator pi; + pi = remap->_parameters.begin(); + if (remap->_has_this && pi != remap->_parameters.end()) { + ++pi; + } + for (; pi != remap->_parameters.end(); ++pi) { + ParameterRemap *param = (*pi)._remap; + if (param->get_default_value() != (CPPExpression *)NULL) { + // We've reached the first parameter that takes a default value. + break; + } else { + ++min_num_args; + } + } + + max_required_args = max(max_num_args, max_required_args); + + for (int i = min_num_args; i <= max_num_args; ++i) { + map_sets[i].insert(remap); + } + out << " * "; + remap->write_orig_prototype(out, 0, false, (max_num_args - min_num_args)); + out << "\n"; } else { - out1 << " * Rejected Remap ["; - remap->write_orig_prototype(out1, 0); - out1 << "]\n"; + out << " * Rejected Remap ["; + remap->write_orig_prototype(out, 0); + out << "]\n"; } } - out1 << " *******************************************************************/\n"; + out << " *******************************************************************/\n"; out << function_name << " {\n"; - if (func->_has_this) { - // Extract pointer from 'self' parameter. + if (has_this) { std::string ClassName = make_safe_name(obj->_itype.get_scoped_name()); std::string cClassName = obj->_itype.get_true_name(); + //string class_name = remap->_cpptype->get_simple_name(); - SlottedFunctionDef def; - get_slotted_function_def(obj, func, def); - + // Extract pointer from 'self' parameter. out << " " << cClassName << " *local_this = NULL;\n"; - out << " DTOOL_Call_ExtractThisPointerForType(self, &Dtool_" << ClassName << ", (void **)&local_this);\n"; - out << " if (local_this == NULL) {\n"; - if (def._wrapper_type == WT_numeric_operator || def._wrapper_type == WT_ternary_operator) { - // WT_numeric_operator means we must return NotImplemented, instead - // of raising an exception, if the this pointer doesn't - // match. This is for things like __sub__, which Python - // likes to call on the wrong-type objects. - out << " Py_INCREF(Py_NotImplemented);\n"; - out << " return Py_NotImplemented;\n"; + if (all_nonconst) { + // All remaps are non-const. Also check that this object isn't const. + out << " if (!Dtool_Call_ExtractThisPointer_NonConst(self, Dtool_" << ClassName << ", " + << "(void **)&local_this, \"" << classNameFromCppName(cClassName, false) + << "." << methodNameFromCppName(remap, cClassName, false) << "\")) {\n"; } else { - // Other functions should raise an exception if the this - // pointer isn't set or is the wrong type. - out << " PyErr_SetString(PyExc_AttributeError, \"C++ object is not yet constructed, or already destructed.\");\n"; - if (return_int) { - out << " return -1;\n"; - } else { - out << " return NULL;\n"; - } + out << " if (!Dtool_Call_ExtractThisPointer(self, Dtool_" << ClassName << ", (void **)&local_this)) {\n"; } + + error_return(out, 4, return_flags); out << " }\n"; } - bool is_inplace = isInplaceFunction(func); - - if (MapSets.empty()) { + if (map_sets.empty()) { + error_return(out, 2, return_flags); + out << "}\n\n"; return; } - std::string FunctionComment = func->_ifunc._comment; - std::string FunctionComment1; - if (FunctionComment.size() > 2) { - FunctionComment += "\n"; + if (args_type == AT_keyword_args || args_type == AT_varargs) { + max_required_args = collapse_default_remaps(map_sets, max_required_args); } - if (MapSets.size() > 1) { - string expected_params; - + if (map_sets.size() > 1) { switch (args_type) { case AT_keyword_args: indent(out, 2) << "int parameter_count = PyTuple_Size(args);\n"; @@ -2572,75 +3197,90 @@ write_function_for_name(ostream &out1, InterfaceMaker::Object *obj, InterfaceMak break; } + // Keep track of how many args this function actually takes for + // the error message. We add one to the parameter count for "self", + // following the Python convention. + int add_self = has_this ? 1 : 0; + set num_args; + indent(out, 2) << "switch (parameter_count) {\n"; - for (mii = MapSets.begin(); mii != MapSets.end(); mii ++) { - indent(out, 2) << "case " << mii->first << ": {\n"; + for (mii = map_sets.begin(); mii != map_sets.end(); ++mii) { + int max_args = mii->first; + int min_args = min(max_required_args, max_args); - write_function_forset(out, obj, func, mii->second, expected_params, 4, is_inplace, - coercion_allowed, coercion_attempted, args_type, return_int); + for (int i = min_args; i <= max_args; ++i) { + indent(out, 2) << "case " << i << ":\n"; + num_args.insert(i + add_self); + } + indent(out, 4) << "{\n"; + num_args.insert(max_args + add_self); - indent(out, 4) << "break;\n"; - indent(out, 2) << "}\n"; - } + if (min_args == 1 && max_args == 1 && args_type == AT_varargs) { + // Might as well, since we already checked the number of args. + indent(out, 6) << " PyObject *arg = PyTuple_GET_ITEM(args, 0);\n"; - indent(out, 2) << "default:\n"; - indent(out, 4) - << "PyErr_Format(PyExc_TypeError, \"" - << methodNameFromCppName(func, "", false) - << "() takes "; - - // We add one to the parameter count for "self", following the - // Python convention. - int add_self = func->_has_this ? 1 : 0; - size_t mic; - for (mic = 0, mii = MapSets.begin(); - mii != MapSets.end(); - ++mii, ++mic) { - if (mic == MapSets.size() - 1) { - if (mic == 1) { - out << " or "; - } else { - out << ", or "; - } - } else if (mic != 0) { - out << ", "; + write_function_forset(out, mii->second, min_args, max_args, expected_params, 6, + coercion_allowed, true, AT_single_arg, return_flags, true, !all_nonconst); + } else { + write_function_forset(out, mii->second, min_args, max_args, expected_params, 6, + coercion_allowed, true, args_type, return_flags, true, !all_nonconst); } - out << mii->first + add_self; + indent(out, 4) << "}\n"; + indent(out, 4) << "break;\n"; } - out << " arguments (%d given)\", parameter_count + " << add_self << ");\n"; + // In NDEBUG case, fall through to the error at end of function. + out << "#ifndef NDEBUG\n"; - if (return_int) { - indent(out, 4) << "return -1;\n"; - } else { - indent(out, 4) << "return (PyObject *) NULL;\n"; + indent(out, 2) << "default:\n"; + + // Format an error saying how many arguments we actually take. So much + // logic for such a silly matter. Sheesh. + ostringstream msg; + msg << methodNameFromCppName(remap, "", false) << "() takes "; + + set::iterator si = num_args.begin(); + msg << *si; + if (num_args.size() == 2) { + msg << " or " << *(++si); + } else if (num_args.size() > 2) { + ++si; + while (si != num_args.end()) { + int num = *si; + if ((++si) == num_args.end()) { + msg << " or " << num; + } else { + msg << ", " << num; + } + } + } + msg << " arguments (%d given)"; + + string count_var = "parameter_count"; + if (add_self) { + count_var += " + 1"; } + error_raise_return(out, 4, return_flags, "TypeError", + msg.str(), count_var); + out << "#endif\n"; indent(out, 2) << "}\n"; - out << " if (!PyErr_Occurred()) { // Let error pass on\n"; - out << " PyErr_SetString(PyExc_TypeError,\n"; - out << " \"Arguments must match one of:\\n\"\n"; + out << " if (!_PyErr_OCCURRED()) {\n" + << " "; + if ((return_flags & ~RF_pyobject) == RF_err_null) { + out << "return "; + } + out << "Dtool_Raise_BadArgumentsError(\n"; output_quoted(out, 6, expected_params); - out << ");\n"; - out << " }\n"; + out << ");\n" + << " }\n"; - if (return_int) { - indent(out, 2) << "return -1;\n"; - } else { - indent(out, 2) << "return (PyObject *) NULL;\n"; - } - - if (!expected_params.empty() && FunctionComment1.empty()) { - FunctionComment1 += "C++ Interface:\n"; - } - - FunctionComment1 += expected_params; + error_return(out, 2, return_flags); } else { - string expected_params = ""; - mii = MapSets.begin(); + mii = map_sets.begin(); // If no parameters are accepted, we do need to check that the argument // count is indeed 0, since we won't check that in write_function_instance. @@ -2658,72 +3298,494 @@ write_function_for_name(ostream &out1, InterfaceMaker::Object *obj, InterfaceMak out << " const int parameter_count = PyTuple_GET_SIZE(args);\n"; break; case AT_single_arg: - default: // Shouldn't happen, but let's handle this case nonetheless. out << " {\n"; out << " const int parameter_count = 1;\n"; break; + case AT_no_args: + break; + case AT_unknown: + break; } - out << " PyErr_Format(PyExc_TypeError,\n" - << " \"" << methodNameFromCppName(func, "", false) - << "() takes no arguments (%d given)\",\n" - << " parameter_count);\n"; + out << "#ifdef NDEBUG\n"; + error_raise_return(out, 4, return_flags, "TypeError", "function takes no arguments"); + out << "#else\n"; + error_raise_return(out, 4, return_flags, "TypeError", + methodNameFromCppName(remap, "", false) + "() takes no arguments (%d given)", + "parameter_count"); + out << "#endif\n"; + out << " }\n"; - if (return_int) { - out << " return -1;\n"; - } else { - out << " return (PyObject *) NULL;\n"; - } + } else if (args_type == AT_keyword_args && max_required_args == 1 && mii->first == 1) { + // Check this to be sure, as we handle the case of only 1 keyword arg + // in write_function_forset (not using ParseTupleAndKeywords). + out << " int parameter_count = PyTuple_Size(args);\n" + " if (kwds != NULL) {\n" + " parameter_count += PyDict_Size(kwds);\n" + " }\n" + " if (parameter_count != 1) {\n" + "#ifdef NDEBUG\n"; + error_raise_return(out, 4, return_flags, "TypeError", + "function takes exactly 1 argument"); + out << "#else\n"; + error_raise_return(out, 4, return_flags, "TypeError", + methodNameFromCppName(remap, "", false) + "() takes exactly 1 argument (%d given)", + "parameter_count"); + out << "#endif\n"; out << " }\n"; } - write_function_forset(out, obj, func, mii->second, expected_params, 2, is_inplace, - coercion_allowed, coercion_attempted, args_type, return_int); + int min_args = min(max_required_args, mii->first); + write_function_forset(out, mii->second, min_args, mii->first, expected_params, 2, + coercion_allowed, true, args_type, return_flags, true, !all_nonconst); - out << " if (!PyErr_Occurred()) {\n"; - out << " PyErr_SetString(PyExc_TypeError,\n"; - out << " \"Arguments must match:\\n\"\n"; - output_quoted(out, 6, expected_params); - out << ");\n"; - out << " }\n"; + // This block is often unreachable for many functions... maybe we can + // figure out a way in the future to better determine when it will be + // and won't be necessary to write this out. + if (args_type != AT_no_args) { + out << " if (!_PyErr_OCCURRED()) {\n" + << " "; + if ((return_flags & ~RF_pyobject) == RF_err_null) { + out << "return "; + } + out << "Dtool_Raise_BadArgumentsError(\n"; + output_quoted(out, 6, expected_params); + out << ");\n" + << " }\n"; - if (return_int) { - indent(out, 2) << "return -1;\n"; - } else { - indent(out, 2) << "return (PyObject *) NULL;\n"; + error_return(out, 2, return_flags); } - - if (!expected_params.empty() && FunctionComment1.empty()) { - FunctionComment1 += "C++ Interface:\n"; - } - - FunctionComment1 += expected_params; } out << "}\n\n"; +} - if (!FunctionComment1.empty()) { - FunctionComment = FunctionComment1 + "\n" + FunctionComment; +//////////////////////////////////////////////////////////////////// +// Function: InterfaceMakerPythonNative::write_coerce_constructor +// Access: Private +// Description: Writes the definition for a coerce constructor: a +// special constructor that is called to implicitly +// cast a tuple or other type to a desired type. This +// is done by calling the appropriate constructor or +// static make() function. Constructors marked with +// the "explicit" keyword aren't considered, just like +// in C++. +// +// There are usually two coerce constructors: one for +// const pointers, one for non-const pointers. This +// is due to the possibility that a static make() +// function may return a const pointer. +// +// There are two variants of this: if the class in +// question is a ReferenceCount, the coerce constructor +// takes a reference to a PointerTo or ConstPointerTo +// to store the converted pointer in. Otherwise, it +// is a regular pointer, and an additional boolean +// indicates whether the caller is supposed to call +// "delete" on the coerced pointer or not. +// +// In all cases, the coerce constructor returns a bool +// indicating whether the conversion was possible. +// It does not raise exceptions when none of the +// constructors matched, but just returns false. +//////////////////////////////////////////////////////////////////// +void InterfaceMakerPythonNative:: +write_coerce_constructor(ostream &out, Object *obj, bool is_const) { + std::map > map_sets; + std::map >::iterator mii; + std::set::iterator sii; + + int max_required_args = 0; + + Functions::iterator fi; + Function::Remaps::const_iterator ri; + + // Go through the methods and find appropriate static make() functions. + for (fi = obj->_methods.begin(); fi != obj->_methods.end(); ++fi) { + Function *func = (*fi); + for (ri = func->_remaps.begin(); ri != func->_remaps.end(); ++ri) { + FunctionRemap *remap = (*ri); + if (is_remap_legal(remap) && remap->_flags & FunctionRemap::F_coerce_constructor) { + nassertd(!remap->_has_this) continue; + + // It's a static make() function. + CPPType *return_type = remap->_return_type->get_new_type(); + + if (!is_const && TypeManager::is_const_pointer_or_ref(return_type)) { + // If we're making the non-const coerce constructor, reject + // this remap if it returns a const pointer. + continue; + } + + int max_num_args = remap->_parameters.size(); + int min_num_args = 0; + FunctionRemap::Parameters::const_iterator pi; + for (pi = remap->_parameters.begin(); pi != remap->_parameters.end(); ++pi) { + ParameterRemap *param = (*pi)._remap; + if (param->get_default_value() != (CPPExpression *)NULL) { + // We've reached the first parameter that takes a default value. + break; + } else { + ++min_num_args; + } + } + + // Coerce constructor should take at least one argument. + nassertd(max_num_args > 0) continue; + min_num_args = max(min_num_args, 1); + + max_required_args = max(max_num_args, max_required_args); + + for (int i = min_num_args; i <= max_num_args; ++i) { + map_sets[i].insert(remap); + } + + int parameter_size = remap->_parameters.size(); + map_sets[parameter_size].insert(remap); + } + } } - if (write_comment) { - // Write out the function doc string. We only do this if it is - // not a constructor, since we don't have a place to put the - // constructor doc string. - out << "#ifndef NDEBUG\n"; - out << "static const char *" << func->_name << "_comment =\n"; - output_quoted(out, 2, FunctionComment); - out << ";\n"; - out << "#else\n"; - out << "static const char *" << func->_name << "_comment = NULL;\n"; - out << "#endif\n"; + // Now go through the constructors that are suitable for coercion. + // This excludes copy constructors and ones marked "explicit". + for (fi = obj->_constructors.begin(); fi != obj->_constructors.end(); ++fi) { + Function *func = (*fi); + for (ri = func->_remaps.begin(); ri != func->_remaps.end(); ++ri) { + FunctionRemap *remap = (*ri); + if (is_remap_legal(remap) && remap->_flags & FunctionRemap::F_coerce_constructor) { + nassertd(!remap->_has_this) continue; + + int max_num_args = remap->_parameters.size(); + int min_num_args = 0; + FunctionRemap::Parameters::const_iterator pi; + for (pi = remap->_parameters.begin(); pi != remap->_parameters.end(); ++pi) { + ParameterRemap *param = (*pi)._remap; + if (param->get_default_value() != (CPPExpression *)NULL) { + // We've reached the first parameter that takes a default value. + break; + } else { + ++min_num_args; + } + } + + // Coerce constructor should take at least one argument. + nassertd(max_num_args > 0) continue; + min_num_args = max(min_num_args, 1); + + max_required_args = max(max_num_args, max_required_args); + + for (int i = min_num_args; i <= max_num_args; ++i) { + map_sets[i].insert(remap); + } + + int parameter_size = remap->_parameters.size(); + map_sets[parameter_size].insert(remap); + } + } } - out << "\n"; + std::string ClassName = make_safe_name(obj->_itype.get_scoped_name()); + std::string cClassName = obj->_itype.get_true_name(); + if (TypeManager::is_reference_count(obj->_itype._cpptype)) { + // The coercion works slightly different for reference counted types, since + // we can handle those a bit more nicely by taking advantage of the refcount + // instead of having to use a boolean to indicate that it should be managed. + if (is_const) { + out << "bool Dtool_Coerce_" << ClassName << "(PyObject *args, CPT(" << cClassName << ") &coerced) {\n"; + } else { + out << "bool Dtool_Coerce_" << ClassName << "(PyObject *args, PT(" << cClassName << ") &coerced) {\n"; + } - out1 << out.str(); + // Note: this relies on the PT() being initialized to NULL. This is currently + // the case in all invocations, but this may not be true in the future. + out << " DTOOL_Call_ExtractThisPointerForType(args, &Dtool_" << ClassName << ", (void**)&coerced.cheat());\n"; + out << " if (coerced != NULL) {\n"; + out << " // The argument is already of matching type, no need to coerce.\n"; + if (!is_const) { + out << " if (!((Dtool_PyInstDef *)args)->_is_const) {\n"; + out << " // A non-const instance is required, which this is.\n"; + out << " coerced->ref();\n"; + out << " return true;\n"; + out << " }\n"; + } else { + out << " coerced->ref();\n"; + out << " return true;\n"; + } + } else { + if (is_const) { + out << "bool Dtool_Coerce_" << ClassName << "(PyObject *args, " << cClassName << " const *&coerced, bool &manage) {\n"; + } else { + out << "bool Dtool_Coerce_" << ClassName << "(PyObject *args, " << cClassName << " *&coerced, bool &manage) {\n"; + } + + out << " DTOOL_Call_ExtractThisPointerForType(args, &Dtool_" << ClassName << ", (void**)&coerced);\n"; + out << " if (coerced != NULL) {\n"; + if (!is_const) { + out << " if (!((Dtool_PyInstDef *)args)->_is_const) {\n"; + out << " // A non-const instance is required, which this is.\n"; + out << " return true;\n"; + out << " }\n"; + } else { + out << " return true;\n"; + } + } + + out << " }\n\n"; + + if (map_sets.empty()) { + error_return(out, 2, RF_coerced | RF_err_false); + out << "}\n\n"; + return; + } + + // Coercion constructors are special cases in that they can take either + // a single value or a tuple. (They never, however, take a tuple + // containing a single value.) + string expected_params; + mii = map_sets.find(1); + if (mii != map_sets.end()) { + out << " if (!PyTuple_Check(args)) {\n"; + out << " PyObject *arg = args;\n"; + + write_function_forset(out, mii->second, mii->first, mii->first, expected_params, 4, false, false, + AT_single_arg, RF_coerced | RF_err_false, true, false); + + if (map_sets.size() == 1) { + out << " }\n"; + //out << " PyErr_Clear();\n"; + error_return(out, 2, RF_coerced | RF_err_false); + out << "}\n\n"; + return; + } + + // We take this one out of the map sets. There's not much value in + // coercing tuples containing just one value. + map_sets.erase(mii); + + out << " } else {\n"; + } else { + out << " if (PyTuple_Check(args)) {\n"; + } + + max_required_args = collapse_default_remaps(map_sets, max_required_args); + + if (map_sets.size() > 1) { + indent(out, 4) << "switch (PyTuple_GET_SIZE(args)) {\n"; + + for (mii = map_sets.begin(); mii != map_sets.end(); ++mii) { + int max_args = mii->first; + int min_args = min(max_required_args, max_args); + + // This is not called for tuples containing just one value or no + // values at all, so we should never have to consider that case. + if (min_args < 2) { + min_args = 2; + } + nassertd(max_args >= min_args) continue; + + for (int i = min_args; i < max_args; ++i) { + if (i != 1) { + indent(out, 6) << "case " << i << ":\n"; + } + } + indent(out, 6) << "case " << max_args << ": {\n"; + + write_function_forset(out, mii->second, min_args, max_args, expected_params, 8, false, false, + AT_varargs, RF_coerced | RF_err_false, true, false); + + indent(out, 8) << "break;\n"; + indent(out, 6) << "}\n"; + } + indent(out, 4) << "}\n"; + + } else { + mii = map_sets.begin(); + + int max_args = mii->first; + int min_args = min(max_required_args, max_args); + + // This is not called for tuples containing just one value or no + // values at all, so we should never have to consider that case. + if (min_args < 2) { + min_args = 2; + } + nassertv(max_args >= min_args); + + if (min_args == max_args) { + indent(out, 4) << "if (PyTuple_GET_SIZE(args) == " << mii->first << ") {\n"; + } else { + indent(out, 4) << "Py_ssize_t size = PyTuple_GET_SIZE(args);\n"; + // Not sure if this check really does any good. I guess it's a + // useful early-fail test. + indent(out, 4) << "if (size >= " << min_args << " && size <= " << max_args << ") {\n"; + } + + write_function_forset(out, mii->second, min_args, max_args, expected_params, 6, false, false, + AT_varargs, RF_coerced | RF_err_false, true, false); + indent(out, 4) << "}\n"; + } + + out << " }\n\n"; + //out << " PyErr_Clear();\n"; + error_return(out, 2, RF_coerced | RF_err_false); + out << "}\n\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterfaceMakerPythonNative::collapse_default_remaps +// Access: Private +// Description: Special case optimization: if the last map is a subset +// of the map before it, and the last parameter is only a +// simple parameter type (that we have special default +// argument handling for), we can merge the cases. +// When this happens, we can make use of a special +// feature of PyArg_ParseTuple for handling of these +// last few default arguments. This doesn't work well +// for all types of default expressions, though, hence the +// need for this elaborate checking mechanism down here, +// which goes in parallel with the actual optional arg +// handling logic in write_function_instance. +// +// This isn't just to help reduce the amount of generated +// code; it also enables arbitrary selection of keyword +// arguments for many functions, ie. for this function: +// +// int func(int a=0, int b=0, bool c=false, string d=""); +// +// Thanks to this mechanism, we can call it like so: +// +// func(c=True, d=".") +// +// The return value is the minimum of the number +// of maximum arguments. +// +// Sorry, let me try that again: it returns the +// largest number of arguments for which the overloads +// will be separated out rather than handled via the +// special default handling mechanism. Or something. +// +// Please don't hate me. +///////////////////////////////////////////////////////////////// +int InterfaceMakerPythonNative:: +collapse_default_remaps(std::map > &map_sets, + int max_required_args) { + if (map_sets.size() < 1) { + return max_required_args; + } + + std::map >::reverse_iterator rmi, rmi_next; + rmi = map_sets.rbegin(); + rmi_next = rmi; + for (++rmi_next; rmi_next != map_sets.rend();) { + if (std::includes(rmi_next->second.begin(), rmi_next->second.end(), + rmi->second.begin(), rmi->second.end())) { + + // Check if the nth argument is something we can easily create a default for. + std::set::iterator sii; + for (sii = rmi->second.begin(); sii != rmi->second.end(); ++sii) { + FunctionRemap *remap = (*sii); + int pn = rmi->first - 1; + if (remap->_has_this && remap->_type != FunctionRemap::T_constructor) { + ++pn; + } + nassertd(pn < remap->_parameters.size()) goto abort_iteration; + + ParameterRemap *param = remap->_parameters[pn]._remap; + CPPType *type = param->get_new_type(); + + if (param->new_type_is_atomic_string()) { + CPPType *orig_type = param->get_orig_type(); + if (TypeManager::is_char_pointer(orig_type)) { + } else if (TypeManager::is_wchar_pointer(orig_type)) { + goto abort_iteration; + } else if (TypeManager::is_wstring(orig_type)) { + goto abort_iteration; + } else if (TypeManager::is_const_ptr_to_basic_string_wchar(orig_type)) { + goto abort_iteration; + } else { + // Regular strings are OK if the default argument is a string literal + // or the default string constructor, since those are trivial to handle. + // This actually covers almost all of the cases of default string args. + CPPExpression::Type expr_type = param->get_default_value()->_type; + if (expr_type != CPPExpression::T_default_construct && + expr_type != CPPExpression::T_string) { + goto abort_iteration; + } + } + } else if (TypeManager::is_bool(type)) { + } else if (TypeManager::is_char(type)) { + } else if (TypeManager::is_wchar(type)) { + goto abort_iteration; + } else if (TypeManager::is_longlong(type)) { + } else if (TypeManager::is_integer(type)) { + } else if (TypeManager::is_double(type)) { + } else if (TypeManager::is_float(type)) { + } else if (TypeManager::is_const_char_pointer(type)) { + } else if (TypeManager::is_pointer_to_PyTypeObject(type)) { + } else if (TypeManager::is_pointer_to_PyStringObject(type)) { + } else if (TypeManager::is_pointer_to_PyUnicodeObject(type)) { + } else if (TypeManager::is_pointer_to_PyObject(type)) { + } else if (TypeManager::is_pointer_to_Py_buffer(type)) { + goto abort_iteration; + } else if (TypeManager::is_pointer_to_simple(type)) { + goto abort_iteration; + } else if (TypeManager::is_pointer(type)) { + // I'm allowing other pointer types, but only if the expression happens + // to evaluate to a numeric constant (which will likely only be NULL). + // There are too many issues to resolve right now with allowing more + // complex default expressions, including issues in the C++ parser + // (but the reader is welcome to give it a try!) + CPPExpression::Result res = param->get_default_value()->evaluate(); + if (res._type != CPPExpression::RT_integer && + res._type != CPPExpression::RT_pointer) { + goto abort_iteration; + } + } else { + goto abort_iteration; + } + } + + // rmi_next has a superset of the remaps in rmi, and we are going to + // erase rmi_next, so put all the remaps in rmi. + //rmi->second = rmi_next->second; + + max_required_args = rmi_next->first; + rmi = rmi_next; + ++rmi_next; + } else { + break; + } + } + +abort_iteration: + // Now erase the other remap sets. Reverse iterators are weird, we + // first need to get forward iterators and decrement them by one. + std::map >::iterator erase_begin, erase_end; + erase_begin = rmi.base(); + erase_end = map_sets.rbegin().base(); + --erase_begin; + --erase_end; + + if (erase_begin == erase_end) { + return max_required_args; + } + + // We're never erasing the map set with the highest number of args. + nassertr(erase_end != map_sets.end(), max_required_args); + + // We know erase_begin is a superset of erase_end, but we want all + // the remaps in erase_end (which we aren't erasing). + //if (rmi == map_sets.rbegin()) { + erase_end->second = erase_begin->second; + //} + + map_sets.erase(erase_begin, erase_end); + + assert(map_sets.size() >= 1); + + return max_required_args; } //////////////////////////////////////////////////////// @@ -2731,25 +3793,43 @@ write_function_for_name(ostream &out1, InterfaceMaker::Object *obj, InterfaceMak // // Support Function used to Sort the name based overrides.. For know must be complex to simple //////////////////////////////////////////////////////// -int GetParnetDepth(CPPType *type) { +int get_type_sort(CPPType *type) { int answer = 0; // printf(" %s\n",type->get_local_name().c_str()); - if (TypeManager::is_basic_string_char(type)) { - } else if (TypeManager::is_basic_string_wchar(type)) { - } else if (TypeManager::is_bool(type)) { - } else if (TypeManager::is_unsigned_longlong(type)) { - } else if (TypeManager::is_longlong(type)) { - } else if (TypeManager::is_integer(type)) { - } else if (TypeManager::is_float(type)) { - } else if (TypeManager::is_char_pointer(type)) { - } else if (TypeManager::is_wchar_pointer(type)) { + // The highest numbered one will be checked first. + if (TypeManager::is_pointer_to_Py_buffer(type)) { + return 14; + } else if (TypeManager::is_pointer_to_PyTypeObject(type)) { + return 13; } else if (TypeManager::is_pointer_to_PyObject(type)) { - } else if (TypeManager::is_pointer_to_Py_buffer(type)) { + return 12; + } else if (TypeManager::is_wstring(type)) { + return 11; + } else if (TypeManager::is_wchar_pointer(type)) { + return 10; + } else if (TypeManager::is_string(type)) { + return 9; + } else if (TypeManager::is_char_pointer(type)) { + return 8; + } else if (TypeManager::is_unsigned_longlong(type)) { + return 7; + } else if (TypeManager::is_longlong(type)) { + return 6; + } else if (TypeManager::is_integer(type)) { + return 5; + } else if (TypeManager::is_double(type)) { + return 4; + } else if (TypeManager::is_float(type)) { + return 3; + } else if (TypeManager::is_pointer_to_simple(type)) { + return 2; + } else if (TypeManager::is_bool(type)) { + return 1; } else if (TypeManager::is_pointer(type) || TypeManager::is_reference(type) || TypeManager::is_struct(type)) { - ++answer; + answer = 20; int deepest = 0; TypeIndex type_index = builder.get_type(TypeManager::unwrap(TypeManager::resolve_type(type)), false); InterrogateDatabase *idb = InterrogateDatabase::get_ptr(); @@ -2760,7 +3840,7 @@ int GetParnetDepth(CPPType *type) { for (int di = 0; di < num_derivations; di++) { TypeIndex d_type_Index = itype.get_derivation(di); const InterrogateType &d_itype = idb->get_type(d_type_Index); - int this_one = GetParnetDepth(d_itype._cpptype); + int this_one = get_type_sort(d_itype._cpptype); if (this_one > deepest) { deepest = this_one; } @@ -2790,26 +3870,8 @@ bool RemapCompareLess(FunctionRemap *in1, FunctionRemap *in2) { CPPType *orig_type1 = in1->_parameters[x]._remap->get_orig_type(); CPPType *orig_type2 = in2->_parameters[x]._remap->get_orig_type(); - // Hack to make int sort before float and double. We do - // this by letting everything compare less than float types. - // But if there's a double variant, prefer it over the float variant. - if (TypeManager::is_float(orig_type1) && - !TypeManager::is_double(orig_type1)) { - return false; - } - if (TypeManager::is_float(orig_type2) && - !TypeManager::is_double(orig_type2)) { - return true; - } - if (TypeManager::is_float(orig_type1)) { - return false; - } - if (TypeManager::is_float(orig_type2)) { - return true; - } - - int pd1 = GetParnetDepth(orig_type1); - int pd2 = GetParnetDepth(orig_type2); + int pd1 = get_type_sort(orig_type1); + int pd2 = get_type_sort(orig_type2); if (pd1 != pd2) { return (pd1 > pd2); } @@ -2820,67 +3882,143 @@ bool RemapCompareLess(FunctionRemap *in1, FunctionRemap *in2) { return false; } -/////////////////////////////////////////////////////////// -// Function : write_function_forset +//////////////////////////////////////////////////////////////////// +// Function: InterfaceMakerPythonNative::write_function_forset +// Access: Private +// Description: Writes out a set of function wrappers that handle +// all instances of a particular function with the +// same number of parameters. +// (Actually, in some cases relating to default +// argument handling, this may be called with remaps +// taking a range of parameters.) // -// A set is defined as all remaps that have the same number of paramaters.. -/////////////////////////////////////////////////////////// +// min_num_args and max_num_args are the range of +// parameter counts to respect for these functions. +// This is important for default argument handling. +// +// expected_params is a reference to a string that +// will be filled in with a list of overloads that +// this function takes, for displaying in the doc +// string and error messages. +// +// If coercion_allowed is true, it will attempt +// to convert arguments to the appropriate parameter +// type using the appropriate Dtool_Coerce function. +// This means it may write some remaps twice: once +// without coercion, and then it may go back and +// write it a second time to try parameter coercion. +// +// If report_errors is true, it will print an error +// and exit when one has occurred, instead of falling +// back to the next overload. This is automatically +// disabled when more than one function is passed. +// +// args_type indicates whether this function takes +// no args, a single PyObject* arg, an args tuple, +// or an args tuple and kwargs dictionary. +// +// return_flags indicates which value should be +// returned from the wrapper function and what should +// be returned on error. +// +// If check_exceptions is false, it will not check +// if the function raised an exception, except if +// it took PyObject* arguments. This should NEVER +// be false for C++ functions that call Python code, +// since that would block a meaningful exception +// like SystemExit or KeyboardInterrupt. +// +// If verify_const is set, it will write out a check +// to make sure that non-const functions aren't called +// for a const "this". This is usually only false when +// write_function_for_name has already done this check +// (which it does when *all* remaps are non-const). +// +// If first_pexpr is not empty, it represents the +// preconverted value of the first parameter. This +// is a special-case hack for one of the slot functions. +//////////////////////////////////////////////////////////////////// void InterfaceMakerPythonNative:: -write_function_forset(ostream &out, InterfaceMaker::Object *obj, - InterfaceMaker::Function *func, - std::set &remapsin, +write_function_forset(ostream &out, + const std::set &remapsin, + int min_num_args, int max_num_args, string &expected_params, int indent_level, - bool is_inplace, bool coercion_allowed, - bool &coercion_attempted, - ArgsType args_type, - bool return_int, const string &first_pexpr) { - // Do we accept any parameters that are class objects? If so, we - // might need to check for parameter coercion. - bool coercion_possible = false; - if (coercion_allowed) { - std::set::const_iterator sii; - for (sii = remapsin.begin(); sii != remapsin.end() && !coercion_possible; ++sii) { - FunctionRemap *remap = (*sii); - int pn = 0; - if (remap->_has_this) { - // Skip the "this" parameter. It's never coercible. - ++pn; - } - while (pn < (int)remap->_parameters.size()) { - CPPType *type = remap->_parameters[pn]._remap->get_new_type(); + bool coercion_allowed, bool report_errors, + ArgsType args_type, int return_flags, + bool check_exceptions, bool verify_const, + const string &first_pexpr) { - if (TypeManager::is_char_pointer(type)) { - } else if (TypeManager::is_wchar_pointer(type)) { - } else if (TypeManager::is_pointer_to_PyObject(type)) { - } else if (TypeManager::is_pointer_to_Py_buffer(type)) { - } else if (TypeManager::is_pointer(type)) { - // This is a pointer to an object, so we - // might be able to coerce a parameter to it. - coercion_possible = true; - break; - } - ++pn; + if (remapsin.empty()) { + return; + } + + FunctionRemap *remap; + std::set::iterator sii; + + bool all_nonconst = false; + + if (verify_const) { + // Check if all of the remaps are non-const. If so, we only have to + // check the constness of the self pointer once, rather than per remap. + all_nonconst = true; + + for (sii = remapsin.begin(); sii != remapsin.end(); ++sii) { + remap = (*sii); + if (!remap->_has_this || remap->_const_method) { + all_nonconst = false; + break; + } + } + + if (all_nonconst) { + // Yes, they do. Check that the parameter has the required constness. + indent(out, indent_level) + << "if (!((Dtool_PyInstDef *)self)->_is_const) {\n"; + indent_level += 2; + verify_const = false; + } + } + + string first_param_name; + bool same_first_param = false; + + // If there's only one arg and all remaps have the same parameter name, we + // extract it from the dictionary, so we don't have to call ParseTupleAndKeywords. + if (first_pexpr.empty() && min_num_args == 1 && max_num_args == 1 && + args_type == AT_keyword_args) { + sii = remapsin.begin(); + remap = (*sii); + first_param_name = remap->_parameters[(int)remap->_has_this]._name; + same_first_param = true; + + for (++sii; sii != remapsin.end(); ++sii) { + remap = (*sii); + if (remap->_parameters[(int)remap->_has_this]._name != first_param_name) { + same_first_param = false; + break; } } } - if (coercion_possible) { - // These objects keep track of whether we have attempted automatic - // parameter coercion. - indent(out, indent_level) - << "{\n"; - indent_level += 2; - indent(out, indent_level) - << "PyObject *coerced = NULL;\n"; - indent(out, indent_level) - << "PyObject **coerced_ptr = NULL;\n"; - indent(out, indent_level) - << "bool report_errors = false;\n"; - indent(out, indent_level) - << "while (true) {\n"; - indent_level += 2; - } else { - out << "\n"; + if (same_first_param) { + // Yes, they all have the same argument name (or there is only one remap). + // Extract it from the dict so we don't have to call ParseTupleAndKeywords. + indent(out, indent_level) << "PyObject *arg = NULL;\n"; + indent(out, indent_level) << "if (PyTuple_GET_SIZE(args) == 1) {\n"; + indent(out, indent_level) << " arg = PyTuple_GET_ITEM(args, 0);\n"; + indent(out, indent_level) << "} else if (kwds != NULL) {\n"; + indent(out, indent_level) << " arg = PyDict_GetItemString(kwds, \"" << first_param_name << "\");\n"; + indent(out, indent_level) << "}\n"; + if (report_errors) { + indent(out, indent_level) << "if (arg == (PyObject *)NULL) {\n"; + error_raise_return(out, indent_level + 2, return_flags, "TypeError", + "Required argument '" + first_param_name + "' (pos 1) not found"); + indent(out, indent_level) << "}\n"; + } else { + indent(out, indent_level) << "if (arg != (PyObject *)NULL) {\n"; + indent_level += 2; + } + args_type = AT_single_arg; } if (remapsin.size() > 1) { @@ -2889,11 +4027,75 @@ write_function_forset(ostream &out, InterfaceMaker::Object *obj, // least-specific, then try them one at a time. std::vector remaps (remapsin.begin(), remapsin.end()); std::sort(remaps.begin(), remaps.end(), RemapCompareLess); + std::vector::const_iterator sii; - std::vector::iterator sii; - for (sii = remaps.begin(); sii != remaps.end(); sii ++) { - FunctionRemap *remap = (*sii); - if (remap->_has_this && !remap->_const_method) { + // Check if all of them have an InternalName pointer as first + // parameter. This is a dirty hack, of course, to work around an + // awkward overload resolution problem in NodePath::set_shader_input() + // (while perhaps also improving its performance). If I had more time + // I'd create a better solution. + bool first_internalname = false; + string first_pexpr2(first_pexpr); + if (first_pexpr.empty() && args_type != AT_no_args) { + first_internalname = true; + + for (sii = remaps.begin(); sii != remaps.end(); ++sii) { + remap = (*sii); + if (remap->_parameters.size() > (int)remap->_has_this) { + ParameterRemap *param = remap->_parameters[(int)remap->_has_this]._remap; + string param_name = param->get_orig_type()->get_local_name(&parser); + + if (param_name != "CPT_InternalName" && + param_name != "InternalName const *" && + param_name != "InternalName *") { + // Aw. + first_internalname = false; + break; + } + } else { + first_internalname = false; + break; + } + } + if (first_internalname) { + // Yeah, all remaps have a first InternalName parameter, so process + // that and remove it from the args tuple. + if (args_type == AT_single_arg) { + // Bit of a weird case, but whatever. + indent(out, indent_level) << "PyObject *name_obj = arg;\n"; + args_type = AT_no_args; + } else if (min_num_args == 2 && max_num_args == 2) { + indent(out, indent_level) << "PyObject *name_obj = PyTuple_GET_ITEM(args, 0);\n"; + indent(out, indent_level) << "PyObject *arg = PyTuple_GET_ITEM(args, 1);\n"; + args_type = AT_single_arg; + } else { + indent(out, indent_level) << "PyObject *name_obj = PyTuple_GET_ITEM(args, 0);\n"; + indent(out, indent_level) << "args = PyTuple_GetSlice(args, 1, PyTuple_GET_SIZE(args));\n"; + return_flags |= RF_decref_args; + } + indent(out, indent_level) << "PT(InternalName) name;\n"; + indent(out, indent_level) << "if (Dtool_Coerce_InternalName(name_obj, name)) {\n"; + indent_level += 2; + first_pexpr2 = "name"; + } + } + + int num_coercion_possible = 0; + sii = remaps.begin(); + while (sii != remaps.end()) { + remap = *(sii++); + + if (coercion_allowed && is_remap_coercion_possible(remap)) { + if (++num_coercion_possible == 1 && sii == remaps.end()) { + // This is the last remap, and it happens to be the only one + // with coercion possible. So we might as well just break off + // now, and let this case be handled by the coercion loop, below. + // BUG: this remap doesn't get listed in expected_params. + break; + } + } + + if (verify_const && (remap->_has_this && !remap->_const_method)) { // If it's a non-const method, we only allow a // non-const this. indent(out, indent_level) @@ -2903,265 +4105,415 @@ write_function_forset(ostream &out, InterfaceMaker::Object *obj, << "{\n"; } - indent(out, indent_level) << "// -2 "; - remap->write_orig_prototype(out, 0); out << "\n"; + indent(out, indent_level) << " // -2 "; + remap->write_orig_prototype(out, 0, false, (max_num_args - min_num_args)); + out << "\n"; - write_function_instance(out, obj, func, remap, expected_params, indent_level + 2, is_inplace, - coercion_possible, coercion_attempted, args_type, return_int, first_pexpr); + // NB. We don't pass on report_errors here because we want + // it to silently drop down to the next overload. + + write_function_instance(out, remap, min_num_args, max_num_args, + expected_params, indent_level + 2, + false, false, args_type, return_flags, + check_exceptions, first_pexpr2); - indent(out, indent_level + 2) << "PyErr_Clear();\n"; indent(out, indent_level) << "}\n\n"; } + + // Go through one more time, but allow coercion this time. + if (coercion_allowed) { + for (sii = remaps.begin(); sii != remaps.end(); sii ++) { + remap = (*sii); + if (!is_remap_coercion_possible(remap)) { + continue; + } + + if (verify_const && (remap->_has_this && !remap->_const_method)) { + indent(out, indent_level) + << "if (!((Dtool_PyInstDef *)self)->_is_const) {\n"; + } else { + indent(out, indent_level) + << "{\n"; + } + + indent(out, indent_level) << " // -2 "; + remap->write_orig_prototype(out, 0, false, (max_num_args - min_num_args)); + out << "\n"; + + string ignore_expected_params; + write_function_instance(out, remap, min_num_args, max_num_args, + ignore_expected_params, indent_level + 2, + true, false, args_type, return_flags, + check_exceptions, first_pexpr2); + + indent(out, indent_level) << "}\n\n"; + } + } + + if (first_internalname) { + indent_level -= 2; + if (report_errors) { + indent(out, indent_level) << "} else {\n"; + + string class_name = remap->_cpptype->get_simple_name(); + ostringstream msg; + msg << classNameFromCppName(class_name, false) << "." + << methodNameFromCppName(remap, class_name, false) + << "() first argument must be str or InternalName"; + + error_raise_return(out, indent_level + 2, return_flags, + "TypeError", msg.str()); + } + indent(out, indent_level) << "}\n"; + } } else { // There is only one possible overload with this number of // parameters. Just call it. - std::set::iterator sii; - for (sii = remapsin.begin(); sii != remapsin.end(); sii ++) { - FunctionRemap *remap = (*sii); - if (remap->_has_this && !remap->_const_method) { - // If it's a non-const method, we only allow a - // non-const this. - indent(out, indent_level) - << "if (!((Dtool_PyInstDef *)self)->_is_const) {\n"; - indent_level += 2; - } + sii = remapsin.begin(); - indent(out, indent_level) - << "// 1-" ; - remap->write_orig_prototype(out, 0); - out << "\n" ; - write_function_instance(out, obj, func, remap, expected_params, indent_level, is_inplace, - coercion_possible, coercion_attempted, args_type, return_int, first_pexpr); + remap = (*sii); + indent(out, indent_level) + << "// 1-" ; + remap->write_orig_prototype(out, 0, false, (max_num_args - min_num_args)); + out << "\n"; - if (remap->_has_this && !remap->_const_method) { - indent(out, indent_level - 2) - << "} else {\n"; - indent(out, indent_level) - << "PyErr_SetString(PyExc_TypeError,\n"; - string class_name = remap->_cpptype->get_simple_name(); - indent(out, indent_level) - << " \"Cannot call " - << classNameFromCppName(class_name, false) - << "." << methodNameFromCppName(func, class_name, false) - << "() on a const object.\");\n"; - - if (return_int) { - indent(out, indent_level) - << "return -1;\n"; - } else { - indent(out, indent_level) - << "return (PyObject *) NULL;\n"; - } - - indent_level -= 2; - indent(out, indent_level) - << "}\n\n"; - } else { - out << "\n"; - } - } + write_function_instance(out, remap, min_num_args, max_num_args, + expected_params, indent_level, + coercion_allowed, report_errors, + args_type, return_flags, + check_exceptions, first_pexpr); } - // Now we've tried all of the possible overloads, and had no luck. - - if (coercion_possible) { - // Try again, this time with coercion enabled. - indent(out, indent_level) - << "if (coerced_ptr == NULL && !report_errors) {\n"; - indent(out, indent_level + 2) - << "coerced_ptr = &coerced;\n"; - indent(out, indent_level + 2) - << "continue;\n"; - indent(out, indent_level) - << "}\n"; - - // No dice. Go back one more time, and this time get the error - // message. - indent(out, indent_level) - << "if (!report_errors) {\n"; - indent(out, indent_level + 2) - << "report_errors = true;\n"; - indent(out, indent_level + 2) - << "continue;\n"; - indent(out, indent_level) - << "}\n"; - - // We've been through three times. We're done. - indent(out, indent_level) - << "break;\n"; - + // Close the brace we opened earlier. + if (same_first_param && !report_errors) { indent_level -= 2; - indent(out, indent_level) - << "}\n"; + indent(out, indent_level) << "}\n"; + } - indent(out, indent_level) - << "Py_XDECREF(coerced);\n"; + // If we did a const check earlier, and we were asked to report errors, + // write out an else case raising an exception. + if (all_nonconst) { + if (report_errors) { + indent(out, indent_level - 2) + << "} else {\n"; + + string class_name = remap->_cpptype->get_simple_name(); + ostringstream msg; + msg << "Cannot call " + << classNameFromCppName(class_name, false) + << "." << methodNameFromCppName(remap, class_name, false) + << "() on a const object."; + + out << "#ifdef NDEBUG\n"; + error_raise_return(out, indent_level, return_flags, "TypeError", + "non-const method called on const object"); + out << "#else\n"; + error_raise_return(out, indent_level, return_flags, "TypeError", msg.str()); + out << "#endif\n"; + } indent_level -= 2; - indent(out, indent_level) - << "}\n"; + indent(out, indent_level) << "}\n"; } } //////////////////////////////////////////////////////////////////// // Function: InterfaceMakerPythonNative::write_function_instance // Access: Private -// Description: Writes out the particular function that handles a -// single instance of an overloaded function. +// Description: Writes out the code to handle a a single instance +// of an overloaded function. This will convert all +// of the arguments from PyObject* to the appropriate +// C++ type, call the C++ function, possibly check +// for errors, and construct a Python wrapper for the +// return value. +// +// return_flags indicates which value should be +// returned from the wrapper function and what should +// be returned on error. +// +// If coercion_possible is true, it will attempt +// to convert arguments to the appropriate parameter +// type using the appropriate Dtool_Coerce function. +// +// If report_errors is true, it will print an error +// and exit when one has occurred, instead of falling +// back to the next overload. This should be done +// if it is the only overload. +// +// If check_exceptions is false, it will not check +// if the function raised an exception, except if +// it took PyObject* arguments. This should NEVER +// be false for C++ functions that call Python code, +// since that would block a meaningful exception +// like SystemExit or KeyboardInterrupt. +// +// If first_pexpr is not empty, it represents the +// preconverted value of the first parameter. This +// is a special-case hack for one of the slot functions. //////////////////////////////////////////////////////////////////// void InterfaceMakerPythonNative:: -write_function_instance(ostream &out, InterfaceMaker::Object *obj, - InterfaceMaker::Function *func, - FunctionRemap *remap, string &expected_params, - int indent_level, bool is_inplace, - bool coercion_possible, bool &coercion_attempted, - ArgsType args_type, bool return_int, +write_function_instance(ostream &out, FunctionRemap *remap, + int min_num_args, int max_num_args, + string &expected_params, int indent_level, + bool coercion_possible, bool report_errors, + ArgsType args_type, int return_flags, + bool check_exceptions, const string &first_pexpr) { string format_specifiers; - std::string keyword_list; + string keyword_list; string parameter_list; string container; + string type_check; vector_string pexprs; - string extra_convert; - string extra_param_check; - string extra_cleanup; + LineStream extra_convert; + ostringstream extra_param_check; + LineStream extra_cleanup; + int min_version = 0; + + // This will be set if the function itself is suspected of possibly + // raising a TypeError. + bool may_raise_typeerror = false; + + // This will be set to true if one of the things we're about to do + // *might* raise a TypeError that we may have to clear. + bool clear_error = false; bool is_constructor = (remap->_type == FunctionRemap::T_constructor); - if (is_constructor && (remap->_flags & FunctionRemap::F_explicit_self) != 0) { - // If we'll be passing "self" to the constructor, we need to - // pre-initialize it here. Unfortunately, we can't pre-load the - // "this" pointer, but the constructor itself can do this. - - indent(out, indent_level) - << "// Pre-initialize self for the constructor\n"; - - CPPType *orig_type = remap->_return_type->get_orig_type(); - TypeIndex type_index = builder.get_type(TypeManager::unwrap(TypeManager::resolve_type(orig_type)), false); - InterrogateDatabase *idb = InterrogateDatabase::get_ptr(); - const InterrogateType &itype = idb->get_type(type_index); - indent(out, indent_level) - << "DTool_PyInit_Finalize(self, NULL, &" - << CLASS_PREFIX << make_safe_name(itype.get_scoped_name()) - << ", false, false);\n"; - } + InterrogateDatabase *idb = InterrogateDatabase::get_ptr(); // Make one pass through the parameter list. We will output a // one-line temporary variable definition for each parameter, while // simultaneously building the ParseTuple() function call and also // the parameter expression list for call_function(). - expected_params += methodNameFromCppName(func, "", false); + expected_params += methodNameFromCppName(remap, "", false); expected_params += "("; - int num_params = 0; - bool only_pyobjects = true; - //bool check_exceptions = false; + int num_params = max_num_args; + if (remap->_has_this) { + num_params += 1; + } + if (num_params > remap->_parameters.size()) { + // Limit to how many parameters this remap actually has. + num_params = remap->_parameters.size(); + max_num_args = num_params; + if (remap->_has_this) { + --max_num_args; + } + } + nassertv(num_params <= remap->_parameters.size()); - int pn; - for (pn = 0; pn < (int)remap->_parameters.size(); ++pn) { + bool only_pyobjects = true; + + int pn = 0; + + if (remap->_has_this) { + // The first parameter is the 'this' parameter. + string expected_class_name = classNameFromCppName(remap->_cpptype->get_simple_name(), false); + if (remap->_const_method) { + expected_params += expected_class_name + " self"; + string class_name = remap->_cpptype->get_local_name(&parser); + container = "(const " + class_name + "*)local_this"; + } else { + expected_params += "const " + expected_class_name + " self"; + container = "local_this"; + } + pexprs.push_back(container); + ++pn; + } + + if (!first_pexpr.empty()) { + if (pn >= num_params) { + // first_pexpr was passed even though the function takes no arguments. + nassert_raise("pn < num_params"); + } else { + // The first actual argument was already converted. + if (pn > 0) { + expected_params += ", "; + } + expected_params += first_pexpr; + pexprs.push_back(first_pexpr); + ++pn; + } + } + + // Now convert (the rest of the) actual arguments, one by one. + for (; pn < num_params; ++pn) { if (pn > 0) { expected_params += ", "; } - if (((remap->_has_this && pn == 1) || - (!remap->_has_this && pn == 0)) && !first_pexpr.empty()) { - // The first param was already converted. - pexprs.push_back(first_pexpr); - continue; + bool is_optional = false; + // Has this remap been selected to consider optional arguments for + // this parameter? We can do that by adding a vertical bar to the + // PyArg_ParseTuple format string, coupled with some extra logic + // in the argument handling, below. + if (remap->_has_this && !is_constructor) { + if (pn > min_num_args) { + is_optional = true; + if ((pn - 1) == min_num_args) { + format_specifiers += "|"; + } + } + } else { + if (pn >= min_num_args) { + is_optional = true; + if (pn == min_num_args) { + format_specifiers += "|"; + } + } } - CPPType *orig_type = remap->_parameters[pn]._remap->get_orig_type(); - CPPType *type = remap->_parameters[pn]._remap->get_new_type(); + ParameterRemap *param = remap->_parameters[pn]._remap; + CPPType *orig_type = param->get_orig_type(); + CPPType *type = param->get_new_type(); + CPPExpression *default_value = param->get_default_value(); string param_name = remap->get_parameter_name(pn); // This is the string to convert our local variable to the // appropriate C++ type. Normally this is just a cast. string pexpr_string = - "(" + type->get_local_name(&parser) + ")" + param_name; + "(" + orig_type->get_local_name(&parser) + ")" + param_name; - if (!remap->_has_this || pn != 0) { - keyword_list += "(char *)\"" + remap->_parameters[pn]._name + "\", "; + string default_expr; + + if (is_optional) { + // If this is an optional argument, PyArg_ParseTuple will leave + // the variable unchanged if it has been omitted, so we have to + // initialize it to the desired default expression. Format it. + ostringstream default_expr_str; + default_expr_str << " = "; + default_value->output(default_expr_str, 0, &parser, false); + default_expr = default_expr_str.str(); + + // We should only ever have to consider optional arguments for + // functions taking a variable number of arguments. + nassertv(args_type == AT_varargs || args_type == AT_keyword_args); } - if (remap->_parameters[pn]._remap->new_type_is_atomic_string()) { + string reported_name = remap->_parameters[pn]._name; + keyword_list += "\"" + reported_name + "\", "; + + if (param->new_type_is_atomic_string()) { + if (TypeManager::is_char_pointer(orig_type)) { - indent(out, indent_level) << "char *" << param_name << ";\n"; - format_specifiers += "s"; + indent(out, indent_level) << "char "; + if (TypeManager::is_const_char_pointer(orig_type)) { + out << "const "; + } + out << "*" << param_name << default_expr << ";\n"; + format_specifiers += "z"; parameter_list += ", &" + param_name; expected_params += "str"; } else if (TypeManager::is_wchar_pointer(orig_type)) { - out << "#if PY_MAJOR_VERSION >= 3\n"; - indent(out, indent_level) << "PyObject *" << param_name << ";\n"; - out << "#else\n"; indent(out, indent_level) << "PyUnicodeObject *" << param_name << ";\n"; - out << "#endif\n"; - format_specifiers += "U"; parameter_list += ", &" + param_name; - extra_convert += " Py_ssize_t " + param_name + "_len = PyUnicode_GetSize((PyObject *)" + param_name + ");" - " wchar_t *" + param_name + "_str = (wchar_t *)alloca(sizeof(wchar_t) * (" + param_name + "_len + 1));" - " PyUnicode_AsWideChar(" + param_name + ", " + param_name + "_str, " + param_name + "_len);" - " " + param_name + "_str[" + param_name + "_len] = 0;"; + extra_convert + << "#if PY_VERSION_HEX >= 0x03030000\n" + << "wchar_t *" << param_name << "_str = PyUnicode_AsWideCharString((PyObject *)" << param_name << ", NULL);\n" + << "#else" + << "Py_ssize_t " << param_name << "_len = PyUnicode_GET_SIZE(" << param_name << ");\n" + << "wchar_t *" << param_name << "_str = (wchar_t *)alloca(sizeof(wchar_t) * (" + param_name + "_len + 1));\n" + << "PyUnicode_AsWideChar(" << param_name << ", " << param_name << "_str, " << param_name << "_len);\n" + << param_name << "_str[" << param_name << "_len] = 0;\n" + << "#endif\n"; pexpr_string = param_name + "_str"; + + extra_cleanup + << "#if PY_VERSION_HEX >= 0x03030000\n" + << "PyMem_Free(" << param_name << "_str);\n" + << "#endif\n"; + expected_params += "unicode"; } else if (TypeManager::is_wstring(orig_type)) { - out << "#if PY_MAJOR_VERSION >= 3\n"; - indent(out, indent_level) << "PyObject *" << param_name << ";\n"; - out << "#else\n"; indent(out, indent_level) << "PyUnicodeObject *" << param_name << ";\n"; - out << "#endif\n"; format_specifiers += "U"; parameter_list += ", &" + param_name; - extra_convert += " Py_ssize_t " + param_name + "_len = PyUnicode_GetSize((PyObject *)" + param_name + ");" - " wchar_t *" + param_name + "_str = (wchar_t *)alloca(sizeof(wchar_t) * " + param_name + "_len);" - " PyUnicode_AsWideChar(" + param_name + ", " + param_name + "_str, " + param_name + "_len);"; + extra_convert + << "#if PY_VERSION_HEX >= 0x03030000\n" + << "Py_ssize_t " << param_name << "_len;\n" + << "wchar_t *" << param_name << "_str = PyUnicode_AsWideCharString((PyObject *)" + << param_name << ", &" << param_name << "_len);\n" + << "#else\n" + << "Py_ssize_t " << param_name << "_len = PyUnicode_GET_SIZE(" << param_name << ");\n" + << "wchar_t *" << param_name << "_str = (wchar_t *)alloca(sizeof(wchar_t) * (" + param_name + "_len + 1));\n" + << "PyUnicode_AsWideChar(" << param_name << ", " << param_name << "_str, " << param_name << "_len);\n" + << "#endif\n"; pexpr_string = "std::wstring(" + - param_name + "_str, " + - param_name + "_len)"; + param_name + "_str, " + param_name + "_len)"; + + extra_cleanup + << "#if PY_VERSION_HEX >= 0x03030000\n" + << "PyMem_Free(" << param_name << "_str);\n" + << "#endif\n"; expected_params += "unicode"; } else if (TypeManager::is_const_ptr_to_basic_string_wchar(orig_type)) { - out << "#if PY_MAJOR_VERSION >= 3\n"; - indent(out, indent_level) << "PyObject *" << param_name << ";\n"; - out << "#else\n"; indent(out, indent_level) << "PyUnicodeObject *" << param_name << ";\n"; - out << "#endif\n"; format_specifiers += "U"; parameter_list += ", &" + param_name; - extra_convert += " Py_ssize_t " + param_name + "_len = PyUnicode_GetSize((PyObject *)" + param_name + ");" - " wchar_t *" + param_name + "_str = (wchar_t *)alloca(sizeof(wchar_t) * " + param_name + "_len);" - " PyUnicode_AsWideChar(" + param_name + ", " + param_name + "_str, " + param_name + "_len);"; + extra_convert + << "#if PY_VERSION_HEX >= 0x03030000\n" + << "Py_ssize_t " << param_name << "_len;\n" + << "wchar_t *" << param_name << "_str = PyUnicode_AsWideCharString((PyObject *)" + << param_name << ", &" << param_name << "_len);\n" + << "#else\n" + << "Py_ssize_t " << param_name << "_len = PyUnicode_GET_SIZE(" << param_name << ");\n" + << "wchar_t *" << param_name << "_str = (wchar_t *)alloca(sizeof(wchar_t) * (" + param_name + "_len + 1));\n" + << "PyUnicode_AsWideChar(" << param_name << ", " << param_name << "_str, " << param_name << "_len);\n" + << "#endif\n"; pexpr_string = "&std::wstring(" + - param_name + "_str, " + - param_name + "_len)"; + param_name + "_str, " + param_name + "_len)"; + + extra_cleanup + << "#if PY_VERSION_HEX >= 0x03030000\n" + << "PyMem_Free(" << param_name << "_str);\n" + << "#endif\n"; expected_params += "unicode"; - } else { - indent(out, indent_level) << "char *" << param_name << "_str;\n"; - indent(out, indent_level) << "Py_ssize_t " << param_name << "_len;\n"; + } else { // A regular string. + if (is_optional) { + CPPExpression::Type expr_type = default_value->_type; + if (expr_type == CPPExpression::T_default_construct) { + // The default string constructor yields an empty string. + indent(out, indent_level) << "const char *" << param_name << "_str = \"\";\n"; + indent(out, indent_level) << "Py_ssize_t " << param_name << "_len = 0;\n"; + } else { + // We only get here for string literals, so this should be fine + indent(out, indent_level) << "const char *" << param_name << "_str" + << default_expr << ";\n"; + indent(out, indent_level) << "Py_ssize_t " << param_name << "_len = " + << default_value->_str.size() << ";\n"; + } + } else { + indent(out, indent_level) << "char *" << param_name << "_str = NULL;\n"; + indent(out, indent_level) << "Py_ssize_t " << param_name << "_len;\n"; + } + if (args_type == AT_single_arg) { out << "#if PY_MAJOR_VERSION >= 3\n"; indent(out, indent_level) << param_name << "_str = PyUnicode_AsUTF8AndSize(arg, &" << param_name << "_len);\n"; - out << "#else\n"; + out << "#else\n"; // NB. PyString_AsStringAndSize also accepts a PyUnicode. indent(out, indent_level) << "if (PyString_AsStringAndSize(arg, &" << param_name << "_str, &" << param_name << "_len) == -1) {\n"; indent(out, indent_level + 2) << param_name << "_str = NULL;\n"; indent(out, indent_level) << "}\n"; out << "#endif\n"; - extra_param_check = " && " + param_name + "_str != NULL"; + extra_param_check << " && " << param_name << "_str != NULL"; } else { format_specifiers += "s#"; parameter_list += ", &" + param_name @@ -3170,149 +4522,308 @@ write_function_instance(ostream &out, InterfaceMaker::Object *obj, if (TypeManager::is_const_ptr_to_basic_string_char(orig_type)) { pexpr_string = "&std::string(" + - param_name + "_str, " + - param_name + "_len)"; + param_name + "_str, " + param_name + "_len)"; } else { pexpr_string = "std::string(" + - param_name + "_str, " + - param_name + "_len)"; + param_name + "_str, " + param_name + "_len)"; } expected_params += "str"; } - ++num_params; only_pyobjects = false; } else if (TypeManager::is_bool(type)) { if (args_type == AT_single_arg) { param_name = "arg"; } else { - indent(out, indent_level) << "PyObject *" << param_name << ";\n"; + indent(out, indent_level) << "PyObject *" << param_name; + if (is_optional) { + CPPExpression::Result res = default_value->evaluate(); + if (res._type != CPPExpression::RT_error) { + // It's a compile-time constant. Write Py_True or Py_False. + out << " = " << (res.as_boolean() ? "Py_True" : "Py_False"); + } else { + // Select Py_True or Py_False at runtime. + out << " = ("; + default_value->output(out, 0, &parser, false); + out << ") ? Py_True : Py_False"; + } + } + out << ";\n"; format_specifiers += "O"; parameter_list += ", &" + param_name; } pexpr_string = "(PyObject_IsTrue(" + param_name + ") != 0)"; expected_params += "bool"; - ++num_params; - } else if (TypeManager::is_unsigned_longlong(type)) { - if (args_type == AT_single_arg) { - param_name = "arg"; - } else { - indent(out, indent_level) << "PyObject *" << param_name << ";\n"; - format_specifiers += "O"; - parameter_list += ", &" + param_name; - } - extra_convert += "PyObject *" + param_name + "_long = PyNumber_Long(" + param_name + ");"; - extra_param_check += " && " + param_name + "_long != NULL"; - pexpr_string = "(" + type->get_local_name(&parser) + ")" + - "PyLong_AsUnsignedLongLong(" + param_name + "_long)"; - extra_cleanup += "Py_XDECREF(" + param_name + "_long);"; - expected_params += "unsigned long long"; - ++num_params; + } else if (TypeManager::is_char(type)) { + indent(out, indent_level) << "char " << param_name << default_expr << ";\n"; - } else if (TypeManager::is_longlong(type)) { - if (args_type == AT_single_arg) { - param_name = "arg"; - } else { - indent(out, indent_level) << "PyObject *" << param_name << ";\n"; - format_specifiers += "O"; - parameter_list += ", &" + param_name; - } - extra_convert += "PyObject *" + param_name + "_long = PyNumber_Long(" + param_name + ");"; - extra_param_check += " && " + param_name + "_long != NULL"; - pexpr_string = "(" + type->get_local_name(&parser) + ")" + - "PyLong_AsLongLong(" + param_name + "_long)"; - extra_cleanup += "Py_XDECREF(" + param_name + "_long);"; - expected_params += "long long"; - ++num_params; + format_specifiers += "c"; + parameter_list += ", &" + param_name; - } else if (TypeManager::is_unsigned_integer(type)) { - if (args_type == AT_single_arg) { - param_name = "arg"; - } else { - indent(out, indent_level) << "PyObject *" << param_name << ";\n"; - format_specifiers += "O"; - parameter_list += ", &" + param_name; - } - extra_convert += "PyObject *" + param_name + "_long = PyNumber_Long(" + param_name + ");"; - extra_param_check += " && " + param_name + "_long != NULL"; - pexpr_string = "(" + type->get_local_name(&parser) + ")" + - "PyLong_AsUnsignedLong(" + param_name + "_long)"; - extra_cleanup += "Py_XDECREF(" + param_name + "_long);"; - expected_params += "unsigned int"; - ++num_params; + //extra_param_check << " && isascii(" << param_name << ")"; + pexpr_string = "(char) " + param_name; + expected_params += "char"; + only_pyobjects = false; - } else if (TypeManager::is_integer(type)) { - indent(out, indent_level) << "int " << param_name << ";\n"; - format_specifiers += "i"; + } else if (TypeManager::is_wchar(type)) { + indent(out, indent_level) << "PyUnicodeObject *" << param_name << ";\n"; + format_specifiers += "U"; + parameter_list += ", &" + param_name; + + // We tell it to copy 2 characters, but make sure it only + // copied one, as a trick to check for the proper length in one go. + extra_convert << "wchar_t " << param_name << "_chars[2];\n"; + extra_param_check << " && PyUnicode_AsWideChar(" << param_name << ", " << param_name << "_chars, 2) == 1"; + + pexpr_string = param_name + "_chars[0]"; + expected_params += "unicode char"; + only_pyobjects = false; + clear_error = true; + + } else if (TypeManager::is_ssize(type)) { + indent(out, indent_level) << "Py_ssize_t " << param_name << default_expr << ";\n"; + format_specifiers += "n"; parameter_list += ", &" + param_name; expected_params += "int"; only_pyobjects = false; - ++num_params; + + } else if (TypeManager::is_size(type)) { + // It certainly isn't the exact same thing as size_t, but Py_ssize_t + // should at least be the same size. The problem with mapping this + // to unsigned int is that that doesn't work well on 64-bit systems, + // on which size_t is a 64-bit integer. + indent(out, indent_level) << "Py_ssize_t " << param_name << default_expr << ";\n"; + format_specifiers += "n"; + parameter_list += ", &" + param_name; + expected_params += "int"; + only_pyobjects = false; + + extra_convert + << "#ifndef NDEBUG\n" + << "if (" << param_name << " < 0) {\n"; + + error_raise_return(extra_convert, 2, return_flags, "OverflowError", + "can't convert negative value %zd to size_t", + param_name); + extra_convert + << "}\n" + << "#endif\n"; + + } else if (TypeManager::is_longlong(type)) { + // It's not trivial to do overflow checking for a long long, so we + // simply don't do it. + if (TypeManager::is_unsigned_longlong(type)) { + indent(out, indent_level) << "unsigned PY_LONG_LONG " << param_name << default_expr << ";\n"; + format_specifiers += "K"; + } else { + indent(out, indent_level) << "PY_LONG_LONG " << param_name << default_expr << ";\n"; + format_specifiers += "L"; + } + parameter_list += ", &" + param_name; + expected_params += "long"; + only_pyobjects = false; + + } else if (TypeManager::is_unsigned_short(type)) { + if (args_type == AT_single_arg) { + // This is defined to PyLong_Check for Python 3 by py_panda.h. + type_check = "PyInt_Check(arg)"; + extra_convert + << "long " << param_name << " = PyInt_AS_LONG(arg);\n"; + + pexpr_string = "(" + type->get_local_name(&parser) + ")" + param_name; + } else { + indent(out, indent_level) << "long " << param_name << default_expr << ";\n"; + format_specifiers += "l"; + parameter_list += ", &" + param_name; + } + + // The "H" format code, unlike "h", does not do overflow checking, so + // we have to do it ourselves (except in release builds). + extra_convert + << "#ifndef NDEBUG\n" + << "if (" << param_name << " < 0 || " << param_name << " > USHRT_MAX) {\n"; + + error_raise_return(extra_convert, 2, return_flags, "OverflowError", + "value %ld out of range for unsigned short integer", + param_name); + extra_convert + << "}\n" + << "#endif\n"; + + expected_params += "int"; + only_pyobjects = false; + + } else if (TypeManager::is_short(type)) { + if (args_type == AT_single_arg) { + // This is defined to PyLong_Check for Python 3 by py_panda.h. + type_check = "PyInt_Check(arg)"; + + // Perform overflow checking in debug builds. + extra_convert + << "long arg_val = PyInt_AS_LONG(arg);\n" + << "#ifndef NDEBUG\n" + << "if (arg_val < SHRT_MIN || arg_val > SHRT_MAX) {\n"; + + error_raise_return(extra_convert, 2, return_flags, "OverflowError", + "value %ld out of range for signed short integer", + "arg_val"); + extra_convert + << "}\n" + << "#endif\n"; + + pexpr_string = "(" + type->get_local_name(&parser) + ")arg_val"; + + } else { + indent(out, indent_level) << "short " << param_name << default_expr << ";\n"; + format_specifiers += "h"; + parameter_list += ", &" + param_name; + } + expected_params += "int"; + only_pyobjects = false; + + } else if (TypeManager::is_unsigned_integer(type)) { + if (args_type == AT_single_arg) { + // Windows has 32-bit longs, and Python 2 stores a C long for PyInt + // internally, so a PyInt wouldn't cover the whole range; that's why + // we have to accept PyLong as well here. + type_check = "PyLongOrInt_Check(arg)"; + extra_convert + << "unsigned long " << param_name << " = PyLong_AsUnsignedLong(arg);\n"; + pexpr_string = "(" + type->get_local_name(&parser) + ")" + param_name; + } else { + indent(out, indent_level) << "unsigned long " << param_name << default_expr << ";\n"; + format_specifiers += "k"; + parameter_list += ", &" + param_name; + } + + // The "I" format code, unlike "i", does not do overflow checking, so + // we have to do it ourselves (in debug builds). Note that Python 2 + // stores longs internally, for ints, so we don't do it for Python 2 on + // Windows, where longs are the same size as ints. + // BUG: does not catch negative values on Windows when going through + // the PyArg_ParseTuple case. + extra_convert + << "#if (SIZEOF_LONG > SIZEOF_INT) && !defined(NDEBUG)\n" + << "if (" << param_name << " > UINT_MAX) {\n"; + + error_raise_return(extra_convert, 2, return_flags, "OverflowError", + "value %lu out of range for unsigned integer", + param_name); + extra_convert + << "}\n" + << "#endif\n"; + + expected_params += "int"; + only_pyobjects = false; + + } else if (TypeManager::is_integer(type)) { + if (args_type == AT_single_arg) { + // This is defined to PyLong_Check for Python 3 by py_panda.h. + type_check = "PyInt_Check(arg)"; + + // Perform overflow checking in debug builds. Note that Python 2 + // stores longs internally, for ints, so we don't do it on Windows, + // where longs are the same size as ints. + extra_convert + << "long arg_val = PyInt_AS_LONG(arg);\n" + << "#if (SIZEOF_LONG > SIZEOF_INT) && !defined(NDEBUG)\n" + << "if (arg_val < INT_MIN || arg_val > INT_MAX) {\n"; + + error_raise_return(extra_convert, 2, return_flags, "OverflowError", + "value %ld out of range for signed integer", + "arg_val"); + extra_convert + << "}\n" + << "#endif\n"; + + pexpr_string = "(" + type->get_local_name(&parser) + ")arg_val"; + + } else { + indent(out, indent_level) << "int " << param_name << default_expr << ";\n"; + format_specifiers += "i"; + parameter_list += ", &" + param_name; + } + expected_params += "int"; + only_pyobjects = false; } else if (TypeManager::is_double(type)) { if (args_type == AT_single_arg) { pexpr_string = "PyFloat_AsDouble(arg)"; - extra_param_check += " && PyNumber_Check(arg)"; + type_check = "PyNumber_Check(arg)"; } else { - indent(out, indent_level) << "double " << param_name << ";\n"; + indent(out, indent_level) << "double " << param_name << default_expr << ";\n"; format_specifiers += "d"; parameter_list += ", &" + param_name; } expected_params += "double"; only_pyobjects = false; - ++num_params; } else if (TypeManager::is_float(type)) { if (args_type == AT_single_arg) { - pexpr_string = "(float) PyFloat_AsDouble(arg)"; - extra_param_check += " && PyNumber_Check(arg)"; + pexpr_string = "(" + type->get_local_name(&parser) + ")PyFloat_AsDouble(arg)"; + type_check = "PyNumber_Check(arg)"; } else { - indent(out, indent_level) << "float " << param_name << ";\n"; + indent(out, indent_level) << "float " << param_name << default_expr << ";\n"; format_specifiers += "f"; parameter_list += ", &" + param_name; } expected_params += "float"; only_pyobjects = false; - ++num_params; - } else if (TypeManager::is_char_pointer(type)) { - indent(out, indent_level) << "char *" << param_name << ";\n"; - format_specifiers += "s"; + } else if (TypeManager::is_const_char_pointer(type)) { + indent(out, indent_level) << "const char *" << param_name << default_expr << ";\n"; + format_specifiers += "z"; parameter_list += ", &" + param_name; - expected_params += "string"; + expected_params += "buffer"; only_pyobjects = false; - ++num_params; + + } else if (TypeManager::is_pointer_to_PyTypeObject(type)) { + if (args_type == AT_single_arg) { + param_name = "arg"; + } else { + indent(out, indent_level) << "PyObject *" << param_name << default_expr << ";\n"; + format_specifiers += "O"; + parameter_list += ", &" + param_name; + pexpr_string = param_name; + } + extra_param_check << " && PyType_Check(" << param_name << ")"; + pexpr_string = "(PyTypeObject *)" + param_name; + expected_params += "type"; + + // It's reasonable to assume that a function taking a PyTypeObject + // might also throw a TypeError if the type is incorrect. + may_raise_typeerror = true; } else if (TypeManager::is_pointer_to_PyStringObject(type)) { if (args_type == AT_single_arg) { // This is a single-arg function, so there's no need // to convert anything. param_name = "arg"; - extra_param_check += " && PyString_Check(arg)"; + type_check = "PyString_Check(arg)"; + pexpr_string = "(PyStringObject *)" + param_name; } else { - indent(out, indent_level) << "PyStringObject *" << param_name << ";\n"; + indent(out, indent_level) << "PyStringObject *" << param_name << default_expr << ";\n"; format_specifiers += "S"; parameter_list += ", &" + param_name; + pexpr_string = param_name; } - pexpr_string = param_name; expected_params += "string"; - ++num_params; } else if (TypeManager::is_pointer_to_PyUnicodeObject(type)) { if (args_type == AT_single_arg) { // This is a single-arg function, so there's no need // to convert anything. param_name = "arg"; - extra_param_check += " && PyUnicode_Check(arg)"; + type_check = "PyUnicode_Check(arg)"; + pexpr_string = "(PyUnicodeObject *)" + param_name; } else { - indent(out, indent_level) << "PyUnicodeObject *" << param_name << ";\n"; + indent(out, indent_level) << "PyUnicodeObject *" << param_name << default_expr << ";\n"; format_specifiers += "U"; parameter_list += ", &" + param_name; + pexpr_string = param_name; } - pexpr_string = param_name; expected_params += "unicode"; - ++num_params; } else if (TypeManager::is_pointer_to_PyObject(type)) { if (args_type == AT_single_arg) { @@ -3320,19 +4831,20 @@ write_function_instance(ostream &out, InterfaceMaker::Object *obj, // to convert anything. param_name = "arg"; } else { - indent(out, indent_level) << "PyObject *" << param_name << ";\n"; + indent(out, indent_level) << "PyObject *" << param_name << default_expr << ";\n"; format_specifiers += "O"; parameter_list += ", &" + param_name; } pexpr_string = param_name; - expected_params += "any"; - ++num_params; + expected_params += "object"; // It's reasonable to assume that a function taking a PyObject // might also throw a TypeError if the type is incorrect. - //check_exceptions = true; + may_raise_typeerror = true; } else if (TypeManager::is_pointer_to_Py_buffer(type)) { + min_version = 0x02060000; // Only support this remap in version 2.6+. + if (args_type == AT_single_arg) { param_name = "arg"; } else { @@ -3340,82 +4852,293 @@ write_function_instance(ostream &out, InterfaceMaker::Object *obj, format_specifiers += "O"; parameter_list += ", &" + param_name; } - extra_convert += "PyObject *" + param_name + "_buffer = PyMemoryView_FromObject(" + param_name + ");"; - extra_param_check += " && " + param_name + "_buffer != NULL"; - pexpr_string = "PyMemoryView_GET_BUFFER(" + param_name + "_buffer)"; - extra_cleanup += "Py_XDECREF(" + param_name + "_buffer);"; - expected_params += "memoryview"; - ++num_params; - //check_exceptions = true; + indent(out, indent_level) << "Py_buffer " << param_name << "_view;\n"; + + extra_param_check << " && PyObject_GetBuffer(" + << param_name << ", &" + << param_name << "_view, PyBUF_FULL) == 0"; + pexpr_string = "&" + param_name + "_view"; + extra_cleanup << "PyBuffer_Release(&" << param_name << "_view);\n"; + expected_params += "buffer"; + may_raise_typeerror = true; + clear_error = true; + + } else if (TypeManager::is_pointer_to_simple(type)) { + if (args_type == AT_single_arg) { + param_name = "arg"; + } else { + indent(out, indent_level) << "PyObject *" << param_name << ";\n"; + format_specifiers += "O"; + parameter_list += ", &" + param_name; + } + indent(out, indent_level) << "Py_buffer " << param_name << "_view;\n"; + + // Unravel the type to determine its properties. + int array_len = -1; + bool is_const = true; + CPPSimpleType *simple = NULL; + CPPType *unwrap = TypeManager::unwrap_const_reference(type); + if (unwrap != NULL) { + CPPArrayType *array_type = unwrap->as_array_type(); + CPPPointerType *pointer_type = unwrap->as_pointer_type(); + + if (array_type != NULL) { + if (array_type->_bounds != NULL) { + array_len = array_type->_bounds->evaluate().as_integer(); + } + unwrap = array_type->_element_type; + } else if (pointer_type != NULL) { + unwrap = pointer_type->_pointing_at; + } + + CPPConstType *const_type = unwrap->as_const_type(); + if (const_type != NULL) { + unwrap = const_type->_wrapped_around; + } else { + is_const = false; + } + + while (unwrap->get_subtype() == CPPDeclaration::ST_typedef) { + unwrap = unwrap->as_typedef_type()->_type; + } + simple = unwrap->as_simple_type(); + } + + // Determine the format, so we can check the type of the buffer we get. + char format_chr = 'B'; + + switch (simple->_type) { + case CPPSimpleType::T_char: + if (simple->_flags & CPPSimpleType::F_unsigned) { + format_chr = 'B'; + } else if (simple->_flags & CPPSimpleType::F_signed) { + format_chr = 'b'; + } else { + format_chr = 'c'; + } + break; + + case CPPSimpleType::T_int: + if (simple->_flags & CPPSimpleType::F_longlong) { + format_chr = 'q'; + } else if (simple->_flags & CPPSimpleType::F_long) { + format_chr = 'l'; + } else if (simple->_flags & CPPSimpleType::F_short) { + format_chr = 'h'; + } else { + format_chr = 'i'; + } + + if (simple->_flags & CPPSimpleType::F_unsigned) { + format_chr &= 0x5f; // Uppercase + } + break; + + case CPPSimpleType::T_float: + format_chr = 'f'; + break; + + case CPPSimpleType::T_double: + format_chr = 'd'; + break; + + default: + nout << "Warning: cannot determine buffer format string for type " + << type->get_local_name(&parser) + << " (simple type " << *simple << ")\n"; + extra_param_check << " && false"; + } + + const char *flags; + if (format_chr == 'B') { + if (is_const) { + flags = "PyBUF_SIMPLE"; + } else { + flags = "PyBUF_WRITABLE"; + } + } else if (is_const) { + flags = "PyBUF_FORMAT"; + } else { + flags = "PyBUF_FORMAT | PyBUF_WRITABLE"; + } + + extra_param_check << " && PyObject_GetBuffer(" << param_name << ", &" + << param_name << "_view, " << flags << ") == 0"; + + if (format_chr != 'B') { + extra_param_check + << " && " << param_name << "_view.format[0] == '" << format_chr << "'" + << " && " << param_name << "_view.format[1] == 0"; + } + if (array_len != -1) { + extra_param_check + << " && " << param_name << "_view.len == " << array_len; + } + + pexpr_string = "(" + simple->get_local_name(&parser) + " *)" + + param_name + "_view.buf"; + + extra_cleanup << "PyBuffer_Release(&" << param_name << "_view);\n"; + expected_params += "buffer"; + clear_error = true; } else if (TypeManager::is_pointer(type)) { CPPType *obj_type = TypeManager::unwrap(TypeManager::resolve_type(type)); bool const_ok = !TypeManager::is_non_const_pointer_or_ref(orig_type); - if (const_ok) { + if (TypeManager::is_const_pointer_or_ref(orig_type)) { expected_params += "const "; //} else { // expected_params += "non-const "; } - expected_params += classNameFromCppName(obj_type->get_simple_name(), false); + string expected_class_name = classNameFromCppName(obj_type->get_simple_name(), false); + expected_params += expected_class_name; - if (!remap->_has_this || pn != 0) { - if (args_type == AT_single_arg) { - param_name = "arg"; - } else { - indent(out, indent_level) << "PyObject *" << param_name << ";\n"; - format_specifiers += "O"; - parameter_list += ", &" + param_name; + if (args_type == AT_single_arg) { + param_name = "arg"; + } else { + indent(out, indent_level) << "PyObject *" << param_name; + if (is_optional) { + out << " = NULL"; } + out << ";\n"; + format_specifiers += "O"; + parameter_list += ", &" + param_name; + } - ++num_params; + string class_name = obj_type->get_local_name(&parser); - TypeIndex p_type_index = builder.get_type(obj_type, false); - InterrogateDatabase *idb = InterrogateDatabase::get_ptr(); - const InterrogateType &p_itype = idb->get_type(p_type_index); + // need to a forward scope for this class.. + if (!isExportThisRun(obj_type)) { + _external_imports.insert(obj_type); + } - bool is_copy_constructor = false; - if (is_constructor && remap->_parameters.size() == 1 && pn == 0) { - if (&p_itype == &obj->_itype) { - // If this is the only one parameter, and it's the same as - // the "this" type, this is a copy constructor. - is_copy_constructor = true; + string this_class_name; + string method_prefix; + if (remap->_cpptype) { + this_class_name = remap->_cpptype->get_simple_name(); + method_prefix = classNameFromCppName(this_class_name, false) + string("."); + } + + if (coercion_possible) { + if (has_coerce_constructor(obj_type->as_struct_type()) == 0) { + // Doesn't actually have a coerce constructor. + coercion_possible = false; + } + } + + if (coercion_possible) { + // Call the coercion function directly, which will try to + // extract the pointer directly before trying coercion. + string coerce_call; + + if (TypeManager::is_reference_count(obj_type)) { + // We use a PointerTo to handle the management here. It's cleaner + // that way. + if (TypeManager::is_const_pointer_to_anything(type)) { + extra_convert << 'C'; } + extra_convert + << "PT(" << class_name << ") " << param_name << "_this" + << default_expr << ";\n"; + + coerce_call = "Dtool_Coerce_" + make_safe_name(class_name) + + "(" + param_name + ", " + param_name + "_this)"; + + // Use move constructor when available for functions that take + // an actual PointerTo. This eliminates an unref()/ref() pair. + pexpr_string = "MOVE(" + param_name + "_this)"; + + } else { + // This is a bit less elegant: we use a bool to store whether + // we're supposed to clean up the reference afterward. + type->output_instance(extra_convert, param_name + "_this", &parser); + extra_convert + << default_expr << ";\n" + << "bool " << param_name << "_manage = false;\n"; + + coerce_call = "Dtool_Coerce_" + make_safe_name(class_name) + + "(" + param_name + ", " + param_name + "_this, " + param_name + "_manage)"; + + extra_cleanup + << "if (" << param_name << "_manage) {\n" + << " delete " << param_name << "_this;\n" + << "}\n"; + + pexpr_string = param_name + "_this"; } - //make_safe_name(itype.get_scoped_name()) - extra_convert += p_itype.get_scoped_name() + " *" + param_name + "_this = (" + p_itype.get_scoped_name()+" *)"; - // need to a forward scope for this class.. - if (!isExportThisRun(p_itype._cpptype)) { - _external_imports.insert(make_safe_name(p_itype.get_scoped_name())); - } + if (report_errors) { + // We were asked to report any errors. Let's do it. + if (is_optional) { + extra_convert << "if (" << param_name << " != NULL && !" << coerce_call << ") {\n"; + } else { + extra_convert << "if (!" << coerce_call << ") {\n"; + } - string class_name; - string method_prefix; - if (remap->_cpptype) { - class_name = remap->_cpptype->get_simple_name(); - method_prefix = classNameFromCppName(class_name, false) + string("."); - } + // Display error like: Class.func() argument 0 must be A, not B + if ((return_flags & ~RF_pyobject) == RF_err_null) { + // Dtool_Raise_ArgTypeError returns NULL already + extra_convert << " return "; + } else { + extra_convert << " "; + } + extra_convert + << "Dtool_Raise_ArgTypeError(" << param_name << ", " + << pn << ", \"" << method_prefix + << methodNameFromCppName(remap, this_class_name, false) + << "\", \"" << expected_class_name << "\");\n"; + + if ((return_flags & ~RF_pyobject) != RF_err_null) { + error_return(extra_convert, 2, return_flags); + } + extra_convert << "}\n"; + + } else if (is_optional) { + extra_param_check << " && (" << param_name << " == NULL || " << coerce_call << ")"; - ostringstream str; - str << "DTOOL_Call_GetPointerThisClass(" << param_name - << ", &Dtool_" << make_safe_name(p_itype.get_scoped_name()) - << ", " << pn << ", \"" - << method_prefix << methodNameFromCppName(func, class_name, false) - << "\", " << const_ok; - if (coercion_possible && !is_copy_constructor) { - // We never attempt to coerce a copy constructor parameter. - // That would lead to infinite recursion. - str << ", coerced_ptr, report_errors"; - coercion_attempted = true; } else { - str << ", NULL, true"; + extra_param_check << " && " << coerce_call; } - str << ");\n"; - extra_convert += str.str(); - extra_param_check += " && " + param_name + "_this != NULL"; - pexpr_string = param_name + "_this"; + } else { + type->output_instance(extra_convert, param_name + "_this", &parser); + if (is_optional) { + extra_convert + << default_expr << ";\n" + << "if (" << param_name << " != (PyObject *)NULL) {\n" + << " " << param_name << "_this"; + } + if (const_ok && !report_errors) { + // This function does the same thing in this case and is slightly + // simpler. But maybe we should just reorganize these functions + // entirely? + extra_convert << ";\n"; + if (is_optional) { + extra_convert << " "; + } + extra_convert + << "DTOOL_Call_ExtractThisPointerForType(" << param_name + << ", &Dtool_" << make_safe_name(class_name) + << ", (void **)&" << param_name << "_this);\n"; + } else { + extra_convert << boolalpha + << " = (" << class_name << " *)" + << "DTOOL_Call_GetPointerThisClass(" << param_name + << ", &Dtool_" << make_safe_name(class_name) + << ", " << pn << ", \"" + << method_prefix << methodNameFromCppName(remap, this_class_name, false) + << "\", " << const_ok << ", " << report_errors << ");\n"; + } + + if (is_optional) { + extra_convert << "}\n"; + extra_param_check << " && (" << param_name << " == NULL || " << param_name << "_this != NULL)"; + } else { + extra_param_check << " && " << param_name << "_this != NULL"; + } + + pexpr_string = param_name + "_this"; } } else { @@ -3428,49 +5151,44 @@ write_function_instance(ostream &out, InterfaceMaker::Object *obj, parameter_list += ", &" + param_name; } expected_params += "any"; - ++num_params; } - if (remap->_parameters[pn]._has_name) { - expected_params += " " + remap->_parameters[pn]._name; + if (!reported_name.empty()) { + expected_params += " " + reported_name; } - - if (remap->_has_this && pn == 0) { - container = "local_this"; - if (remap->_const_method) { - string class_name = remap->_cpptype->get_local_name(&parser); - container = "(const " + class_name + "*)local_this"; - } - } - pexprs.push_back(pexpr_string); } expected_params += ")\n"; - // If this is the only overload, don't bother checking for type errors. - // Any type error that is raised will simply pass through to below. - //if (func->_remaps.size() == 1) { - // check_exceptions = false; - //} + if (min_version > 0) { + out << "#if PY_VERSION_HEX >= 0x" << hex << min_version << dec << "\n"; + } // Track how many curly braces we've opened. short open_scopes = 0; - if (!format_specifiers.empty()) { - std::string format_specifiers1 = format_specifiers + ":" + - methodNameFromCppName(func, "", false); + if (!type_check.empty() && args_type == AT_single_arg) { + indent(out, indent_level) + << "if (" << type_check << ") {\n"; + + ++open_scopes; + indent_level += 2; + + } else if (!format_specifiers.empty()) { + string method_name = methodNameFromCppName(remap, "", false); switch (args_type) { case AT_keyword_args: // Wrapper takes a varargs tuple and a keyword args dict. indent(out, indent_level) - << "static char *keyword_list[] = {" << keyword_list << "NULL};\n"; + << "static const char *keyword_list[] = {" << keyword_list << "NULL};\n"; indent(out, indent_level) << "if (PyArg_ParseTupleAndKeywords(args, kwds, \"" - << format_specifiers1 << "\", keyword_list" << parameter_list - << ")) {\n"; + << format_specifiers << ":" << method_name + << "\", (char **)keyword_list" << parameter_list << ")) {\n"; ++open_scopes; + clear_error = true; indent_level += 2; break; @@ -3481,28 +5199,30 @@ write_function_instance(ostream &out, InterfaceMaker::Object *obj, // more efficient PyArg_UnpackTuple function instead. indent(out, indent_level) << "if (PyArg_UnpackTuple(args, \"" - << methodNameFromCppName(func, "", false) - << "\", " << num_params - << ", " << num_params + << methodNameFromCppName(remap, "", false) + << "\", " << min_num_args << ", " << max_num_args << parameter_list << ")) {\n"; + } else { indent(out, indent_level) << "if (PyArg_ParseTuple(args, \"" - << format_specifiers1 << "\"" - << parameter_list << ")) {\n"; + << format_specifiers << ":" << method_name + << "\"" << parameter_list << ")) {\n"; } ++open_scopes; + clear_error = true; indent_level += 2; break; case AT_single_arg: - // Wrapper takes a single PyObject* argument. + // Single argument. If not a PyObject*, use PyArg_Parse. if (!only_pyobjects && format_specifiers != "O") { indent(out, indent_level) - << "if (PyArg_Parse(arg, \"" << format_specifiers << "\"" - << parameter_list << ")) {\n"; + << "if (PyArg_Parse(arg, \"" << format_specifiers << ":" + << method_name << "\"" << parameter_list << ")) {\n"; ++open_scopes; + clear_error = true; indent_level += 2; } @@ -3511,197 +5231,360 @@ write_function_instance(ostream &out, InterfaceMaker::Object *obj, } } - if (!extra_convert.empty()) { - indent(out, indent_level) - << extra_convert << "\n"; + while (extra_convert.is_text_available()) { + string line = extra_convert.get_line(); + if (line.size() == 0 || line[0] == '#') { + out << line << "\n"; + } else { + indent(out, indent_level) << line << "\n"; + } } - if (!extra_param_check.empty()) { + string extra_param_check_str = extra_param_check.str(); + if (!extra_param_check_str.empty()) { indent(out, indent_level) - << "if (" << extra_param_check.substr(4) << ") {\n"; + << "if (" << extra_param_check_str.substr(4) << ") {\n"; ++open_scopes; indent_level += 2; } + if (!remap->_has_this && (remap->_flags & FunctionRemap::F_explicit_self) != 0) { + // If we'll be passing "self" to the constructor, we need to + // pre-initialize it here. Unfortunately, we can't pre-load the + // "this" pointer, but the constructor itself can do this. + + CPPType *orig_type = remap->_return_type->get_orig_type(); + TypeIndex type_index = builder.get_type(TypeManager::unwrap(TypeManager::resolve_type(orig_type)), false); + const InterrogateType &itype = idb->get_type(type_index); + + indent(out, indent_level) + << "// Pre-initialize self for the constructor\n"; + + if (!is_constructor || (return_flags & RF_int) == 0) { + // This is not a constructor, but somehow we landed up here at a + // static method requiring a 'self' pointer. This happens in + // coercion constructors in particular. We'll have to create + // a temporary PyObject instance to pass to it. + + indent(out, indent_level) + << "PyObject *self = Dtool_new_" + << make_safe_name(itype.get_scoped_name()) << "(&" + << CLASS_PREFIX << make_safe_name(itype.get_scoped_name()) + << "._PyType, NULL, NULL);\n"; + + extra_cleanup << "PyObject_Del(self);\n"; + } else { + //XXX rdb: this isn't needed, is it, because tp_new already + // initializes the instance? + indent(out, indent_level) + << "DTool_PyInit_Finalize(self, NULL, &" + << CLASS_PREFIX << make_safe_name(itype.get_scoped_name()) + << ", false, false);\n"; + } + } + string return_expr; - if (!remap->_void_return && - remap->_return_type->new_type_is_atomic_string()) { + if (remap->_blocking) { + // With SIMPLE_THREADS, it's important that we never release the + // interpreter lock. + out << "#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)\n"; + indent(out, indent_level) + << "PyThreadState *_save;\n"; + indent(out, indent_level) + << "Py_UNBLOCK_THREADS\n"; + out << "#endif // HAVE_THREADS && !SIMPLE_THREADS\n"; + } + if (track_interpreter) { + indent(out, indent_level) << "in_interpreter = 0;\n"; + } + + // If the function returns a pointer that we may need to manage, we store + // it in a temporary return_value variable and set this to true. + bool manage_return = false; + + if (remap->_return_type->new_type_is_atomic_string()) { // Treat strings as a special case. We don't want to format the // return expression. - if (remap->_blocking) { - // With SIMPLE_THREADS, it's important that we never release the - // interpreter lock. - out << "#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)\n"; - indent(out, indent_level) - << "PyThreadState *_save;\n"; - indent(out, indent_level) - << "Py_UNBLOCK_THREADS\n"; - out << "#endif // HAVE_THREADS && !SIMPLE_THREADS\n"; - } - if (track_interpreter) { - indent(out, indent_level) << "in_interpreter = 0;\n"; - } - string tt; return_expr = remap->call_function(out, indent_level, false, container, pexprs); CPPType *type = remap->_return_type->get_orig_type(); indent(out, indent_level); type->output_instance(out, "return_value", &parser); - // type->output_instance(tt, "return_value", &parser); out << " = " << return_expr << ";\n"; - - if (track_interpreter) { - indent(out, indent_level) << "in_interpreter = 1;\n"; - } - if (remap->_blocking) { - out << "#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)\n"; - indent(out, indent_level) - << "Py_BLOCK_THREADS\n"; - out << "#endif // HAVE_THREADS && !SIMPLE_THREADS\n"; - } - if (!extra_cleanup.empty()) { - indent(out, indent_level) << extra_cleanup << "\n"; - } - - return_expr = manage_return_value(out, 4, remap, "return_value"); + manage_return = remap->_return_value_needs_management; + return_expr = "return_value"; } else { - if (remap->_blocking) { - out << "#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)\n"; - indent(out, indent_level) - << "PyThreadState *_save;\n"; - indent(out, indent_level) - << "Py_UNBLOCK_THREADS\n"; - out << "#endif // HAVE_THREADS && !SIMPLE_THREADS\n"; - } - if (track_interpreter) { - indent(out, indent_level) << "in_interpreter = 0;\n"; - } - + // The general case; an ordinary constructor or function. return_expr = remap->call_function(out, indent_level, true, container, pexprs); - if (return_expr.empty()) { - if (track_interpreter) { - indent(out, indent_level) << "in_interpreter = 1;\n"; - } - if (remap->_blocking) { - out << "#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)\n"; - indent(out, indent_level) - << "Py_BLOCK_THREADS\n"; - out << "#endif // HAVE_THREADS && !SIMPLE_THREADS\n"; - } - if (!extra_cleanup.empty()) { - indent(out, indent_level) << extra_cleanup << "\n"; - } - if (coercion_possible) { - indent(out, indent_level) - << "Py_XDECREF(coerced);\n"; - } - } else { + if (return_flags & RF_self) { + // We won't be using the return value, anyway. + return_expr.clear(); + } + + if (!return_expr.empty()) { + manage_return = remap->_return_value_needs_management; CPPType *type = remap->_return_type->get_temporary_type(); - if (!is_inplace) { - indent(out, indent_level); - type->output_instance(out, "return_value", &parser); - out << " = " << return_expr << ";\n"; - } - - if (track_interpreter) { - indent(out, indent_level) << "in_interpreter = 1;\n"; - } - if (remap->_blocking) { - out << "#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)\n"; - indent(out, indent_level) - << "Py_BLOCK_THREADS\n"; - out << "#endif // HAVE_THREADS && !SIMPLE_THREADS\n"; - } - if (!extra_cleanup.empty()) { - indent(out, indent_level) << extra_cleanup << "\n"; - } - if (coercion_possible) { - indent(out, indent_level) - << "Py_XDECREF(coerced);\n"; - } - if (!is_inplace) { - if (remap->_return_type->return_value_needs_management()) { - // If a constructor returns NULL, that means allocation failed. - indent(out, indent_level) << "if (return_value == NULL) {\n"; - if (return_int) { - indent(out, indent_level) << " PyErr_NoMemory();\n"; - indent(out, indent_level) << " return -1;\n"; - } else { - indent(out, indent_level) << " return PyErr_NoMemory();\n"; - } - indent(out, indent_level) << "}\n"; - } - - return_expr = manage_return_value(out, indent_level, remap, "return_value"); - } - return_expr = remap->_return_type->temporary_to_return(return_expr); + indent(out, indent_level); + type->output_instance(out, "return_value", &parser); + out << " = " << return_expr << ";\n"; + return_expr = "return_value"; } } - // If a method raises TypeError, continue. - //if (check_exceptions) { - if (true) { - indent(out, indent_level) - << "if (PyErr_Occurred()) {\n"; - delete_return_value(out, indent_level + 2, remap, return_expr); - indent(out, indent_level) - << " if (PyErr_ExceptionMatches(PyExc_TypeError)) {\n"; - indent(out, indent_level) - << " // TypeError raised; continue to next overload type.\n"; - indent(out, indent_level) - << " } else {\n"; - if (return_int) { - indent(out, indent_level + 2) - << " return -1;\n"; + // Clean up any memory we might have allocate for parsing the parameters. + while (extra_cleanup.is_text_available()) { + string line = extra_cleanup.get_line(); + if (line.size() == 0 || line[0] == '#') { + out << line << "\n"; } else { - indent(out, indent_level + 2) - << " return (PyObject *)NULL;\n"; + indent(out, indent_level) << line << "\n"; } - indent(out, indent_level) - << " }\n"; - indent(out, indent_level) - << "} else {\n"; - - ++open_scopes; - indent_level += 2; } - // Outputs code to check to see if an assertion has failed while - // the C++ code was executing, and report this failure back to Python. - if (watch_asserts) { - out << "#ifndef NDEBUG\n"; + if (track_interpreter) { + indent(out, indent_level) << "in_interpreter = 1;\n"; + } + if (remap->_blocking) { + out << "#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)\n"; indent(out, indent_level) - << "Notify *notify = Notify::ptr();\n"; - indent(out, indent_level) - << "if (notify->has_assert_failed()) {\n"; - indent(out, indent_level + 2) - << "PyErr_SetString(PyExc_AssertionError, notify->get_assert_error_message().c_str());\n"; - indent(out, indent_level + 2) - << "notify->clear_assert_failed();\n"; - delete_return_value(out, indent_level + 2, remap, return_expr); - if (return_int) { - indent(out, indent_level + 2) << "return -1;\n"; + << "Py_BLOCK_THREADS\n"; + out << "#endif // HAVE_THREADS && !SIMPLE_THREADS\n"; + } + + if (manage_return) { + // If a constructor returns NULL, that means allocation failed. + if (remap->_return_type->return_value_needs_management()) { + indent(out, indent_level) << "if (return_value == NULL) {\n"; + if ((return_flags & ~RF_pyobject) == RF_err_null) { + // PyErr_NoMemory returns NULL, so allow tail call elimination. + indent(out, indent_level) << " return PyErr_NoMemory();\n"; + } else { + indent(out, indent_level) << " PyErr_NoMemory();\n"; + error_return(out, indent_level + 2, return_flags); + } + indent(out, indent_level) << "}\n"; + } + + return_expr = manage_return_value(out, indent_level, remap, "return_value"); + return_expr = remap->_return_type->temporary_to_return(return_expr); + } + + // How could we raise a TypeError if we don't take any args? + if (args_type == AT_no_args || max_num_args == 0) { + may_raise_typeerror = false; + } + + // If a function takes a PyObject* argument, it would be a good idea to + // always check for exceptions. + if (may_raise_typeerror) { + check_exceptions = true; + } + + // Generated getters and setters don't raise exceptions or asserts + // since they don't contain any code. + if (remap->_type == FunctionRemap::T_getter || + remap->_type == FunctionRemap::T_setter) { + check_exceptions = false; + } + + // The most common case of the below logic is consolidated in a single + // function, as another way to reduce code bloat. Sigh. + if (check_exceptions && (!may_raise_typeerror || report_errors) && + watch_asserts && (return_flags & RF_coerced) == 0) { + + if (return_flags & RF_decref_args) { + indent(out, indent_level) << "Py_DECREF(args);\n"; + return_flags &= ~RF_decref_args; + } + + // An even specialer special case for functions with void return or + // bool return. We have our own functions that do all this in a + // single function call, so it should reduce the amount of code output + // while not being any slower. + bool return_null = (return_flags & RF_pyobject) != 0 && + (return_flags & RF_err_null) != 0; + if (return_null && return_expr.empty()) { + indent(out, indent_level) + << "return Dtool_Return_None();\n"; + + // Reset the return value bit so that the code below doesn't generate + // the return statement a second time. + return_flags &= ~RF_pyobject; + + } else if (return_null && TypeManager::is_bool(remap->_return_type->get_new_type())) { + indent(out, indent_level) + << "return Dtool_Return_Bool(" << return_expr << ");\n"; + return_flags &= ~RF_pyobject; + } else { - indent(out, indent_level + 2) << "return (PyObject *)NULL;\n"; + indent(out, indent_level) + << "if (Dtool_CheckErrorOccurred()) {\n"; + + if (manage_return) { + delete_return_value(out, indent_level + 2, remap, return_expr); + } + error_return(out, indent_level + 2, return_flags); + + indent(out, indent_level) << "}\n"; } - indent(out, indent_level) - << "}\n"; - out << "#endif\n"; + } else { + if (check_exceptions) { + // Check if a Python exception has occurred. We only do this when + // check_exception is set. If report_errors is set, this method + // must terminate on error. + if (!may_raise_typeerror || report_errors) { + indent(out, indent_level) + << "if (_PyErr_OCCURRED()) {\n"; + } else { + // If a method is some extension method that takes a PyObject*, + // and it raised a TypeError, continue. + // The documentation tells us not to compare the result of + // PyErr_Occurred against a specific exception type. However, in our + // case, this seems okay because we know that the TypeError we want + // to catch here is going to be generated by a PyErr_SetString call, + // not by user code. + indent(out, indent_level) + << "PyObject *exception = _PyErr_OCCURRED();\n"; + indent(out, indent_level) + << "if (exception == PyExc_TypeError) {\n"; + indent(out, indent_level) + << " // TypeError raised; continue to next overload type.\n"; + indent(out, indent_level) + << "} else if (exception != (PyObject *)NULL) {\n"; + } + + if (manage_return) { + delete_return_value(out, indent_level + 2, remap, return_expr); + } + + error_return(out, indent_level + 2, return_flags); + + indent(out, indent_level) + << "} else {\n"; + + ++open_scopes; + indent_level += 2; + } + + if (return_flags & RF_decref_args) { + indent(out, indent_level) << "Py_DECREF(args);\n"; + return_flags &= ~RF_decref_args; + } + + // Outputs code to check to see if an assertion has failed while + // the C++ code was executing, and report this failure back to Python. + // Don't do this for coercion constructors since they are called by + // other wrapper functions which already check this on their own. + if (watch_asserts && (return_flags & RF_coerced) == 0) { + out << "#ifndef NDEBUG\n"; + indent(out, indent_level) + << "Notify *notify = Notify::ptr();\n"; + indent(out, indent_level) + << "if (notify->has_assert_failed()) {\n"; + + if (manage_return) { + // Output code to delete any temporary object we may have allocated. + delete_return_value(out, indent_level + 2, remap, return_expr); + } + + if (return_flags & RF_err_null) { + // This function returns NULL, so we can pass it on. + indent(out, indent_level + 2) + << "return Dtool_Raise_AssertionError();\n"; + } else { + indent(out, indent_level + 2) + << "Dtool_Raise_AssertionError();\n"; + error_return(out, indent_level + 2, return_flags); + } + + indent(out, indent_level) + << "}\n"; + out << "#endif\n"; + } } - if (return_expr.empty()) { - if (return_int) { + // Okay, we're past all the error conditions and special cases. Now + // return the return type in the way that was requested. + if (return_flags & RF_int) { + CPPType *orig_type = remap->_return_type->get_orig_type(); + if (is_constructor) { + // Special case for constructor. + TypeIndex type_index = builder.get_type(TypeManager::unwrap(TypeManager::resolve_type(orig_type)), false); + const InterrogateType &itype = idb->get_type(type_index); + indent(out, indent_level) + << "return DTool_PyInit_Finalize(self, " << return_expr << ", &" << CLASS_PREFIX << make_safe_name(itype.get_scoped_name()) << ", true, false);\n"; + + } else if (TypeManager::is_integer(orig_type)) { + indent(out, indent_level) << "return " << return_expr << ";\n"; + + } else if (TypeManager::is_void(orig_type)) { indent(out, indent_level) << "return 0;\n"; + } else { + cerr << "Warning: function has return type " << *orig_type + << ", expected int or void:\n" << expected_params << "\n"; + indent(out, indent_level) << "// Don't know what to do with return type " + << *orig_type << ".\n"; + indent(out, indent_level) << "return 0;\n"; + } + + } else if (return_flags & RF_self) { + indent(out, indent_level) << "Py_INCREF(self);\n"; + indent(out, indent_level) << "return self;\n"; + + } else if (return_flags & RF_pyobject) { + if (return_expr.empty()) { indent(out, indent_level) << "Py_INCREF(Py_None);\n"; indent(out, indent_level) << "return Py_None;\n"; + + } else { + pack_return_value(out, indent_level, remap, return_expr); } - } else { - pack_return_value(out, indent_level, remap, return_expr, is_inplace); + + } else if (return_flags & RF_coerced) { + // We were asked to assign the result to a "coerced" reference. + CPPType *return_type = remap->_cpptype; + CPPType *orig_type = remap->_return_type->get_orig_type(); + + // Special case for static make function that returns a pointer: + // cast the pointer to the right pointer type. + if (!is_constructor && (remap->_flags & FunctionRemap::F_coerce_constructor) != 0 && + (TypeManager::is_pointer(orig_type) || TypeManager::is_pointer_to_base(orig_type))) { + + CPPType *new_type = remap->_return_type->get_new_type(); + + if (TypeManager::is_const_pointer_to_anything(new_type)) { + return_type = CPPType::new_type(new CPPConstType(return_type)); + } + + if (IsPandaTypedObject(return_type->as_struct_type())) { + return_expr = "DCAST(" + + return_type->get_local_name(&parser) + + ", " + return_expr + ")"; + + } else { + return_type = CPPType::new_type(new CPPPointerType(return_type)); + return_expr = "(" + return_type->get_local_name(&parser) + + ") " + return_expr; + } + } + + if (return_expr == "coerced") { + // We already did this earlier... + + } else if (TypeManager::is_reference_count(remap->_cpptype)) { + indent(out, indent_level) << "coerced = MOVE(" << return_expr << ");\n"; + + } else { + indent(out, indent_level) << "coerced = " << return_expr << ";\n"; + indent(out, indent_level) << "manage = true;\n"; + } + + indent(out, indent_level) << "return true;\n"; } // Close the extra braces opened earlier. @@ -3711,6 +5594,103 @@ write_function_instance(ostream &out, InterfaceMaker::Object *obj, --open_scopes; } + + if (clear_error && !report_errors) { + // We were asked not to report errors, so clear the active exception + // if this overload might have raised a TypeError. + indent(out, indent_level) << "PyErr_Clear();\n"; + } + + if (min_version > 0) { + // Close the #if PY_VERSION_HEX check. + out << "#endif\n"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: InterfaceMakerPythonNative::error_return +// Access: Private +// Description: Outputs the correct return statement that should be +// used in case of error based on the ReturnFlags. +//////////////////////////////////////////////////////////////////// +void InterfaceMakerPythonNative:: +error_return(ostream &out, int indent_level, int return_flags) { + //if (return_flags & RF_coerced) { + // indent(out, indent_level) << "coerced = NULL;\n"; + //} + + if (return_flags & RF_decref_args) { + indent(out, indent_level) << "Py_DECREF(args);\n"; + } + + if (return_flags & RF_int) { + indent(out, indent_level) << "return -1;\n"; + + } else if (return_flags & RF_err_notimplemented) { + indent(out, indent_level) << "Py_INCREF(Py_NotImplemented);\n"; + indent(out, indent_level) << "return Py_NotImplemented;\n"; + + } else if (return_flags & RF_err_null) { + indent(out, indent_level) << "return NULL;\n"; + + } else if (return_flags & RF_err_false) { + indent(out, indent_level) << "return false;\n"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: InterfaceMakerPythonNative::error_raise_return +// Access: Private +// Description: Similar to error_return, except raises an exception +// before returning. If format_args are not the empty +// string, uses PyErr_Format instead of PyErr_SetString. +//////////////////////////////////////////////////////////////////// +void InterfaceMakerPythonNative:: +error_raise_return(ostream &out, int indent_level, int return_flags, + const string &exc_type, const string &message, + const string &format_args) { + + if (return_flags & RF_decref_args) { + indent(out, indent_level) << "Py_DECREF(args);\n"; + return_flags &= ~RF_decref_args; + } + + if (format_args.empty()) { + if (exc_type == "TypeError") { + if ((return_flags & RF_err_null) != 0) { + // This is probably an over-optimization, but why the heck not. + indent(out, indent_level) << "return Dtool_Raise_TypeError("; + output_quoted(out, indent_level + 29, message, false); + out << ");\n"; + return; + } else { + indent(out, indent_level) << "Dtool_Raise_TypeError("; + output_quoted(out, indent_level + 22, message, false); + out << ");\n"; + } + } else { + indent(out, indent_level) << "PyErr_SetString(PyExc_" << exc_type << ",\n"; + output_quoted(out, indent_level + 16, message); + out << ");\n"; + } + + } else if ((return_flags & RF_err_null) != 0) { + // PyErr_Format always returns NULL. Passing it on directly allows + // the compiler to make a tiny optimization, so why not. + indent(out, indent_level) << "return PyErr_Format(PyExc_" << exc_type << ",\n"; + output_quoted(out, indent_level + 20, message); + out << ",\n"; + indent(out, indent_level + 20) << format_args << ");\n"; + return; + + } else { + indent(out, indent_level) << "PyErr_Format(PyExc_" << exc_type << ",\n"; + output_quoted(out, indent_level + 13, message); + out << ",\n"; + indent(out, indent_level + 13) << format_args << ");\n"; + } + + error_return(out, indent_level, return_flags); } //////////////////////////////////////////////////////////////////// @@ -3721,58 +5701,24 @@ write_function_instance(ostream &out, InterfaceMaker::Object *obj, //////////////////////////////////////////////////////////////////// void InterfaceMakerPythonNative:: pack_return_value(ostream &out, int indent_level, FunctionRemap *remap, - const string &return_expr, bool is_inplace) { + string return_expr) { - if (remap->_type == FunctionRemap::T_constructor) { - // should only reach this in the INIT function a a Class .. IE the PY exists before the CPP object - // this is were we type to returned a class/struct.. ie CPP Type - CPPType *orig_type = remap->_return_type->get_orig_type(); - - TypeIndex type_index = builder.get_type(TypeManager::unwrap(TypeManager::resolve_type(orig_type)), false); - InterrogateDatabase *idb = InterrogateDatabase::get_ptr(); - const InterrogateType &itype = idb->get_type(type_index); - indent(out, indent_level) - << "return DTool_PyInit_Finalize(self, " << return_expr << ", &" << CLASS_PREFIX << make_safe_name(itype.get_scoped_name()) << ", true, false);\n"; - - } else { - ParameterRemap *return_type = remap->_return_type; - pack_python_value(out, indent_level, remap, return_type, return_expr, "", is_inplace); - } -} - -//////////////////////////////////////////////////////////////////// -// Function: InterfaceMakerPythonNative::pack_python_value -// Access: Private -// Description: Outputs a command to pack the indicated expression, -// of the return_type type, as a Python value. -// If assign_to is empty, the Python object is -// returned. Otherwise, it is assigned to a variable -// of that name (expected to already be declared). -//////////////////////////////////////////////////////////////////// -void InterfaceMakerPythonNative:: -pack_python_value(ostream &out, int indent_level, FunctionRemap *remap, - ParameterRemap *return_type, const string &return_expr, - const string &assign_to, bool is_inplace) { + ParameterRemap *return_type = remap->_return_type; CPPType *orig_type = return_type->get_orig_type(); CPPType *type = return_type->get_new_type(); - string assign_stmt("return "); - if (!assign_to.empty()) { - assign_stmt = assign_to + " = "; - } - if (return_type->new_type_is_atomic_string()) { if (TypeManager::is_char_pointer(orig_type)) { indent(out, indent_level) << "if (" << return_expr << " == NULL) {\n"; indent(out, indent_level) << " Py_INCREF(Py_None);\n"; - indent(out, indent_level+2) << assign_stmt << "Py_None;\n"; + indent(out, indent_level) << " return Py_None;\n"; indent(out, indent_level) << "} else {\n"; out << "#if PY_MAJOR_VERSION >= 3\n"; - indent(out, indent_level+2) << assign_stmt + indent(out, indent_level) << " return " << "PyUnicode_FromString(" << return_expr << ");\n"; out << "#else\n"; - indent(out, indent_level+2) << assign_stmt + indent(out, indent_level) << " return " << "PyString_FromString(" << return_expr << ");\n"; out << "#endif\n"; @@ -3781,25 +5727,25 @@ pack_python_value(ostream &out, int indent_level, FunctionRemap *remap, } else if (TypeManager::is_wchar_pointer(orig_type)) { indent(out, indent_level) << "if (" << return_expr << " == NULL) {\n"; indent(out, indent_level) << " Py_INCREF(Py_None);\n"; - indent(out, indent_level+2) << assign_stmt << "Py_None;\n"; + indent(out, indent_level) << " return Py_None;\n"; indent(out, indent_level) << "} else {\n"; indent(out, indent_level+2) - << assign_stmt << "PyUnicode_FromWideChar(" + << "return PyUnicode_FromWideChar(" << return_expr << ", wcslen(" << return_expr << "));\n"; indent(out, indent_level) << "}\n"; } else if (TypeManager::is_wstring(orig_type)) { - indent(out, indent_level) << assign_stmt - << "PyUnicode_FromWideChar(" + indent(out, indent_level) + << "return PyUnicode_FromWideChar(" << return_expr << ".data(), (int) " << return_expr << ".length());\n"; } else if (TypeManager::is_const_ptr_to_basic_string_wchar(orig_type)) { indent(out, indent_level) << "if (" << return_expr << " == NULL) {\n"; indent(out, indent_level) << " Py_INCREF(Py_None);\n"; - indent(out, indent_level+2) << assign_stmt << "Py_None;\n"; + indent(out, indent_level) << " return Py_None;\n"; indent(out, indent_level) << "} else {\n"; - indent(out, indent_level+2) << assign_stmt + indent(out, indent_level) << " return " << "PyUnicode_FromWideChar(" << return_expr << "->data(), (int) " << return_expr << "->length());\n"; @@ -3808,15 +5754,15 @@ pack_python_value(ostream &out, int indent_level, FunctionRemap *remap, } else if (TypeManager::is_const_ptr_to_basic_string_char(orig_type)) { indent(out, indent_level) << "if (" << return_expr<< " == NULL) {\n"; indent(out, indent_level) << " Py_INCREF(Py_None);\n"; - indent(out, indent_level+2) << assign_stmt << "Py_None;\n"; + indent(out, indent_level) << " return Py_None;\n"; indent(out, indent_level) << "} else {\n"; out << "#if PY_MAJOR_VERSION >= 3\n"; - indent(out, indent_level+2) << assign_stmt + indent(out, indent_level) << " return " << "PyUnicode_FromStringAndSize(" << return_expr << "->data(), (Py_ssize_t)" << return_expr << "->length());\n"; out << "#else\n"; - indent(out, indent_level+2) << assign_stmt + indent(out, indent_level) << " return " << "PyString_FromStringAndSize(" << return_expr << "->data(), (Py_ssize_t)" << return_expr << "->length());\n"; out << "#endif\n"; @@ -3825,61 +5771,78 @@ pack_python_value(ostream &out, int indent_level, FunctionRemap *remap, } else { out << "#if PY_MAJOR_VERSION >= 3\n"; - indent(out, indent_level) << assign_stmt - << "PyUnicode_FromStringAndSize(" + indent(out, indent_level) + << "return PyUnicode_FromStringAndSize(" << return_expr << ".data(), (Py_ssize_t)" << return_expr << ".length());\n"; out << "#else\n"; - indent(out, indent_level) << assign_stmt - << "PyString_FromStringAndSize(" + indent(out, indent_level) + << "return PyString_FromStringAndSize(" << return_expr << ".data(), (Py_ssize_t)" << return_expr << ".length());\n"; out << "#endif\n"; } } else if (TypeManager::is_bool(type)) { - indent(out, indent_level) << assign_stmt - << "PyBool_FromLong(" << return_expr << ");\n"; + indent(out, indent_level) + << "return PyBool_FromLong(" << return_expr << ");\n"; + + } else if (TypeManager::is_size(type)) { + indent(out, indent_level) + << "return PyLongOrInt_FromSize_t(" << return_expr << ");\n"; + + } else if (TypeManager::is_char(type)) { + out << "#if PY_MAJOR_VERSION >= 3\n"; + indent(out, indent_level) + << "return PyUnicode_FromStringAndSize(&" << return_expr << ", 1);\n"; + out << "#else\n"; + indent(out, indent_level) + << "return PyString_FromStringAndSize(&" << return_expr << ", 1);\n"; + out << "#endif\n"; + + } else if (TypeManager::is_wchar(type)) { + indent(out, indent_level) + << "return PyUnicode_FromWideChar(&" << return_expr << ", 1);\n"; } else if (TypeManager::is_unsigned_longlong(type)) { - indent(out, indent_level) << assign_stmt - << "PyLong_FromUnsignedLongLong(" << return_expr << ");\n"; + indent(out, indent_level) + << "return PyLong_FromUnsignedLongLong(" << return_expr << ");\n"; } else if (TypeManager::is_longlong(type)) { - indent(out, indent_level) << assign_stmt - << "PyLong_FromLongLong(" << return_expr << ");\n"; + indent(out, indent_level) + << "return PyLong_FromLongLong(" << return_expr << ");\n"; } else if (TypeManager::is_unsigned_integer(type)){ out << "#if PY_MAJOR_VERSION >= 3\n"; - indent(out, indent_level) << assign_stmt - << "PyLong_FromUnsignedLong(" << return_expr << ");\n"; + indent(out, indent_level) + << "return PyLong_FromUnsignedLong(" << return_expr << ");\n"; out << "#else\n"; - indent(out, indent_level) << assign_stmt - << "PyLongOrInt_FromUnsignedLong(" << return_expr << ");\n"; + indent(out, indent_level) + << "return PyLongOrInt_FromUnsignedLong(" << return_expr << ");\n"; out << "#endif\n"; } else if (TypeManager::is_integer(type)) { out << "#if PY_MAJOR_VERSION >= 3\n"; - indent(out, indent_level) << assign_stmt - << "PyLong_FromLong(" << return_expr << ");\n"; + indent(out, indent_level) + << "return PyLong_FromLong(" << return_expr << ");\n"; out << "#else\n"; - indent(out, indent_level) << assign_stmt - << "PyInt_FromLong(" << return_expr << ");\n"; + indent(out, indent_level) + << "return PyInt_FromLong(" << return_expr << ");\n"; out << "#endif\n"; } else if (TypeManager::is_float(type)) { - indent(out, indent_level) << assign_stmt - << "PyFloat_FromDouble(" << return_expr << ");\n"; + indent(out, indent_level) + << "return PyFloat_FromDouble(" << return_expr << ");\n"; } else if (TypeManager::is_char_pointer(type)) { indent(out, indent_level) << "if (" << return_expr << " == NULL) {\n"; indent(out, indent_level) << " Py_INCREF(Py_None);\n"; - indent(out, indent_level+2) << assign_stmt << "Py_None;\n"; + indent(out, indent_level) << " return Py_None;\n"; indent(out, indent_level) << "} else {\n"; out << "#if PY_MAJOR_VERSION >= 3\n"; - indent(out, indent_level+2) << assign_stmt + indent(out, indent_level) << " return " << "PyUnicode_FromString(" << return_expr << ");\n"; out << "#else\n"; - indent(out, indent_level+2) << assign_stmt + indent(out, indent_level) << " return " << "PyString_FromString(" << return_expr << ");\n"; out << "#endif\n"; @@ -3888,9 +5851,9 @@ pack_python_value(ostream &out, int indent_level, FunctionRemap *remap, } else if (TypeManager::is_wchar_pointer(type)) { indent(out, indent_level) << "if (" << return_expr << " == NULL) {\n"; indent(out, indent_level) << " Py_INCREF(Py_None);\n"; - indent(out, indent_level+2) << assign_stmt << "Py_None;\n"; + indent(out, indent_level) << " return Py_None;\n"; indent(out, indent_level) << "} else {\n"; - indent(out, indent_level+2) << assign_stmt + indent(out, indent_level) << " return " << "PyUnicode_FromWideChar(" << return_expr << ", wcslen(" << return_expr << "));\n"; @@ -3898,100 +5861,78 @@ pack_python_value(ostream &out, int indent_level, FunctionRemap *remap, } else if (TypeManager::is_pointer_to_PyObject(type)) { indent(out, indent_level) - << assign_stmt << return_expr << ";\n"; + << "return " << return_expr << ";\n"; } else if (TypeManager::is_pointer_to_Py_buffer(type)) { indent(out, indent_level) << "if (" << return_expr << " == NULL) {\n"; indent(out, indent_level) << " Py_INCREF(Py_None);\n"; - indent(out, indent_level+2) << assign_stmt << "Py_None;\n"; + indent(out, indent_level) << " return Py_None;\n"; indent(out, indent_level) << "} else {\n"; - indent(out, indent_level+2) << assign_stmt + indent(out, indent_level) << " return " << "PyMemoryView_FromBuffer(" << return_expr << ");\n"; indent(out, indent_level) << "}\n"; } else if (TypeManager::is_pointer(type)) { - string const_flag; - if (TypeManager::is_const_pointer_to_anything(type)) { - const_flag = "true"; - } else { - const_flag = "false"; + bool is_const = TypeManager::is_const_pointer_to_anything(type); + bool owns_memory = remap->_return_value_needs_management; + + // Note, we don't check for NULL here any more. This is now done by the + // appropriate CreateInstance(Typed) function. + + if (manage_reference_counts && TypeManager::is_pointer_to_base(orig_type)) { + // Use a trick to transfer the reference count to avoid a pair of + // unnecessary ref() and unref() calls. Ideally we'd use move + // semantics, but py_panda.cxx cannot make use of PointerTo. + indent(out, indent_level) << "// Transfer ownership of return_value.\n"; + indent(out, indent_level); + type->output_instance(out, "return_ptr", &parser); + out << " = " << return_expr << ";\n"; + indent(out, indent_level) << "return_value.cheat() = NULL;\n"; + return_expr = "return_ptr"; } - if (TypeManager::is_struct(orig_type) || TypeManager::is_ref_to_anything(orig_type)) { - if (TypeManager::is_ref_to_anything(orig_type)) { - TypeIndex type_index = builder.get_type(TypeManager::unwrap(TypeManager::resolve_type(type)),false); - InterrogateDatabase *idb = InterrogateDatabase::get_ptr(); - const InterrogateType &itype = idb->get_type(type_index); - std::string owns_memory_flag("true"); + InterrogateDatabase *idb = InterrogateDatabase::get_ptr(); - if (remap->_return_value_needs_management) { - owns_memory_flag = "true"; - } else { - owns_memory_flag = "false"; - } + if (TypeManager::is_struct(orig_type) || TypeManager::is_ref_to_anything(orig_type)) { + if (TypeManager::is_ref_to_anything(orig_type) || remap->_manage_reference_count) { + TypeIndex type_index = builder.get_type(TypeManager::unwrap(TypeManager::resolve_type(type)),false); + const InterrogateType &itype = idb->get_type(type_index); if (!isExportThisRun(itype._cpptype)) { - _external_imports.insert(make_safe_name(itype.get_scoped_name())); + _external_imports.insert(itype._cpptype); } - write_python_instance(out, indent_level, return_expr, assign_to, owns_memory_flag, itype.get_scoped_name(), itype._cpptype, is_inplace, const_flag); + write_python_instance(out, indent_level, return_expr, owns_memory, itype.get_scoped_name(), itype._cpptype, is_const); } else { - std::string owns_memory_flag("true"); - if (remap->_return_value_needs_management) { - owns_memory_flag = "true"; - } else { - owns_memory_flag = "false"; + TypeIndex type_index = builder.get_type(TypeManager::unwrap(TypeManager::resolve_type(orig_type)),false); + const InterrogateType &itype = idb->get_type(type_index); + + if (!isExportThisRun(itype._cpptype)) { + _external_imports.insert(itype._cpptype); } - if (remap->_manage_reference_count) { - TypeIndex type_index = builder.get_type(TypeManager::unwrap(TypeManager::resolve_type(type)),false); - InterrogateDatabase *idb = InterrogateDatabase::get_ptr(); - const InterrogateType &itype = idb->get_type(type_index); - - if (!isExportThisRun(itype._cpptype)) { - _external_imports.insert(make_safe_name(itype.get_scoped_name())); - } - - write_python_instance(out, indent_level, return_expr, assign_to, owns_memory_flag, itype.get_scoped_name(), itype._cpptype, is_inplace, const_flag); - } else { - TypeIndex type_index = builder.get_type(TypeManager::unwrap(TypeManager::resolve_type(orig_type)),false); - InterrogateDatabase *idb = InterrogateDatabase::get_ptr(); - const InterrogateType &itype = idb->get_type(type_index); - - if (!isExportThisRun(itype._cpptype)) { - _external_imports.insert(make_safe_name(itype.get_scoped_name())); - } - - write_python_instance(out, indent_level, return_expr, assign_to, owns_memory_flag, itype.get_scoped_name(), itype._cpptype, is_inplace, const_flag); - } + write_python_instance(out, indent_level, return_expr, owns_memory, itype.get_scoped_name(), itype._cpptype, is_const); } } else if (TypeManager::is_struct(orig_type->as_pointer_type()->_pointing_at)) { TypeIndex type_index = builder.get_type(TypeManager::unwrap(TypeManager::resolve_type(orig_type)),false); - InterrogateDatabase *idb = InterrogateDatabase::get_ptr(); const InterrogateType &itype = idb->get_type(type_index); - std::string owns_memory_flag("true"); - if (remap->_return_value_needs_management) { - owns_memory_flag = "true"; - } else { - owns_memory_flag = "false"; - } - if (!isExportThisRun(itype._cpptype)) { - _external_imports.insert(make_safe_name(itype.get_scoped_name())); + _external_imports.insert(itype._cpptype); } - write_python_instance(out, indent_level, return_expr, assign_to, owns_memory_flag, itype.get_scoped_name(), itype._cpptype, is_inplace, const_flag); + write_python_instance(out, indent_level, return_expr, owns_memory, itype.get_scoped_name(), itype._cpptype, is_const); } else { - indent(out, indent_level) << " Should Never Reach This InterfaceMakerPythonNative::pack_python_value"; - //<< "return PyInt_FromLong((int) " << return_expr << ");\n"; + indent(out, indent_level) << "Should Never Reach This InterfaceMakerPythonNative::pack_python_value"; + //<< "return PyLongOrInt_FromLong((int) " << return_expr << ");\n"; } + } else { // Return None. indent(out, indent_level) - << assign_stmt << "Py_BuildValue(\"\");\n"; + << "return Py_BuildValue(\"\");\n"; } } @@ -4003,17 +5944,48 @@ pack_python_value(ostream &out, int indent_level, FunctionRemap *remap, //////////////////////////////////////////////////////////////////// void InterfaceMakerPythonNative:: write_make_seq(ostream &out, Object *obj, const std::string &ClassName, - MakeSeq *make_seq) { + const std::string &cClassName, MakeSeq *make_seq) { out << "/******************************************************************\n" << " * Python make_seq wrapper\n"; out << " *******************************************************************/\n"; out << "static PyObject *" << make_seq->_name + "(PyObject *self, PyObject *) {\n"; - string num_name = methodNameFromCppName(make_seq->_num_name, ClassName, false); string element_name = methodNameFromCppName(make_seq->_element_name, ClassName, false); - out << " return make_list_for_item(self, \"" << num_name - << "\", \"" << element_name << "\");\n"; - out << "}\n"; + // This used to be a list. But it should really be a tuple, I think, + // because it probably makes more sense for it to be immutable (as + // changes to it won't be visible on the C++ side anyway). + + out << " " << cClassName << " *local_this = NULL;\n" + << " if (!Dtool_Call_ExtractThisPointer(self, Dtool_" << ClassName << ", (void **)&local_this)) {\n" + << " return NULL;\n" + << " }\n" + << "\n" + << " PyObject *getter = PyObject_GetAttrString(self, \"" << element_name << "\");\n" + << " if (getter == (PyObject *)NULL) {\n" + << " return NULL;\n" + << " }\n" + << "\n" + << " Py_ssize_t count = (Py_ssize_t)local_this->" << make_seq->_num_name << "();\n" + << " PyObject *tuple = PyTuple_New(count);\n" + << "\n" + << " for (Py_ssize_t i = 0; i < count; ++i) {\n" + << "#if PY_MAJOR_VERSION >= 3\n" + << " PyObject *index = PyLong_FromSsize_t(i);\n" + << "#else\n" + << " PyObject *index = PyInt_FromSsize_t(i);\n" + << "#endif\n" + << " PyObject *value = PyObject_CallFunctionObjArgs(getter, index, NULL);\n" + << " PyTuple_SET_ITEM(tuple, i, value);\n" + << " Py_DECREF(index);\n" + << " }\n" + << "\n" + << " if (Dtool_CheckErrorOccurred()) {\n" + << " Py_DECREF(tuple);\n" + << " return NULL;\n" + << " }\n" + << " return tuple;\n" + << "}\n" + << "\n"; } //////////////////////////////////////////////////////////////////// @@ -4092,7 +6064,7 @@ record_object(TypeIndex type_index) { function = record_function(base_type, itype.derivation_get_downcast(di)); if (is_function_legal(function)) { - Object * pobject = record_object(base_type_index); + Object *pobject = record_object(base_type_index); if (pobject != NULL) { pobject->_methods.push_back(function); } @@ -4215,25 +6187,32 @@ is_cpp_type_legal(CPPType *in_ctype) { return false; } - if (builder.in_ignoretype(in_ctype->get_local_name(&parser))) { + string name = in_ctype->get_local_name(&parser); + + if (builder.in_ignoretype(name)) { return false; } - //bool answer = false; - CPPType *type = TypeManager::unwrap(TypeManager::resolve_type(in_ctype)); - type = TypeManager::unwrap(type); - //CPPType *type = ctype; + if (builder.in_forcetype(name)) { + return true; + } - if (TypeManager::is_basic_string_char(type)) { + //bool answer = false; + CPPType *type = TypeManager::resolve_type(in_ctype); + type = TypeManager::unwrap(type); + + if (TypeManager::is_void(type)) { + return true; + } else if (TypeManager::is_basic_string_char(type)) { return true; } else if (TypeManager::is_basic_string_wchar(type)) { return true; } else if (TypeManager::is_simple(type)) { return true; - } else if (builder.in_forcetype(type->get_local_name(&parser))) { + } else if (TypeManager::is_pointer_to_simple(type)) { + return true; + } else if (TypeManager::is_exported(type)) { return true; - } else if (TypeManager::IsExported(type)) { - return true; } else if (TypeManager::is_pointer_to_PyObject(in_ctype)) { return true; } else if (TypeManager::is_pointer_to_Py_buffer(in_ctype)) { @@ -4245,25 +6224,28 @@ is_cpp_type_legal(CPPType *in_ctype) { return false; } -////////////////////////////////////////////// +////////////////////////////////////////////// // Function :isExportThisRun // -////////////////////////////////////////////// +////////////////////////////////////////////// bool InterfaceMakerPythonNative:: isExportThisRun(CPPType *ctype) { - CPPType *type = TypeManager::unwrap(ctype); - if (TypeManager::IsLocal(type)) { + if (builder.in_forcetype(ctype->get_local_name(&parser))) { return true; } - if (builder.in_forcetype(type->get_local_name(&parser))) { + if (!TypeManager::is_exported(ctype)) { + return false; + } + + if (TypeManager::is_local(ctype)) { return true; } return false; } -////////////////////////////////////////////// +////////////////////////////////////////////// // Function : isExportThisRun ///////////////////////////////////////////// bool InterfaceMakerPythonNative:: @@ -4273,7 +6255,7 @@ isExportThisRun(Function *func) { } Function::Remaps::const_iterator ri; - for (ri = func->_remaps.begin(); ri != func->_remaps.end(); ++ri) { + for (ri = func->_remaps.begin(); ri != func->_remaps.end();) { FunctionRemap *remap = (*ri); return isExportThisRun(remap->_cpptype); } @@ -4281,7 +6263,7 @@ isExportThisRun(Function *func) { return false; } -////////////////////////////////////////////// +////////////////////////////////////////////// // Function : is_remap_legal ////////////////////////////////////////////// bool InterfaceMakerPythonNative:: @@ -4296,6 +6278,12 @@ is_remap_legal(FunctionRemap *remap) { return false; } + // We don't currently support returning pointers, but we accept + // them as function parameters. + if (TypeManager::is_pointer_to_simple(remap->_return_type->get_orig_type())) { + return false; + } + // ouch .. bad things will happen here .. do not even try.. if (remap->_ForcedVoidReturn) { return false; @@ -4313,6 +6301,111 @@ is_remap_legal(FunctionRemap *remap) { return true; } +////////////////////////////////////////////// +// Function : has_coerce_constructor +// Returns 1 if coerce constructor +// returns const, 2 if non-const. +////////////////////////////////////////////// +int InterfaceMakerPythonNative:: +has_coerce_constructor(CPPStructType *type) { + if (type == NULL) { + return 0; + } + + CPPScope *scope = type->get_scope(); + if (scope == NULL) { + return 0; + } + + int result = 0; + + CPPScope::Functions::iterator fgi; + for (fgi = scope->_functions.begin(); fgi != scope->_functions.end(); ++fgi) { + CPPFunctionGroup *fgroup = fgi->second; + + CPPFunctionGroup::Instances::iterator ii; + for (ii = fgroup->_instances.begin(); ii != fgroup->_instances.end(); ++ii) { + CPPInstance *inst = (*ii); + CPPFunctionType *ftype = inst->_type->as_function_type(); + if (ftype == NULL) { + continue; + } + if (inst->_storage_class & CPPInstance::SC_explicit) { + // Skip it if it is marked not to allow coercion. + continue; + } + + if (inst->_vis > min_vis) { + // Not published. + continue; + } + + CPPParameterList::Parameters ¶ms = ftype->_parameters->_parameters; + if (params.size() == 0) { + // It's useless if it doesn't take any parameters. + continue; + } + + if (ftype->_flags & CPPFunctionType::F_constructor) { + if (params.size() == 1 && + TypeManager::unwrap(params[0]->_type) == type) { + // Skip a copy constructor. + continue; + } else { + return 2; + } + + } else if (fgroup->_name == "make" && (inst->_storage_class & CPPInstance::SC_static) != 0) { + if (TypeManager::is_const_pointer_or_ref(ftype->_return_type)) { + result = 1; + } else { + return 2; + } + } + } + } + + return result; +} + +////////////////////////////////////////////// +// Function : is_remap_coercion_possible +////////////////////////////////////////////// +bool InterfaceMakerPythonNative:: +is_remap_coercion_possible(FunctionRemap *remap) { + if (remap == NULL) { + return false; + } + + int pn = 0; + if (remap->_has_this) { + // Skip the "this" parameter. It's never coercible. + ++pn; + } + while (pn < (int)remap->_parameters.size()) { + CPPType *type = remap->_parameters[pn]._remap->get_new_type(); + + if (TypeManager::is_char_pointer(type)) { + } else if (TypeManager::is_wchar_pointer(type)) { + } else if (TypeManager::is_pointer_to_PyObject(type)) { + } else if (TypeManager::is_pointer_to_Py_buffer(type)) { + } else if (TypeManager::is_pointer_to_simple(type)) { + } else if (TypeManager::is_pointer(type)) { + // This is a pointer to an object, so we + // might be able to coerce a parameter to it. + CPPType *obj_type = TypeManager::unwrap(TypeManager::resolve_type(type)); + if (has_coerce_constructor(obj_type->as_struct_type()) > 0) { + // It has a coercion constructor, so go for it. + return true; + } + break; + } + ++pn; + } + + return false; +} + //////////////////////////////////////////////////////////////////////// // Function : is_function_legal //////////////////////////////////////////////////////////////////////// @@ -4450,7 +6543,7 @@ HasAGetClassTypeFunction(const InterrogateType &itype_class) { if (cppfunc != NULL && cppfunc->_return_type != NULL && cppfunc->_parameters != NULL) { CPPType *ret_type = TypeManager::unwrap(cppfunc->_return_type); - if (TypeManager::is_struct(ret_type) && + if (TypeManager::is_struct(ret_type) && ret_type->get_simple_name() == "TypeHandle") { if (cppfunc->_parameters->_parameters.size() == 0) { return true; @@ -4678,11 +6771,12 @@ NeedsARichCompareFunction(const InterrogateType &itype_class) { // string, following the trailing quotation mark. //////////////////////////////////////////////////////////////////// void InterfaceMakerPythonNative:: -output_quoted(ostream &out, int indent_level, const std::string &str) { - indent(out, indent_level) +output_quoted(ostream &out, int indent_level, const std::string &str, + bool first_line) { + indent(out, (first_line ? indent_level : 0)) << '"'; std::string::const_iterator si; - for (si = str.begin(); si != str.end(); ++si) { + for (si = str.begin(); si != str.end();) { switch (*si) { case '"': case '\\': @@ -4690,10 +6784,14 @@ output_quoted(ostream &out, int indent_level, const std::string &str) { break; case '\n': - out << "\\n\"\n"; + out << "\\n\""; + if (++si == str.end()) { + return; + } + out << "\n"; indent(out, indent_level) << '"'; - break; + continue; default: if (!isprint(*si)) { @@ -4703,6 +6801,7 @@ output_quoted(ostream &out, int indent_level, const std::string &str) { out << *si; } } + ++si; } out << '"'; } diff --git a/dtool/src/interrogate/interfaceMakerPythonNative.h b/dtool/src/interrogate/interfaceMakerPythonNative.h index 7c7c35b317..7288f8ce57 100644 --- a/dtool/src/interrogate/interfaceMakerPythonNative.h +++ b/dtool/src/interrogate/interfaceMakerPythonNative.h @@ -31,32 +31,33 @@ class InterfaceMakerPythonNative : public InterfaceMakerPython { public: InterfaceMakerPythonNative(InterrogateModuleDef *def); virtual ~InterfaceMakerPythonNative(); - - + + virtual void write_prototypes(ostream &out, ostream *out_h); void write_prototypes_class(ostream &out, ostream *out_h, Object *obj) ; void write_prototypes_class_external(ostream &out, Object *obj); - + virtual void write_functions(ostream &out); - + virtual void write_module(ostream &out, ostream *out_h, InterrogateModuleDef *def); virtual void write_module_support(ostream &out, ostream *out_h, InterrogateModuleDef *def); - - void write_module_class(ostream &out, Object *cls); - virtual void write_sub_module(ostream &out, Object *obj); - + + void write_module_class(ostream &out, Object *cls); + virtual void write_sub_module(ostream &out, Object *obj); + virtual bool synthesize_this_parameter(); - + virtual bool separate_overloading(); + virtual Object *record_object(TypeIndex type_index); - + protected: virtual string get_wrapper_prefix(); virtual string get_unique_prefix(); - virtual void record_function_wrapper(InterrogateFunction &ifunc, + virtual void record_function_wrapper(InterrogateFunction &ifunc, FunctionWrapperIndex wrapper_index); - + virtual void generate_wrappers(); - + private: // This enum defines the various prototypes that must be generated // for the specialty functions that Python requires, especially for @@ -65,7 +66,7 @@ private: WT_none, WT_no_params, WT_one_param, - WT_numeric_operator, + WT_binary_operator, WT_setattr, WT_getattr, WT_sequence_getitem, @@ -76,8 +77,34 @@ private: WT_getbuffer, WT_releasebuffer, WT_iter_next, - WT_one_or_two_params, WT_ternary_operator, + WT_inplace_binary_operator, + WT_inplace_ternary_operator, + WT_traverse, + }; + + // This enum is passed to the wrapper generation functions to indicate + // what sort of values the wrapper function is expected to return. + enum ReturnFlags { + // -1 on failure, 0 on success. + RF_int = 0x100, + + // Returns the actual return value as PyObject*. + RF_pyobject = 0x010, + + // Returns a reference to self. + RF_self = 0x020, + + // Assign to the coerced argument, in the case of a coercion constructor. + RF_coerced = 0x040, + + // These indicate what should be returned on error. + RF_err_notimplemented = 0x002, + RF_err_null = 0x004, + RF_err_false = 0x008, + + // Decref temporary args object before returning. + RF_decref_args = 0x200, }; class SlottedFunctionDef { @@ -85,40 +112,59 @@ private: string _answer_location; WrapperType _wrapper_type; int _min_version; + Function *_func; + string _wrapper_name; + set _remaps; }; - static bool get_slotted_function_def(Object *obj, Function *func, SlottedFunctionDef &def); + typedef std::map SlottedFunctions; + + static bool get_slotted_function_def(Object *obj, Function *func, FunctionRemap *remap, SlottedFunctionDef &def); + static void write_function_slot(ostream &out, int indent_level, + const SlottedFunctions &slots, + const string &slot, const string &def = "0"); void write_prototype_for_name(ostream &out, Function *func, const std::string &name); void write_prototype_for(ostream &out, Function *func); void write_function_for_top(ostream &out, Object *obj, Function *func); - void write_function_for_name(ostream &out, Object *obj, Function *func, - const std::string &name, - bool coercion_allowed, bool &coercion_attempted, - ArgsType args_type, bool return_int, bool write_comment); - void write_function_forset(ostream &out, Object *obj, Function *func, - std::set &remaps, string &expected_params, - int indent_level, bool inplace, - bool coercion_allowed, bool &coercion_attempted, - ArgsType args_type, bool return_int, + void write_function_for_name(ostream &out, Object *obj, + const Function::Remaps &remaps, + const std::string &name, string &expected_params, + bool coercion_allowed, + ArgsType args_type, int return_flags); + void write_coerce_constructor(ostream &out, Object *obj, bool is_const); + + int collapse_default_remaps(std::map > &map_sets, + int max_required_args); + + void write_function_forset(ostream &out, + const std::set &remaps, + int min_num_args, int max_num_args, + string &expected_params, int indent_level, + bool coercion_allowed, bool report_errors, + ArgsType args_type, int return_flags, + bool check_exceptions = true, + bool verify_const = true, const string &first_expr = string()); - void write_function_instance(ostream &out, Object *obj, Function *func, - FunctionRemap *remap, string &expected_params, - int indent_level, bool is_inplace, - bool coercion_allowed, bool &coercion_attempted, - ArgsType args_type, bool return_int, + void write_function_instance(ostream &out, FunctionRemap *remap, + int min_num_args, int max_num_args, + string &expected_params, int indent_level, + bool coercion_allowed, bool report_errors, + ArgsType args_type, int return_flags, + bool check_exceptions = true, const string &first_pexpr = string()); + void error_return(ostream &out, int indent_level, int return_flags); + void error_raise_return(ostream &out, int indent_level, int return_flags, + const string &exc_type, const string &message, + const string &format_args = ""); void pack_return_value(ostream &out, int indent_level, FunctionRemap *remap, - const std::string &return_expr, bool in_place); - void pack_python_value(ostream &out, int indent_level, FunctionRemap *remap, - ParameterRemap *return_type, const std::string &return_expr, - const std::string &assign_expr, bool in_place); + std::string return_expr); void write_make_seq(ostream &out, Object *obj, const std::string &ClassName, - MakeSeq *make_seq); + const std::string &cClassName, MakeSeq *make_seq); void write_class_prototypes(ostream &out) ; void write_class_declarations(ostream &out, ostream *out_h, Object *obj); @@ -126,7 +172,9 @@ private: public: bool is_remap_legal(FunctionRemap *remap); - bool is_function_legal( Function *func); + int has_coerce_constructor(CPPStructType *type); + bool is_remap_coercion_possible(FunctionRemap *remap); + bool is_function_legal(Function *func); bool is_cpp_type_legal(CPPType *ctype); bool isExportThisRun(CPPType *ctype); bool isExportThisRun(Function *func); @@ -145,17 +193,18 @@ public: void get_valid_child_classes(std::map &answer, CPPStructType *inclass, const std::string &upcast_seed = "", bool can_downcast = true); bool DoesInheritFromIsClass(const CPPStructType * inclass, const std::string &name); bool IsPandaTypedObject(CPPStructType * inclass) { return DoesInheritFromIsClass(inclass,"TypedObject"); }; - void write_python_instance(ostream &out, int indent_level, const std::string &return_expr, const std::string &assign_expr, std::string &owns_memory_flag, const std::string &class_name, CPPType *ctype, bool inplace, const std::string &const_flag); + void write_python_instance(ostream &out, int indent_level, const std::string &return_expr, bool owns_memory, const std::string &class_name, CPPType *ctype, bool is_const); string HasAGetKeyFunction(const InterrogateType &itype_class); bool HasAGetClassTypeFunction(const InterrogateType &itype_class); int NeedsAStrFunction(const InterrogateType &itype_class); int NeedsAReprFunction(const InterrogateType &itype_class); bool NeedsARichCompareFunction(const InterrogateType &itype_class); - void output_quoted(ostream &out, int indent_level, const std::string &str); - + void output_quoted(ostream &out, int indent_level, const std::string &str, + bool first_line=true); + // stash the forward declarations for this compile pass.. - std::set _external_imports; + std::set _external_imports; }; #endif diff --git a/dtool/src/interrogate/interrogate.cxx b/dtool/src/interrogate/interrogate.cxx index 4649cafa9f..15abba6ac2 100644 --- a/dtool/src/interrogate/interrogate.cxx +++ b/dtool/src/interrogate/interrogate.cxx @@ -46,6 +46,7 @@ bool save_unique_names = false; bool no_database = false; bool generate_spam = false; bool left_inheritance_requires_upcast = true; +bool mangle_names = true; CPPVisibility min_vis = V_published; string library_name; string module_name; diff --git a/dtool/src/interrogate/interrogate.h b/dtool/src/interrogate/interrogate.h index ec05f5f3eb..a7d6ed275d 100644 --- a/dtool/src/interrogate/interrogate.h +++ b/dtool/src/interrogate/interrogate.h @@ -43,9 +43,9 @@ extern bool save_unique_names; extern bool no_database; extern bool generate_spam; extern bool left_inheritance_requires_upcast; +extern bool mangle_names; extern CPPVisibility min_vis; extern string library_name; extern string module_name; #endif - diff --git a/dtool/src/interrogate/interrogateBuilder.cxx b/dtool/src/interrogate/interrogateBuilder.cxx index c46b9f9c1c..9be10dc81f 100644 --- a/dtool/src/interrogate/interrogateBuilder.cxx +++ b/dtool/src/interrogate/interrogateBuilder.cxx @@ -40,7 +40,7 @@ #include "cppExtensionType.h" #include "cppStructType.h" #include "cppExpression.h" -#include "cppTypedef.h" +#include "cppTypedefType.h" #include "cppTypeDeclaration.h" #include "cppEnumType.h" #include "cppCommentBlock.h" @@ -286,7 +286,8 @@ build() { } } else if ((*di)->get_subtype() == CPPDeclaration::ST_typedef) { - CPPTypedef *tdef = (*di)->as_typedef(); + CPPTypedefType *tdef = (*di)->as_typedef_type(); + if (tdef->_type->get_subtype() == CPPDeclaration::ST_struct) { // A typedef counts as a declaration. This lets us pick up // most template instantiations. @@ -295,13 +296,16 @@ build() { scan_struct_type(struct_type); } + scan_typedef_type(tdef); + } else if ((*di)->get_subtype() == CPPDeclaration::ST_type_declaration) { CPPType *type = (*di)->as_type_declaration()->_type; + type->_vis = (*di)->_vis; - if (type->get_subtype() == CPPDeclaration::ST_struct) - { - CPPStructType *struct_type =type->as_type()->resolve_type(&parser, &parser)->as_struct_type(); + if (type->get_subtype() == CPPDeclaration::ST_struct) { + CPPStructType *struct_type = + type->as_type()->resolve_type(&parser, &parser)->as_struct_type(); scan_struct_type(struct_type); } else if (type->get_subtype() == CPPDeclaration::ST_enum) { @@ -329,7 +333,8 @@ build() { // Description: Generates all the code necessary to the indicated // output stream. //////////////////////////////////////////////////////////////////// -void InterrogateBuilder::write_code(ostream &out_code,ostream * out_include, InterrogateModuleDef *def) { +void InterrogateBuilder:: +write_code(ostream &out_code,ostream * out_include, InterrogateModuleDef *def) { typedef vector InterfaceMakers; InterfaceMakers makers; @@ -394,9 +399,10 @@ void InterrogateBuilder::write_code(ostream &out_code,ostream * out_include, Int } declaration_bodies << "#include \"py_panda.h\"\n"; declaration_bodies << "#include \"extension.h\"\n"; + declaration_bodies << "#include \"dcast.h\"\n"; } declaration_bodies << "\n"; - + IncludeFiles::const_iterator ifi; for (ifi = _include_files.begin(); ifi != _include_files.end(); @@ -413,7 +419,6 @@ void InterrogateBuilder::write_code(ostream &out_code,ostream * out_include, Int } declaration_bodies << "\n"; - for (mi = makers.begin(); mi != makers.end(); ++mi) { (*mi)->write_includes(declaration_bodies); } @@ -427,8 +432,6 @@ void InterrogateBuilder::write_code(ostream &out_code,ostream * out_include, Int declaration_bodies << "\n"; - - // And now the prototypes. for (mi = makers.begin(); mi != makers.end(); ++mi) { (*mi)->write_prototypes(declaration_bodies,out_include); @@ -438,21 +441,19 @@ void InterrogateBuilder::write_code(ostream &out_code,ostream * out_include, Int // if(out_include != NULL) // (*out_include) << declaration_bodies.str(); // else - out_code << declaration_bodies.str(); + out_code << declaration_bodies.str(); - // Followed by the function bodies. out_code << function_bodies.str() << "\n"; - for (mi = makers.begin(); mi != makers.end(); ++mi) { - (*mi)->write_module_support(out_code,out_include,def); - } - + for (mi = makers.begin(); mi != makers.end(); ++mi) { + (*mi)->write_module_support(out_code, out_include, def); + } if (output_module_specific) { // Output whatever stuff we should output if this were a module. for (mi = makers.begin(); mi != makers.end(); ++mi) { - (*mi)->write_module(out_code,out_include, def); + (*mi)->write_module(out_code, out_include, def); } } @@ -461,7 +462,7 @@ void InterrogateBuilder::write_code(ostream &out_code,ostream * out_include, Int for (mi = makers.begin(); mi != makers.end(); ++mi) { (*mi)->get_function_remaps(remaps); } - + // Make sure all of the function wrappers appear first in the set of // indices, and that they occupy consecutive index numbers, so we // can build a simple array of function pointers by index. @@ -504,7 +505,6 @@ void InterrogateBuilder::write_code(ostream &out_code,ostream * out_include, Int out_code << "};\n\n"; } - if (save_unique_names) { // Write out the table of unique names, in no particular order. out_code << "static InterrogateUniqueNameDef _in_unique_names[" @@ -518,9 +518,6 @@ void InterrogateBuilder::write_code(ostream &out_code,ostream * out_include, Int out_code << "};\n\n"; } -//if(1==2) -{ - if (!no_database) { // Now build the module definition structure to add ourselves to // the global interrogate database. @@ -544,7 +541,7 @@ void InterrogateBuilder::write_code(ostream &out_code,ostream * out_include, Int << " 0, /* num_unique_names */\n"; } - if (output_function_pointers) { + if (output_function_pointers) { out_code << " _in_fptrs,\n" << " " << num_wrappers << ", /* num_fptrs */\n"; } else { @@ -565,7 +562,6 @@ void InterrogateBuilder::write_code(ostream &out_code,ostream * out_include, Int << "}\n\n"; } } -} //////////////////////////////////////////////////////////////////// // Function: InterrogateBuilder::make_module_def @@ -887,6 +883,15 @@ in_ignoreinvolved(CPPType *type) const { return false; } + case CPPDeclaration::ST_typedef: + { + if (in_ignoreinvolved(type->get_simple_name())) { + return true; + } + CPPTypedefType *tdef = type->as_typedef_type(); + return in_ignoreinvolved(tdef->_type); + } + default: return in_ignoreinvolved(type->get_simple_name()); } @@ -1145,7 +1150,7 @@ scan_function(CPPInstance *function) { } get_function(function, "", - (CPPStructType *)NULL, scope, + (CPPStructType *)NULL, scope, InterrogateFunction::F_global); } @@ -1181,8 +1186,7 @@ scan_struct_type(CPPStructType *type) { // Check if any of the members are exported. If none of them are, // and the type itself is not marked for export, then never mind. - if (type->_vis > min_vis) - { + if (type->_vis > min_vis) { CPPScope *scope = type->_scope; bool any_exported = false; @@ -1192,6 +1196,7 @@ scan_struct_type(CPPStructType *type) { ++di) { if ((*di)->_vis <= min_vis) { any_exported = true; + break; } } @@ -1241,6 +1246,93 @@ scan_enum_type(CPPEnumType *type) { get_type(type, true); } +//////////////////////////////////////////////////////////////////// +// Function: InterrogateBuilder::scan_typedef_type +// Access: Private +// Description: Adds the indicated typedef type to the database, if +// warranted. +//////////////////////////////////////////////////////////////////// +void InterrogateBuilder:: +scan_typedef_type(CPPTypedefType *type) { + if (type == (CPPTypedefType *)NULL) { + return; + } + + // A typedef cannot be a template declaration. + assert(!type->is_template()); + + if (type->_file.is_c_file()) { + // This type declaration appears in a .C file. We can only export + // types defined in a .h file. + return; + } + + if (type->_file._source != CPPFile::S_local || + in_ignorefile(type->_file._filename_as_referenced)) { + // The type is defined in some other package or in an + // ignorable file. + return; + } + + // Do we require explicitly placing BEGIN_PUBLISH/END_PUBLISH + // blocks around typedefs for them to be exported? My thinking is + // that we shoudn't, for now, since we don't require it for structs + // either (we only require it to have published methods). + //if (type->_vis > min_vis) { + // // The wrapped type is not marked to be exported. + // return; + //} + + // Find out what this typedef points to. + CPPType *wrapped_type = type->_type; + bool forced = in_forcetype(wrapped_type->get_local_name(&parser)); + + while (wrapped_type->get_subtype() == CPPDeclaration::ST_typedef) { + wrapped_type = wrapped_type->as_typedef_type()->_type; + forced = forced || in_forcetype(wrapped_type->get_local_name(&parser)); + } + + CPPStructType *struct_type = wrapped_type->as_struct_type(); + if (struct_type == (CPPStructType *)NULL) { + // We only export typedefs to structs, for now. + return; + } + + // Always export typedefs pointing to forced types. + if (!forced) { + if (wrapped_type->_file._source != CPPFile::S_local || + in_ignorefile(wrapped_type->_file._filename_as_referenced)) { + // The wrapped type is defined in some other package or + // in an ignorable file. + return; + } + + // Check if any of the wrapped type's members are published. + // If none of them are, and the wrapped type itself is not + // marked for export, then never mind. + if (struct_type->_vis > min_vis) { + CPPScope *scope = struct_type->_scope; + + bool any_exported = false; + CPPScope::Declarations::const_iterator di; + for (di = scope->_declarations.begin(); + di != scope->_declarations.end() && !any_exported; + ++di) { + if ((*di)->_vis <= min_vis) { + any_exported = true; + break; + } + } + + if (!any_exported) { + return; + } + } + } + + get_type(type, true); +} + //////////////////////////////////////////////////////////////////// // Function: InterrogateBuilder::scan_manifest // Access: Private @@ -1683,6 +1775,11 @@ get_function(CPPInstance *function, string description, return 0; } + TypeIndex class_index = 0; + if (struct_type != (CPPStructType *)NULL) { + class_index = get_type(struct_type, false); + } + string function_name = TypeManager::get_function_name(function); string function_signature = TypeManager::get_function_signature(function); @@ -1698,18 +1795,17 @@ get_function(CPPInstance *function, string description, _functions_by_name.find(function_name); if (tni != _functions_by_name.end()) { FunctionIndex index = (*tni).second; + // It's already here, so update the flags. InterrogateFunction &ifunction = InterrogateDatabase::get_ptr()->update_function(index); - // Not 100% sure why, but there's a case where this happens, - // in a case where a typedef shadowed an actual type. ~rdb nassertr(&ifunction != NULL, 0); ifunction._flags |= flags; // Also, make sure this particular signature is defined. - pair result = + pair result = ifunction._instances->insert(InterrogateFunction::Instances::value_type(function_signature, function)); InterrogateFunction::Instances::iterator ii = result.first; @@ -1764,7 +1860,7 @@ get_function(CPPInstance *function, string description, if (struct_type != (CPPStructType *)NULL) { // The function is a method. ifunction->_flags |= InterrogateFunction::F_method; - ifunction->_class = get_type(struct_type, false); + ifunction->_class = class_index; } if (ftype->_flags & CPPFunctionType::F_unary_op) { @@ -1974,7 +2070,7 @@ get_type(CPPType *type, bool global) { if (true_name.empty()) { // Whoops, it's an anonymous type. That's okay, because we'll // usually only encounter them once anyway, so let's go ahead and - // define it without checking in _types_by_name. + // define it without checking _types_by_name first. } else { TypesByName::const_iterator tni = _types_by_name.find(true_name); @@ -2044,29 +2140,39 @@ get_type(CPPType *type, bool global) { } } - CPPExtensionType *ext_type = type->as_extension_type(); - if (ext_type != (CPPExtensionType *)NULL) { - // If it's an extension type of some kind, it might be scoped. + CPPScope *scope = NULL; + // If it's an extension type or typedef, it might be scoped. + if (CPPTypedefType *td_type = type->as_typedef_type()) { + scope = td_type->_ident->get_scope(&parser, &parser); + + } else if (CPPExtensionType *ext_type = type->as_extension_type()) { if (ext_type->_ident != (CPPIdentifier *)NULL) { - CPPScope *scope = ext_type->_ident->get_scope(&parser, &parser); - while (scope->as_template_scope() != (CPPTemplateScope *)NULL) - { - assert(scope->get_parent_scope() != scope); - scope = scope->get_parent_scope(); - assert(scope != (CPPScope *)NULL); - } - itype._cppscope = scope; + scope = ext_type->_ident->get_scope(&parser, &parser); - if (scope != &parser) { - // We're scoped! - itype._scoped_name = - descope(scope->get_local_name(&parser) + "::" + itype._name); - CPPStructType *struct_type = scope->get_struct_type(); + } else if (CPPEnumType *enum_type = ext_type->as_enum_type()) { + // Special case for anonymous enums. + scope = enum_type->_parent_scope; + } - if (struct_type != (CPPStructType *)NULL) { - itype._flags |= InterrogateType::F_nested; - itype._outer_class = get_type(struct_type, false); - } + } + + if (scope != (CPPScope *)NULL) { + while (scope->as_template_scope() != (CPPTemplateScope *)NULL) { + assert(scope->get_parent_scope() != scope); + scope = scope->get_parent_scope(); + assert(scope != (CPPScope *)NULL); + } + itype._cppscope = scope; + + if (scope != &parser) { + // We're scoped! + itype._scoped_name = + descope(scope->get_local_name(&parser) + "::" + itype._name); + CPPStructType *struct_type = scope->get_struct_type(); + + if (struct_type != (CPPStructType *)NULL) { + itype._flags |= InterrogateType::F_nested; + itype._outer_class = get_type(struct_type, false); } } } @@ -2092,8 +2198,15 @@ get_type(CPPType *type, bool global) { } else if (type->as_extension_type() != (CPPExtensionType *)NULL) { define_extension_type(itype, type->as_extension_type()); + } else if (type->as_typedef_type() != (CPPTypedefType *)NULL) { + define_typedef_type(itype, type->as_typedef_type()); + + } else if (type->as_array_type() != (CPPArrayType *)NULL) { + define_array_type(itype, type->as_array_type()); + } else { - // nout << "Attempt to define invalid type " << true_name << "\n"; + nout << "Attempt to define invalid type " << *type + << " (subtype " << type->get_subtype() << ")\n"; // Remove the type from the database. InterrogateDatabase::get_ptr()->remove_type(index); @@ -2129,6 +2242,16 @@ define_atomic_type(InterrogateType &itype, CPPSimpleType *cpptype) { itype._atomic_token = AT_int; break; + case CPPSimpleType::T_char16_t: + itype._flags |= InterrogateType::F_unsigned; + itype._atomic_token = AT_int; + break; + + case CPPSimpleType::T_char32_t: + itype._flags |= InterrogateType::F_unsigned; + itype._atomic_token = AT_int; + break; + case CPPSimpleType::T_int: if ((cpptype->_flags & CPPSimpleType::F_longlong) != 0) { itype._atomic_token = AT_longlong; @@ -2150,7 +2273,8 @@ define_atomic_type(InterrogateType &itype, CPPSimpleType *cpptype) { break; default: - nout << "Invalid CPPSimpleType: " << (int)cpptype->_type << "\n"; + nout << "Type \"" << *cpptype << "\" has invalid CPPSimpleType: " + << (int)cpptype->_type << "\n"; itype._atomic_token = AT_not_atomic; } @@ -2204,7 +2328,7 @@ define_struct_type(InterrogateType &itype, CPPStructType *cpptype, TypeIndex type_index, bool forced) { if (cpptype->get_simple_name().empty()) { // If the type has no name, forget it. We don't export anonymous - // types. + // structs. return; } @@ -2265,23 +2389,25 @@ define_struct_type(InterrogateType &itype, CPPStructType *cpptype, // A struct type should always be global. itype._flags |= InterrogateType::F_global; - + CPPScope *scope = cpptype->_scope; - + CPPStructType::Derivation::const_iterator bi; for (bi = cpptype->_derivation.begin(); bi != cpptype->_derivation.end(); ++bi) { - const CPPStructType::Base &base = (*bi); - if (base._vis <= V_public) - { + const CPPStructType::Base &base = (*bi); + if (base._vis <= V_public) { CPPType *base_type = TypeManager::resolve_type(base._base, scope); TypeIndex base_index = get_type(base_type, true); - if (base_index == 0) { - nout << *cpptype << " reports a derivation from an invalid type.\n"; + if (base_type != NULL) { + nout << *cpptype << " reports a derivation from invalid type " << *base_type << ".\n"; + } else { + nout << *cpptype << " reports a derivation from an invalid type.\n"; + } } else { InterrogateType::Derivation d; @@ -2289,7 +2415,7 @@ define_struct_type(InterrogateType &itype, CPPStructType *cpptype, d._base = base_index; d._upcast = 0; d._downcast = 0; - + // Do we need to synthesize upcast and downcast functions? bool generate_casts = false; @@ -2297,12 +2423,12 @@ define_struct_type(InterrogateType &itype, CPPStructType *cpptype, if (base._is_virtual) { // We do in the presence of virtual inheritance. generate_casts = true; - + } else if (bi != cpptype->_derivation.begin()) { // Or if we're not talking about the leftmost fork of multiple // inheritance. generate_casts = true; - + } else if (cpptype->_derivation.size() != 1 && left_inheritance_requires_upcast) { // Or even if we are the leftmost fork of multiple @@ -2318,13 +2444,11 @@ define_struct_type(InterrogateType &itype, CPPStructType *cpptype, // pointer, while the parent class won't). generate_casts = true; } - - if (generate_casts) { - + if (generate_casts) { d._upcast = get_cast_function(base_type, cpptype, "upcast"); d._flags |= InterrogateType::DF_upcast; - + if (base._is_virtual) { // If this is a virtual inheritance, we can't write a // downcast. @@ -2371,8 +2495,9 @@ define_struct_type(InterrogateType &itype, CPPStructType *cpptype, CPPExtensionType *nested_type = type->as_extension_type(); assert(nested_type != (CPPExtensionType *)NULL); - // Only try to export named types. - if (nested_type->_ident != (CPPIdentifier *)NULL) { + // For now, we don't allow anonymous structs. + if (nested_type->_ident != (CPPIdentifier *)NULL || + nested_type->as_enum_type() != (CPPEnumType *)NULL) { TypeIndex nested_index = get_type(nested_type, false); itype._nested_types.push_back(nested_index); } @@ -2382,11 +2507,31 @@ define_struct_type(InterrogateType &itype, CPPStructType *cpptype, CPPType *type = (*di)->as_enum_type(); // An anonymous enum type. - if ((*di)->_vis <= min_vis) { + if (type->_vis <= min_vis) { TypeIndex nested_index = get_type(type, false); itype._nested_types.push_back(nested_index); } + } else if ((*di)->get_subtype() == CPPDeclaration::ST_typedef) { + CPPTypedefType *type = (*di)->as_typedef_type(); + + // A nested typedef. Unwrap it to find out what it's pointing to. + CPPType *wrapped_type = type->_type; + + while (wrapped_type->get_subtype() == CPPDeclaration::ST_typedef) { + wrapped_type = wrapped_type->as_typedef_type()->_type; + } + + CPPStructType *struct_type = wrapped_type->as_struct_type(); + if (struct_type != (CPPStructType *)NULL) { + // We only export typedefs to structs, for now. + + if (type->_vis <= min_vis) { + TypeIndex nested_index = get_type(type, false); + itype._nested_types.push_back(nested_index); + } + } + } else if ((*di)->get_subtype() == CPPDeclaration::ST_make_property) { ElementIndex element_index = get_make_property((*di)->as_make_property(), cpptype); itype._elements.push_back(element_index); @@ -2483,7 +2628,7 @@ update_function_comment(CPPInstance *function, CPPScope *scope) { ifunction._comment += comment; // Also update the particular wrapper comment. - InterrogateFunction::Instances::iterator ii = + InterrogateFunction::Instances::iterator ii = ifunction._instances->find(function_signature); if (ii != ifunction._instances->end()) { if ((*ii).second->_leading_comment == NULL || @@ -2505,8 +2650,7 @@ void InterrogateBuilder:: define_method(CPPFunctionGroup *fgroup, InterrogateType &itype, CPPStructType *struct_type, CPPScope *scope) { CPPFunctionGroup::Instances::const_iterator fi; - for (fi = fgroup->_instances.begin(); fi != fgroup->_instances.end(); ++fi) - { + for (fi = fgroup->_instances.begin(); fi != fgroup->_instances.end(); ++fi) { CPPInstance *function = (*fi); define_method(function, itype, struct_type, scope); } @@ -2642,7 +2786,7 @@ void InterrogateBuilder:: define_enum_type(InterrogateType &itype, CPPEnumType *cpptype) { itype._flags |= InterrogateType::F_enum; - CPPScope *scope = &parser; + CPPScope *scope = cpptype->_parent_scope; if (cpptype->_ident != (CPPIdentifier *)NULL) { scope = cpptype->_ident->get_scope(&parser, &parser); } @@ -2683,7 +2827,15 @@ define_enum_type(InterrogateType &itype, CPPEnumType *cpptype) { if (element->_initializer != (CPPExpression *)NULL) { CPPExpression::Result result = element->_initializer->evaluate(); - next_value = result.as_integer(); + + if (result._type == CPPExpression::RT_error) { + nout << "enum value "; + element->output(nout, 0, &parser, true); + nout << " has invalid definition!\n"; + return; + } else { + next_value = result.as_integer(); + } } evalue._value = next_value; itype._enum_values.push_back(evalue); @@ -2692,6 +2844,30 @@ define_enum_type(InterrogateType &itype, CPPEnumType *cpptype) { } } +//////////////////////////////////////////////////////////////////// +// Function: InterrogateBuilder::define_typedef_type +// Access: Private +// Description: Builds up a definition for the indicated typedef. +//////////////////////////////////////////////////////////////////// +void InterrogateBuilder:: +define_typedef_type(InterrogateType &itype, CPPTypedefType *cpptype) { + itype._flags |= InterrogateType::F_typedef; + itype._wrapped_type = get_type(cpptype->_type, false); +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateBuilder::define_array_type +// Access: Private +// Description: Builds up a definition for the indicated wrapped type. +//////////////////////////////////////////////////////////////////// +void InterrogateBuilder:: +define_array_type(InterrogateType &itype, CPPArrayType *cpptype) { + itype._flags |= InterrogateType::F_array; + itype._wrapped_type = get_type(cpptype->_element_type, false); + + itype._array_size = cpptype->_bounds->evaluate().as_integer(); +} + //////////////////////////////////////////////////////////////////// // Function: InterrogateBuilder::define_extension_type // Access: Private diff --git a/dtool/src/interrogate/interrogateBuilder.h b/dtool/src/interrogate/interrogateBuilder.h index 63a0814abb..a11b5cfaa5 100644 --- a/dtool/src/interrogate/interrogateBuilder.h +++ b/dtool/src/interrogate/interrogateBuilder.h @@ -33,6 +33,8 @@ class CPPConstType; class CPPExtensionType; class CPPStructType; class CPPEnumType; +class CPPTypedefType; +class CPPArrayType; class CPPFunctionType; class CPPScope; class CPPIdentifier; @@ -92,6 +94,7 @@ public: void scan_function(CPPInstance *function); void scan_struct_type(CPPStructType *type); void scan_enum_type(CPPEnumType *type); + void scan_typedef_type(CPPTypedefType *type); void scan_manifest(CPPManifest *manifest); ElementIndex scan_element(CPPInstance *element, CPPStructType *struct_type, CPPScope *scope); @@ -128,6 +131,8 @@ public: void define_method(CPPInstance *function, InterrogateType &itype, CPPStructType *struct_type, CPPScope *scope); void define_enum_type(InterrogateType &itype, CPPEnumType *cpptype); + void define_typedef_type(InterrogateType &itype, CPPTypedefType *cpptype); + void define_array_type(InterrogateType &itype, CPPArrayType *cpptype); void define_extension_type(InterrogateType &itype, CPPExtensionType *cpptype); diff --git a/dtool/src/interrogate/parameterRemapConcreteToPointer.cxx b/dtool/src/interrogate/parameterRemapConcreteToPointer.cxx index d1dd7485fd..92739e96eb 100644 --- a/dtool/src/interrogate/parameterRemapConcreteToPointer.cxx +++ b/dtool/src/interrogate/parameterRemapConcreteToPointer.cxx @@ -43,7 +43,14 @@ ParameterRemapConcreteToPointer(CPPType *orig_type) : //////////////////////////////////////////////////////////////////// void ParameterRemapConcreteToPointer:: pass_parameter(ostream &out, const string &variable_name) { - out << "*" << variable_name; + if (variable_name.size() > 1 && variable_name[0] == '&') { + // Prevent generating something like *¶m + // Also, if this is really some local type, we can presumably + // just move it? + out << "MOVE(" << variable_name.substr(1) << ")"; + } else { + out << "*" << variable_name; + } } //////////////////////////////////////////////////////////////////// diff --git a/dtool/src/interrogate/parameterRemapReferenceToPointer.cxx b/dtool/src/interrogate/parameterRemapReferenceToPointer.cxx index 74e8f80822..ab7137e6bb 100644 --- a/dtool/src/interrogate/parameterRemapReferenceToPointer.cxx +++ b/dtool/src/interrogate/parameterRemapReferenceToPointer.cxx @@ -42,7 +42,16 @@ ParameterRemapReferenceToPointer(CPPType *orig_type) : //////////////////////////////////////////////////////////////////// void ParameterRemapReferenceToPointer:: pass_parameter(ostream &out, const string &variable_name) { - out << "*" << variable_name; + if (variable_name.size() > 1 && variable_name[0] == '&') { + // Prevent generating something like *¶m + // Also, if this is really some local type, we can presumably just + // move it? This is only relevant if this parameter is an rvalue + // reference, but CPPParser can't know that, and it might have an overload + // that takes an rvalue reference. It shouldn't hurt either way. + out << "MOVE(" << variable_name.substr(1) << ")"; + } else { + out << "*" << variable_name; + } } //////////////////////////////////////////////////////////////////// diff --git a/dtool/src/interrogate/parse_file.cxx b/dtool/src/interrogate/parse_file.cxx index 524461b9df..33d99917c6 100644 --- a/dtool/src/interrogate/parse_file.cxx +++ b/dtool/src/interrogate/parse_file.cxx @@ -16,7 +16,7 @@ #include "cppManifest.h" #include "cppStructType.h" #include "cppFunctionGroup.h" -#include "cppTypedef.h" +#include "cppTypedefType.h" #include "cppExpressionParser.h" #include "cppExpression.h" #include "cppType.h" @@ -166,7 +166,7 @@ show_data_members(const string &str) { } void -show_typedefs(const string &str) { +show_nested_types(const string &str) { CPPType *type = parser.parse_type(str); if (type == NULL) { cerr << "Invalid type: " << str << "\n"; @@ -182,12 +182,12 @@ show_typedefs(const string &str) { CPPScope *scope = stype->get_scope(); assert(scope != (CPPScope *)NULL); - cerr << "Typedefs in " << *stype << ":\n"; + cerr << "Nested types in " << *stype << ":\n"; - CPPScope::Typedefs::const_iterator ti; - for (ti = scope->_typedefs.begin(); ti != scope->_typedefs.end(); ++ti) { - CPPTypedef *td = (*ti).second; - cerr << " " << *td << "\n"; + CPPScope::Types::const_iterator ti; + for (ti = scope->_types.begin(); ti != scope->_types.end(); ++ti) { + CPPType *tp = (*ti).second; + cerr << " " << *tp << "\n"; } } @@ -286,8 +286,8 @@ main(int argc, char **argv) { show_methods(remainder); } else if (first_word == "members") { show_data_members(remainder); - } else if (first_word == "typedefs") { - show_typedefs(remainder); + } else if (first_word == "types") { + show_nested_types(remainder); } else { show_type_or_expression(str); } diff --git a/dtool/src/interrogate/typeManager.cxx b/dtool/src/interrogate/typeManager.cxx index c13fca7496..911ae81b36 100644 --- a/dtool/src/interrogate/typeManager.cxx +++ b/dtool/src/interrogate/typeManager.cxx @@ -15,20 +15,20 @@ #include "typeManager.h" #include "interrogate.h" -#include "cppFunctionType.h" -#include "cppFunctionGroup.h" -#include "cppParameterList.h" +#include "cppArrayType.h" #include "cppConstType.h" -#include "cppReferenceType.h" +#include "cppEnumType.h" +#include "cppFunctionGroup.h" +#include "cppFunctionType.h" +#include "cppParameterList.h" #include "cppPointerType.h" +#include "cppReferenceType.h" #include "cppSimpleType.h" #include "cppStructType.h" #include "cppTemplateScope.h" #include "cppTypeDeclaration.h" +#include "cppTypedefType.h" #include "pnotify.h" -#include "cppTypedef.h" -#include "cppEnumType.h" - //////////////////////////////////////////////////////////////////// // Function: TypeManager::resolve_type @@ -45,6 +45,7 @@ resolve_type(CPPType *type, CPPScope *scope) { scope = &parser; } + CPPType *orig_type = type; type = type->resolve_type(scope, &parser); string name = type->get_local_name(&parser); if (name.empty()) { @@ -52,14 +53,19 @@ resolve_type(CPPType *type, CPPScope *scope) { return type; } + // I think I fixed the bug; no need for the below hack any more. + return type; + +/* CPPType *new_type = parser.parse_type(name); if (new_type == (CPPType *)NULL) { - nout << "Type " << name << " is unknown to parser.\n"; + nout << "Type \"" << name << "\" (from " << *orig_type << ") is unknown to parser.\n"; } else { type = new_type->resolve_type(&parser, &parser); } return type; +*/ } //////////////////////////////////////////////////////////////////// @@ -96,6 +102,9 @@ is_assignable(CPPType *type) { return false; + case CPPDeclaration::ST_typedef: + return is_assignable(type->as_typedef_type()->_type); + default: return true; } @@ -117,6 +126,9 @@ is_reference(CPPType *type) { case CPPDeclaration::ST_reference: return is_pointable(type->as_reference_type()->_pointing_at); + case CPPDeclaration::ST_typedef: + return is_reference(type->as_typedef_type()->_type); + default: return false; } @@ -138,6 +150,9 @@ is_ref_to_anything(CPPType *type) { case CPPDeclaration::ST_reference: return true; + case CPPDeclaration::ST_typedef: + return is_ref_to_anything(type->as_typedef_type()->_type); + default: return false; } @@ -158,6 +173,9 @@ is_const_ref_to_anything(CPPType *type) { case CPPDeclaration::ST_reference: return is_const(type->as_reference_type()->_pointing_at); + case CPPDeclaration::ST_typedef: + return is_const_ref_to_anything(type->as_typedef_type()->_type); + default: return false; } @@ -178,6 +196,42 @@ is_const_pointer_to_anything(CPPType *type) { case CPPDeclaration::ST_pointer: return is_const(type->as_pointer_type()->_pointing_at); + case CPPDeclaration::ST_typedef: + return is_const_pointer_to_anything(type->as_typedef_type()->_type); + + default: + return false; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeManager::is_const_pointer_or_ref +// Access: Public, Static +// Description: Returns true if the indicated type is a non-const +// pointer or reference to something, false otherwise. +//////////////////////////////////////////////////////////////////// +bool TypeManager:: +is_const_pointer_or_ref(CPPType *type) { + switch (type->get_subtype()) { + case CPPDeclaration::ST_const: + return is_const_pointer_or_ref(type->as_const_type()->_wrapped_around); + + case CPPDeclaration::ST_pointer: + return is_const(type->as_pointer_type()->_pointing_at); + + case CPPDeclaration::ST_reference: + return is_const(type->as_reference_type()->_pointing_at); + + case CPPDeclaration::ST_typedef: + return is_const_pointer_or_ref(type->as_typedef_type()->_type); + + case CPPDeclaration::ST_struct: + if (type->get_simple_name() == "PointerTo") { + return false; + } else if (type->get_simple_name() == "ConstPointerTo") { + return true; + } + default: return false; } @@ -201,6 +255,16 @@ is_non_const_pointer_or_ref(CPPType *type) { case CPPDeclaration::ST_reference: return !is_const(type->as_reference_type()->_pointing_at); + case CPPDeclaration::ST_typedef: + return is_non_const_pointer_or_ref(type->as_typedef_type()->_type); + + case CPPDeclaration::ST_struct: + if (type->get_simple_name() == "PointerTo") { + return true; + } else if (type->get_simple_name() == "ConstPointerTo") { + return false; + } + default: return false; } @@ -221,6 +285,9 @@ is_pointer(CPPType *type) { case CPPDeclaration::ST_pointer: return is_pointable(type->as_pointer_type()->_pointing_at); + case CPPDeclaration::ST_typedef: + return is_pointer(type->as_typedef_type()->_type); + default: return false; } @@ -238,6 +305,9 @@ is_const(CPPType *type) { case CPPDeclaration::ST_const: return true; + case CPPDeclaration::ST_typedef: + return is_const(type->as_typedef_type()->_type); + default: return false; } @@ -259,6 +329,9 @@ is_struct(CPPType *type) { case CPPDeclaration::ST_extension: return true; + case CPPDeclaration::ST_typedef: + return is_struct(type->as_typedef_type()->_type); + default: return false; } @@ -279,6 +352,9 @@ is_enum(CPPType *type) { case CPPDeclaration::ST_const: return is_enum(type->as_const_type()->_wrapped_around); + case CPPDeclaration::ST_typedef: + return is_enum(type->as_typedef_type()->_type); + default: return false; } @@ -296,6 +372,9 @@ is_const_enum(CPPType *type) { case CPPDeclaration::ST_const: return is_enum(type->as_const_type()->_wrapped_around); + case CPPDeclaration::ST_typedef: + return is_enum(type->as_typedef_type()->_type); + default: return false; } @@ -313,6 +392,9 @@ is_const_ref_to_enum(CPPType *type) { case CPPDeclaration::ST_reference: return is_const_enum(type->as_reference_type()->_pointing_at); + case CPPDeclaration::ST_typedef: + return is_const_ref_to_enum(type->as_typedef_type()->_type); + default: return false; } @@ -331,11 +413,14 @@ is_simple(CPPType *type) { switch (type->get_subtype()) { case CPPDeclaration::ST_simple: case CPPDeclaration::ST_enum: - return true; + return !is_void(type); case CPPDeclaration::ST_const: return is_simple(type->as_const_type()->_wrapped_around); + case CPPDeclaration::ST_typedef: + return is_simple(type->as_typedef_type()->_type); + default: return false; } @@ -353,6 +438,9 @@ is_const_simple(CPPType *type) { case CPPDeclaration::ST_const: return is_simple(type->as_const_type()->_wrapped_around); + case CPPDeclaration::ST_typedef: + return is_const_simple(type->as_typedef_type()->_type); + default: return false; } @@ -371,6 +459,9 @@ is_const_ref_to_simple(CPPType *type) { case CPPDeclaration::ST_reference: return is_const_simple(type->as_reference_type()->_pointing_at); + case CPPDeclaration::ST_typedef: + return is_const_ref_to_simple(type->as_typedef_type()->_type); + default: return false; } @@ -389,6 +480,62 @@ is_ref_to_simple(CPPType *type) { case CPPDeclaration::ST_reference: return is_simple(type->as_reference_type()->_pointing_at); + case CPPDeclaration::ST_typedef: + return is_ref_to_simple(type->as_typedef_type()->_type); + + default: + return false; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeManager::is_simple_array +// Access: Public, Static +// Description: Returns true if the indicated type is an array of +// a simple type. +//////////////////////////////////////////////////////////////////// +bool TypeManager:: +is_simple_array(CPPType *type) { + switch (type->get_subtype()) { + case CPPDeclaration::ST_const: + return is_simple_array(type->as_const_type()->_wrapped_around); + + case CPPDeclaration::ST_array: + return is_simple(type->as_array_type()->_element_type); + + case CPPDeclaration::ST_typedef: + return is_simple_array(type->as_typedef_type()->_type); + + default: + return false; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeManager::is_pointer_to_simple +// Access: Public, Static +// Description: Returns true if the indicated type is a const or +// a non-constant pointer to a simple type. This could +// also be a reference to an array of the simple type. +//////////////////////////////////////////////////////////////////// +bool TypeManager:: +is_pointer_to_simple(CPPType *type) { + switch (type->get_subtype()) { + case CPPDeclaration::ST_const: + return is_pointer_to_simple(type->as_const_type()->_wrapped_around); + + case CPPDeclaration::ST_pointer: + return is_simple(type->as_pointer_type()->_pointing_at); + + case CPPDeclaration::ST_array: + return is_simple(type->as_array_type()->_element_type); + + case CPPDeclaration::ST_reference: + return is_pointer_to_simple(type->as_reference_type()->_pointing_at); + + case CPPDeclaration::ST_typedef: + return is_pointer_to_simple(type->as_typedef_type()->_type); + default: return false; } @@ -414,8 +561,11 @@ is_pointable(CPPType *type) { case CPPDeclaration::ST_struct: return true; - case CPPDeclaration::ST_simple: - return is_char(type); + //case CPPDeclaration::ST_simple: + // return is_char(type); + + case CPPDeclaration::ST_typedef: + return is_pointable(type->as_typedef_type()->_type); default: return false; @@ -444,6 +594,43 @@ is_char(CPPType *type) { } } + case CPPDeclaration::ST_typedef: + return is_char(type->as_typedef_type()->_type); + + default: + break; + } + + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeManager::is_unsigned_char +// Access: Public, Static +// Description: Returns true if the indicated type is unsigned char, +// but not signed or 'plain' char. +//////////////////////////////////////////////////////////////////// +bool TypeManager:: +is_unsigned_char(CPPType *type) { + switch (type->get_subtype()) { + case CPPDeclaration::ST_const: + return is_unsigned_char(type->as_const_type()->_wrapped_around); + + case CPPDeclaration::ST_simple: + { + CPPSimpleType *simple_type = type->as_simple_type(); + + if (simple_type != (CPPSimpleType *)NULL) { + return + (simple_type->_type == CPPSimpleType::T_char) && + (simple_type->_flags & CPPSimpleType::F_unsigned) != 0; + } + } + break; + + case CPPDeclaration::ST_typedef: + return is_unsigned_char(type->as_typedef_type()->_type); + default: break; } @@ -466,6 +653,75 @@ is_char_pointer(CPPType *type) { case CPPDeclaration::ST_pointer: return is_char(type->as_pointer_type()->_pointing_at); + case CPPDeclaration::ST_typedef: + return is_char_pointer(type->as_typedef_type()->_type); + + default: + return false; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeManager::is_const_char_pointer +// Access: Public, Static +// Description: Returns true if the indicated type is const char*. +//////////////////////////////////////////////////////////////////// +bool TypeManager:: +is_const_char_pointer(CPPType *type) { + switch (type->get_subtype()) { + case CPPDeclaration::ST_const: + return is_const_char_pointer(type->as_const_type()->_wrapped_around); + + case CPPDeclaration::ST_pointer: + return (is_const(type->as_pointer_type()->_pointing_at) && + is_char(type->as_pointer_type()->_pointing_at)); + + case CPPDeclaration::ST_typedef: + return is_const_char_pointer(type->as_typedef_type()->_type); + + default: + return false; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeManager::is_unsigned_char_pointer +// Access: Public, Static +// Description: Returns true if the indicated type is unsigned char* +// or const unsigned char*. +//////////////////////////////////////////////////////////////////// +bool TypeManager:: +is_unsigned_char_pointer(CPPType *type) { + switch (type->get_subtype()) { + case CPPDeclaration::ST_const: + return is_unsigned_char_pointer(type->as_const_type()->_wrapped_around); + + case CPPDeclaration::ST_pointer: + return is_unsigned_char(type->as_pointer_type()->_pointing_at); + + case CPPDeclaration::ST_typedef: + return is_unsigned_char_pointer(type->as_typedef_type()->_type); + + default: + return false; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeManager::is_const_unsigned_char_pointer +// Access: Public, Static +// Description: Returns true if the indicated type is +// const unsigned char*. +//////////////////////////////////////////////////////////////////// +bool TypeManager:: +is_const_unsigned_char_pointer(CPPType *type) { + switch (type->get_subtype()) { + case CPPDeclaration::ST_const: + return is_unsigned_char_pointer(type->as_const_type()->_wrapped_around); + + case CPPDeclaration::ST_typedef: + return is_const_unsigned_char_pointer(type->as_typedef_type()->_type); + default: return false; } @@ -489,6 +745,9 @@ is_basic_string_char(CPPType *type) { case CPPDeclaration::ST_const: return is_basic_string_char(type->as_const_type()->_wrapped_around); + case CPPDeclaration::ST_typedef: + return is_basic_string_char(type->as_typedef_type()->_type); + default: break; } @@ -508,6 +767,9 @@ is_const_basic_string_char(CPPType *type) { case CPPDeclaration::ST_const: return is_basic_string_char(type->as_const_type()->_wrapped_around); + case CPPDeclaration::ST_typedef: + return is_const_basic_string_char(type->as_typedef_type()->_type); + default: return false; } @@ -525,6 +787,9 @@ is_const_ref_to_basic_string_char(CPPType *type) { case CPPDeclaration::ST_reference: return is_const_basic_string_char(type->as_reference_type()->_pointing_at); + case CPPDeclaration::ST_typedef: + return is_const_ref_to_basic_string_char(type->as_typedef_type()->_type); + default: return false; } @@ -542,6 +807,9 @@ is_const_ptr_to_basic_string_char(CPPType *type) { case CPPDeclaration::ST_pointer: return is_const_basic_string_char(type->as_pointer_type()->_pointing_at); + case CPPDeclaration::ST_typedef: + return is_const_ptr_to_basic_string_char(type->as_typedef_type()->_type); + default: return false; } @@ -559,6 +827,9 @@ is_string(CPPType *type) { case CPPDeclaration::ST_reference: return is_const_basic_string_char(type->as_reference_type()->_pointing_at); + case CPPDeclaration::ST_typedef: + return is_string(type->as_typedef_type()->_type); + default: break; } @@ -586,6 +857,9 @@ is_wchar(CPPType *type) { } } + case CPPDeclaration::ST_typedef: + return is_wchar(type->as_typedef_type()->_type); + default: break; } @@ -608,6 +882,9 @@ is_wchar_pointer(CPPType *type) { case CPPDeclaration::ST_pointer: return is_wchar(type->as_pointer_type()->_pointing_at); + case CPPDeclaration::ST_typedef: + return is_wchar_pointer(type->as_typedef_type()->_type); + default: return false; } @@ -631,6 +908,9 @@ is_basic_string_wchar(CPPType *type) { case CPPDeclaration::ST_const: return is_basic_string_wchar(type->as_const_type()->_wrapped_around); + case CPPDeclaration::ST_typedef: + return is_basic_string_wchar(type->as_typedef_type()->_type); + default: break; } @@ -650,6 +930,9 @@ is_const_basic_string_wchar(CPPType *type) { case CPPDeclaration::ST_const: return is_basic_string_wchar(type->as_const_type()->_wrapped_around); + case CPPDeclaration::ST_typedef: + return is_const_basic_string_wchar(type->as_typedef_type()->_type); + default: return false; } @@ -667,6 +950,9 @@ is_const_ref_to_basic_string_wchar(CPPType *type) { case CPPDeclaration::ST_reference: return is_const_basic_string_wchar(type->as_reference_type()->_pointing_at); + case CPPDeclaration::ST_typedef: + return is_const_ref_to_basic_string_wchar(type->as_typedef_type()->_type); + default: return false; } @@ -684,6 +970,9 @@ is_const_ptr_to_basic_string_wchar(CPPType *type) { case CPPDeclaration::ST_pointer: return is_const_basic_string_wchar(type->as_pointer_type()->_pointing_at); + case CPPDeclaration::ST_typedef: + return is_const_ptr_to_basic_string_wchar(type->as_typedef_type()->_type); + default: return false; } @@ -701,6 +990,9 @@ is_wstring(CPPType *type) { case CPPDeclaration::ST_reference: return is_const_basic_string_wchar(type->as_reference_type()->_pointing_at); + case CPPDeclaration::ST_typedef: + return is_wstring(type->as_typedef_type()->_type); + default: break; } @@ -730,6 +1022,9 @@ is_bool(CPPType *type) { } break; + case CPPDeclaration::ST_typedef: + return is_bool(type->as_typedef_type()->_type); + default: break; } @@ -761,11 +1056,16 @@ is_integer(CPPType *type) { (simple_type->_type == CPPSimpleType::T_bool || simple_type->_type == CPPSimpleType::T_char || simple_type->_type == CPPSimpleType::T_wchar_t || + simple_type->_type == CPPSimpleType::T_char16_t || + simple_type->_type == CPPSimpleType::T_char32_t || simple_type->_type == CPPSimpleType::T_int); } } break; + case CPPDeclaration::ST_typedef: + return is_integer(type->as_typedef_type()->_type); + default: break; } @@ -794,11 +1094,69 @@ is_unsigned_integer(CPPType *type) { simple_type->_type == CPPSimpleType::T_char || simple_type->_type == CPPSimpleType::T_wchar_t || simple_type->_type == CPPSimpleType::T_int) && - (simple_type->_flags & CPPSimpleType::F_unsigned) != 0); + (simple_type->_flags & CPPSimpleType::F_unsigned) != 0) || + (simple_type->_type == CPPSimpleType::T_char16_t || + simple_type->_type == CPPSimpleType::T_char32_t); } } break; + case CPPDeclaration::ST_typedef: + return is_unsigned_integer(type->as_typedef_type()->_type); + + default: + break; + } + + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeManager::is_size +// Access: Public, Static +// Description: Returns true if the indicated type is the "size_t" +// type, or a const size_t, or a typedef to either. +//////////////////////////////////////////////////////////////////// +bool TypeManager:: +is_size(CPPType *type) { + switch (type->get_subtype()) { + case CPPDeclaration::ST_const: + return is_size(type->as_const_type()->_wrapped_around); + + case CPPDeclaration::ST_typedef: + if (type->get_simple_name() == "size_t") { + return is_integer(type->as_typedef_type()->_type); + } else { + return is_size(type->as_typedef_type()->_type); + } + + default: + break; + } + + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeManager::is_ssize +// Access: Public, Static +// Description: Returns true if the indicated type is the "ssize_t" +// type, or a const ssize_t, or a typedef to either. +//////////////////////////////////////////////////////////////////// +bool TypeManager:: +is_ssize(CPPType *type) { + switch (type->get_subtype()) { + case CPPDeclaration::ST_const: + return is_ssize(type->as_const_type()->_wrapped_around); + + case CPPDeclaration::ST_typedef: + if (type->get_simple_name() == "Py_ssize_t" || + type->get_simple_name() == "ssize_t") { + return is_integer(type->as_typedef_type()->_type); + } else { + return is_ssize(type->as_typedef_type()->_type); + } + default: break; } @@ -822,12 +1180,15 @@ is_short(CPPType *type) { { CPPSimpleType *simple_type = type->as_simple_type(); if (simple_type != (CPPSimpleType *)NULL) { - return (simple_type->_type == CPPSimpleType::T_int && + return (simple_type->_type == CPPSimpleType::T_int && (simple_type->_flags & CPPSimpleType::F_short) != 0); } } break; + case CPPDeclaration::ST_typedef: + return is_short(type->as_typedef_type()->_type); + default: break; } @@ -851,12 +1212,15 @@ is_unsigned_short(CPPType *type) { { CPPSimpleType *simple_type = type->as_simple_type(); if (simple_type != (CPPSimpleType *)NULL) { - return (simple_type->_type == CPPSimpleType::T_int && + return (simple_type->_type == CPPSimpleType::T_int && (simple_type->_flags & (CPPSimpleType::F_short | CPPSimpleType::F_unsigned)) == (CPPSimpleType::F_short | CPPSimpleType::F_unsigned)); } } break; + case CPPDeclaration::ST_typedef: + return is_unsigned_short(type->as_typedef_type()->_type); + default: break; } @@ -881,12 +1245,15 @@ is_longlong(CPPType *type) { { CPPSimpleType *simple_type = type->as_simple_type(); if (simple_type != (CPPSimpleType *)NULL) { - return (simple_type->_type == CPPSimpleType::T_int && + return (simple_type->_type == CPPSimpleType::T_int && (simple_type->_flags & CPPSimpleType::F_longlong) != 0); } } break; + case CPPDeclaration::ST_typedef: + return is_longlong(type->as_typedef_type()->_type); + default: break; } @@ -911,12 +1278,15 @@ is_unsigned_longlong(CPPType *type) { { CPPSimpleType *simple_type = type->as_simple_type(); if (simple_type != (CPPSimpleType *)NULL) { - return (simple_type->_type == CPPSimpleType::T_int && + return (simple_type->_type == CPPSimpleType::T_int && (simple_type->_flags & (CPPSimpleType::F_longlong | CPPSimpleType::F_unsigned)) == (CPPSimpleType::F_longlong | CPPSimpleType::F_unsigned)); } } break; + case CPPDeclaration::ST_typedef: + return is_unsigned_longlong(type->as_typedef_type()->_type); + default: break; } @@ -945,6 +1315,9 @@ is_double(CPPType *type) { } break; + case CPPDeclaration::ST_typedef: + return is_double(type->as_typedef_type()->_type); + default: break; } @@ -976,6 +1349,9 @@ is_float(CPPType *type) { } break; + case CPPDeclaration::ST_typedef: + return is_float(type->as_typedef_type()->_type); + default: break; } @@ -1033,6 +1409,9 @@ is_reference_count(CPPType *type) { } break; + case CPPDeclaration::ST_typedef: + return is_reference_count(type->as_typedef_type()->_type); + default: break; } @@ -1055,6 +1434,9 @@ is_reference_count_pointer(CPPType *type) { case CPPDeclaration::ST_pointer: return is_reference_count(type->as_pointer_type()->_pointing_at); + case CPPDeclaration::ST_typedef: + return is_reference_count_pointer(type->as_typedef_type()->_type); + default: return false; } @@ -1096,6 +1478,10 @@ is_pointer_to_base(CPPType *type) { } } } + return false; + + case CPPDeclaration::ST_typedef: + return is_pointer_to_base(type->as_typedef_type()->_type); default: return false; @@ -1114,6 +1500,9 @@ is_const_pointer_to_base(CPPType *type) { case CPPDeclaration::ST_const: return is_pointer_to_base(type->as_const_type()->_wrapped_around); + case CPPDeclaration::ST_typedef: + return is_const_pointer_to_base(type->as_typedef_type()->_type); + default: return false; } @@ -1134,6 +1523,9 @@ is_const_ref_to_pointer_to_base(CPPType *type) { case CPPDeclaration::ST_reference: return is_const_pointer_to_base(type->as_reference_type()->_pointing_at); + case CPPDeclaration::ST_typedef: + return is_const_ref_to_pointer_to_base(type->as_typedef_type()->_type); + default: return false; } @@ -1161,6 +1553,9 @@ is_pair(CPPType *type) { case CPPDeclaration::ST_reference: return is_pair(type->as_reference_type()->_pointing_at); + case CPPDeclaration::ST_typedef: + return is_pair(type->as_typedef_type()->_type); + default: break; } @@ -1182,6 +1577,9 @@ is_pointer_to_PyObject(CPPType *type) { case CPPDeclaration::ST_pointer: return is_PyObject(type->as_pointer_type()->_pointing_at); + case CPPDeclaration::ST_typedef: + return is_pointer_to_PyObject(type->as_typedef_type()->_type); + default: return false; } @@ -1200,13 +1598,64 @@ is_PyObject(CPPType *type) { case CPPDeclaration::ST_extension: case CPPDeclaration::ST_struct: - return (type->get_local_name(&parser) == "PyObject" || - type->get_local_name(&parser) == "PyTypeObject" || - type->get_local_name(&parser) == "PyStringObject" || - type->get_local_name(&parser) == "PyUnicodeObject" || - type->get_local_name(&parser) == "_object" || + return (type->get_local_name(&parser) == "_object" || type->get_local_name(&parser) == "_typeobject"); + case CPPDeclaration::ST_typedef: + return (is_struct(type->as_typedef_type()->_type) && + (type->get_local_name(&parser) == "PyObject" || + type->get_local_name(&parser) == "PyTypeObject" || + type->get_local_name(&parser) == "PyStringObject" || + type->get_local_name(&parser) == "PyUnicodeObject")) || + is_PyObject(type->as_typedef_type()->_type); + + default: + return false; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeManager::is_pointer_to_PyTypeObject +// Access: Public, Static +// Description: Returns true if the indicated type is PyTypeObject *. +//////////////////////////////////////////////////////////////////// +bool TypeManager:: +is_pointer_to_PyTypeObject(CPPType *type) { + switch (type->get_subtype()) { + case CPPDeclaration::ST_const: + return is_pointer_to_PyTypeObject(type->as_const_type()->_wrapped_around); + + case CPPDeclaration::ST_pointer: + return is_PyTypeObject(type->as_pointer_type()->_pointing_at); + + case CPPDeclaration::ST_typedef: + return is_pointer_to_PyTypeObject(type->as_typedef_type()->_type); + + default: + return false; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeManager::is_PyTypeObject +// Access: Public, Static +// Description: Returns true if the indicated type is PyTypeObject. +//////////////////////////////////////////////////////////////////// +bool TypeManager:: +is_PyTypeObject(CPPType *type) { + switch (type->get_subtype()) { + case CPPDeclaration::ST_const: + return is_PyTypeObject(type->as_const_type()->_wrapped_around); + + case CPPDeclaration::ST_extension: + case CPPDeclaration::ST_struct: + return (type->get_local_name(&parser) == "_typeobject"); + + case CPPDeclaration::ST_typedef: + return (type->get_local_name(&parser) == "PyTypeObject" && + is_struct(type->as_typedef_type()->_type)) || + is_PyTypeObject(type->as_typedef_type()->_type); + default: return false; } @@ -1242,9 +1691,10 @@ is_PyStringObject(CPPType *type) { case CPPDeclaration::ST_const: return is_PyStringObject(type->as_const_type()->_wrapped_around); - case CPPDeclaration::ST_extension: - case CPPDeclaration::ST_struct: - return (type->get_local_name(&parser) == "PyStringObject"); + case CPPDeclaration::ST_typedef: + return (type->get_local_name(&parser) == "PyStringObject" && + is_struct(type->as_typedef_type()->_type)) || + is_PyStringObject(type->as_typedef_type()->_type); default: return false; @@ -1281,9 +1731,10 @@ is_PyUnicodeObject(CPPType *type) { case CPPDeclaration::ST_const: return is_PyUnicodeObject(type->as_const_type()->_wrapped_around); - case CPPDeclaration::ST_extension: - case CPPDeclaration::ST_struct: - return (type->get_local_name(&parser) == "PyUnicodeObject"); + case CPPDeclaration::ST_typedef: + return (type->get_local_name(&parser) == "PyUnicodeObject" && + is_struct(type->as_typedef_type()->_type)) || + is_PyUnicodeObject(type->as_typedef_type()->_type); default: return false; @@ -1304,6 +1755,9 @@ is_pointer_to_Py_buffer(CPPType *type) { case CPPDeclaration::ST_pointer: return is_Py_buffer(type->as_pointer_type()->_pointing_at); + case CPPDeclaration::ST_typedef: + return is_pointer_to_Py_buffer(type->as_typedef_type()->_type); + default: return false; } @@ -1324,6 +1778,9 @@ is_Py_buffer(CPPType *type) { case CPPDeclaration::ST_struct: return (type->get_local_name(&parser) == "Py_buffer"); + case CPPDeclaration::ST_typedef: + return is_Py_buffer(type->as_typedef_type()->_type); + default: return false; } @@ -1342,6 +1799,9 @@ bool TypeManager::is_ostream(CPPType *type) { case CPPDeclaration::ST_struct: return (type->get_local_name(&parser) == "ostream"); + case CPPDeclaration::ST_typedef: + return is_ostream(type->as_typedef_type()->_type); + default: return false; } @@ -1364,6 +1824,9 @@ is_pointer_to_ostream(CPPType *type) { case CPPDeclaration::ST_pointer: return is_ostream(type->as_pointer_type()->_pointing_at); + case CPPDeclaration::ST_typedef: + return is_pointer_to_ostream(type->as_typedef_type()->_type); + default: return false; } @@ -1440,6 +1903,9 @@ involves_unpublished(CPPType *type) { } */ + case CPPDeclaration::ST_typedef: + return involves_unpublished(type->as_typedef_type()->_type); + default: if (type->_declaration != (CPPTypeDeclaration *)NULL) { return (type->_declaration->_vis > min_vis); @@ -1486,6 +1952,9 @@ involves_protected(CPPType *type) { return false; } + case CPPDeclaration::ST_typedef: + return involves_protected(type->as_typedef_type()->_type); + default: if (type->_declaration != (CPPTypeDeclaration *)NULL) { return (type->_declaration->_vis > V_public); @@ -1572,8 +2041,8 @@ unwrap_const_reference(CPPType *source_type) { //////////////////////////////////////////////////////////////////// // Function: TypeManager::unwrap // Access: Public, Static -// Description: Removes all const, pointer, and reference wrappers, -// to get to the thing we're talking about. +// Description: Removes all const, pointer, reference wrappers, and +// typedefs, to get to the thing we're talking about. //////////////////////////////////////////////////////////////////// CPPType *TypeManager:: unwrap(CPPType *source_type) { @@ -1587,6 +2056,9 @@ unwrap(CPPType *source_type) { case CPPDeclaration::ST_pointer: return unwrap(source_type->as_pointer_type()->_pointing_at); + case CPPDeclaration::ST_typedef: + return unwrap(source_type->as_typedef_type()->_type); + default: return source_type; } @@ -1645,6 +2117,9 @@ get_template_parameter_type(CPPType *source_type, int i) { case CPPDeclaration::ST_reference: return get_template_parameter_type(source_type->as_reference_type()->_pointing_at, i); + case CPPDeclaration::ST_typedef: + return get_template_parameter_type(source_type->as_typedef_type()->_type); + default: break; } @@ -1657,7 +2132,7 @@ get_template_parameter_type(CPPType *source_type, int i) { // I'm not sure how reliable this is, but I don't know if there // is a more proper way to access this. CPPTemplateParameterList *templ = type->_ident->_names.back().get_templ(); - if (templ == NULL || i >= templ->_parameters.size()) { + if (templ == NULL || i >= (int)templ->_parameters.size()) { return NULL; } @@ -1919,152 +2394,152 @@ has_protected_destructor(CPPType *type) { return false; } //////////////////////////////////////////////////////////////////// -// Function: TypeManager::has_protected_destructor +// Function: TypeManager::is_exported // Access: Public, Static -// Description: Returns true if the destructor for the given class or -// struct is protected or private, or false if the -// destructor is public or absent. +// Description: //////////////////////////////////////////////////////////////////// -bool TypeManager::IsExported(CPPType *in_type) -{ - +bool TypeManager:: +is_exported(CPPType *in_type) { string name = in_type->get_local_name(&parser); if (name.empty()) { - return false; + return false; } - //return true; + // this question is about the base type + CPPType *base_type = resolve_type(unwrap(in_type)); + //CPPType *base_type = in_type; + // Ok export Rules.. + // Classes and Structs and Unions are exported only if they have a + // function that is exported.. + // function is the easiest case. - // this question is about the base type - CPPType *base_type = resolve_type(unwrap(in_type)); - //CPPType *base_type = in_type; - // Ok export Rules.. - // Classes and Structs and Unions are exported only if they have a - // function that is exported.. - // function is the easiest case. + if (base_type->_vis <= min_vis) { + return true; + } - if (base_type->_vis <= min_vis) + if (in_type->_vis <= min_vis) { + return true; + } + + if (base_type->get_subtype() == CPPDeclaration::ST_struct) { + CPPStructType *struct_type = base_type->resolve_type(&parser, &parser)->as_struct_type(); + CPPScope *scope = struct_type->_scope; + + CPPScope::Declarations::const_iterator di; + for (di = scope->_declarations.begin(); + di != scope->_declarations.end(); ++di) { + if ((*di)->_vis <= min_vis) { return true; + } + } - if (in_type->_vis <= min_vis) + } else if (base_type->get_subtype() == CPPDeclaration::ST_instance) { + CPPInstance *inst = base_type->as_instance(); + if (inst->_type->get_subtype() == CPPDeclaration::ST_function) { + CPPInstance *function = inst; + CPPFunctionType *ftype = function->_type->resolve_type(&parser, &parser)->as_function_type(); + if (ftype->_vis <= min_vis) { return true; - - - if (base_type->get_subtype() == CPPDeclaration::ST_struct) - { - CPPStructType *sstruct_type = base_type->as_struct_type(); - CPPStructType *struct_type =sstruct_type->resolve_type(&parser, &parser)->as_struct_type(); - CPPScope *scope = struct_type->_scope; - - CPPScope::Declarations::const_iterator di; - for (di = scope->_declarations.begin();di != scope->_declarations.end(); di++) - { - if ((*di)->_vis <= min_vis) - return true; - } - - - + } + } else { + if (inst->_vis <= min_vis) { + return true; + } } - else if (base_type->get_subtype() == CPPDeclaration::ST_instance) - { - CPPInstance *inst = base_type->as_instance(); - if (inst->_type->get_subtype() == CPPDeclaration::ST_function) - { - CPPInstance *function = inst; - CPPFunctionType *ftype = function->_type->resolve_type(&parser, &parser)->as_function_type(); - if (ftype->_vis <= min_vis) - return true; - } - else - { - if (inst->_vis <= min_vis) - return true; - } + } else if (base_type->get_subtype() == CPPDeclaration::ST_typedef) { + CPPTypedefType *tdef = base_type->as_typedef_type(); + if (tdef->_type->get_subtype() == CPPDeclaration::ST_struct) { + CPPStructType *struct_type =tdef->_type->resolve_type(&parser, &parser)->as_struct_type(); + return is_exported(struct_type); } - else if (base_type->get_subtype() == CPPDeclaration::ST_typedef) - { - CPPTypedef *tdef = base_type->as_typedef(); - if (tdef->_type->get_subtype() == CPPDeclaration::ST_struct) - { - CPPStructType *struct_type =tdef->_type->resolve_type(&parser, &parser)->as_struct_type(); - return IsExported(struct_type); - } - } - else if (base_type->get_subtype() == CPPDeclaration::ST_type_declaration) - { - CPPType *type = base_type->as_type_declaration()->_type; - if (type->get_subtype() == CPPDeclaration::ST_struct) - { - CPPStructType *struct_type =type->as_type()->resolve_type(&parser, &parser)->as_struct_type(); - //CPPScope *scope = struct_type->_scope; - return IsExported(struct_type); + } else if (base_type->get_subtype() == CPPDeclaration::ST_type_declaration) { + CPPType *type = base_type->as_type_declaration()->_type; + if (type->get_subtype() == CPPDeclaration::ST_struct) { + CPPStructType *struct_type =type->as_type()->resolve_type(&parser, &parser)->as_struct_type(); + //CPPScope *scope = struct_type->_scope; + return is_exported(struct_type); - } - else if (type->get_subtype() == CPPDeclaration::ST_enum) - { - //CPPEnumType *enum_type = type->as_type()->resolve_type(&parser, &parser)->as_enum_type(); - if (type->_vis <= min_vis) - return true; - } + } else if (type->get_subtype() == CPPDeclaration::ST_enum) { + //CPPEnumType *enum_type = type->as_type()->resolve_type(&parser, &parser)->as_enum_type(); + if (type->_vis <= min_vis) { + return true; + } } + } /* - printf("---------------------> Visibility Failed %s %d Vis=%d, Minvis=%d\n", - base_type->get_fully_scoped_name().c_str(), - base_type->get_subtype(), - base_type->_vis, - min_vis); + printf("---------------------> Visibility Failed %s %d Vis=%d, Minvis=%d\n", + base_type->get_fully_scoped_name().c_str(), + base_type->get_subtype(), + base_type->_vis, + min_vis); */ - return false; -}; + return false; +} -bool TypeManager::IsLocal(CPPType *in_type) -{ - // A local means it was compiled in this scope of work.. - // IE a should actualy generate code for this objects.... - CPPType *base_type = resolve_type(unwrap(in_type)); - if (base_type->_forcetype) { - return true; - } +//////////////////////////////////////////////////////////////////// +// Function: TypeManager::is_local +// Access: Public, Static +// Description: Returns true if the type is defined in a local +// file rather than one that is included. +//////////////////////////////////////////////////////////////////// +bool TypeManager:: +is_local(CPPType *source_type) { + switch (source_type->get_subtype()) { + case CPPDeclaration::ST_const: + return is_local(source_type->as_const_type()->_wrapped_around); - if (base_type->_file._source == CPPFile::S_local && !base_type->is_incomplete()) { - return true; - } + case CPPDeclaration::ST_reference: + return is_local(source_type->as_reference_type()->_pointing_at); - return false; + case CPPDeclaration::ST_pointer: + return is_local(source_type->as_pointer_type()->_pointing_at); + + case CPPDeclaration::ST_simple: + return false; + + default: + { + CPPType *resolved_type = resolve_type(source_type); + if (resolved_type->_file._source == CPPFile::S_local && !resolved_type->is_incomplete()) { + return true; + } + } + } + + return false; /* - if (base_type->get_subtype() == CPPDeclaration::ST_struct) + if (base_type->get_subtype() == CPPDeclaration::ST_struct) { CPPStructType *struct_type = base_type->as_struct_type(); if (struct_type->_file._source == CPPFile::S_local) return true; } - else if (base_type->get_subtype() == CPPDeclaration::ST_instance) + else if (base_type->get_subtype() == CPPDeclaration::ST_instance) { CPPInstance *inst = base_type->as_instance(); - if (inst->_type->get_subtype() == CPPDeclaration::ST_function) + if (inst->_type->get_subtype() == CPPDeclaration::ST_function) { CPPInstance *function = inst; CPPFunctionType *ftype = function->_type->resolve_type(&parser, &parser)->as_function_type(); if (ftype->_file._source == CPPFile::S_local) return true; } - else + else { if (inst->_file._source == CPPFile::S_local) return true; } } - else if (base_type->get_subtype() == CPPDeclaration::ST_typedef) + else if (base_type->get_subtype() == CPPDeclaration::ST_typedef) { CPPTypedef *tdef = base_type->as_typedef(); - if (tdef->_type->get_subtype() == CPPDeclaration::ST_struct) + if (tdef->_type->get_subtype() == CPPDeclaration::ST_struct) { CPPStructType *struct_type =tdef->_type->resolve_type(&parser, &parser)->as_struct_type(); return IsLocal(struct_type); @@ -2072,7 +2547,7 @@ bool TypeManager::IsLocal(CPPType *in_type) } } - else if (base_type->get_subtype() == CPPDeclaration::ST_type_declaration) + else if (base_type->get_subtype() == CPPDeclaration::ST_type_declaration) { CPPType *type = base_type->as_type_declaration()->_type; if (type->get_subtype() == CPPDeclaration::ST_struct) @@ -2082,7 +2557,7 @@ bool TypeManager::IsLocal(CPPType *in_type) return true; } - else if (type->get_subtype() == CPPDeclaration::ST_enum) + else if (type->get_subtype() == CPPDeclaration::ST_enum) { CPPEnumType *enum_type = type->as_type()->resolve_type(&parser, &parser)->as_enum_type(); if (enum_type->_file._source != CPPFile::S_local) @@ -2091,7 +2566,7 @@ bool TypeManager::IsLocal(CPPType *in_type) } if (base_type->_file._source == CPPFile::S_local) - return true; + return true; return false; */ diff --git a/dtool/src/interrogate/typeManager.h b/dtool/src/interrogate/typeManager.h index 974a5b3e19..16b9dfda22 100644 --- a/dtool/src/interrogate/typeManager.h +++ b/dtool/src/interrogate/typeManager.h @@ -50,6 +50,7 @@ public: static bool is_ref_to_anything(CPPType *type); static bool is_const_ref_to_anything(CPPType *type); static bool is_const_pointer_to_anything(CPPType *type); + static bool is_const_pointer_or_ref(CPPType *type); static bool is_non_const_pointer_or_ref(CPPType *type); static bool is_pointer(CPPType *type); static bool is_const(CPPType *type); @@ -61,9 +62,15 @@ public: static bool is_const_simple(CPPType *type); static bool is_const_ref_to_simple(CPPType *type); static bool is_ref_to_simple(CPPType *type); + static bool is_simple_array(CPPType *type); + static bool is_pointer_to_simple(CPPType *type); static bool is_pointable(CPPType *type); static bool is_char(CPPType *type); + static bool is_unsigned_char(CPPType *type); static bool is_char_pointer(CPPType *type); + static bool is_const_char_pointer(CPPType *type); + static bool is_unsigned_char_pointer(CPPType *type); + static bool is_const_unsigned_char_pointer(CPPType *type); static bool is_basic_string_char(CPPType *type); static bool is_const_basic_string_char(CPPType *type); static bool is_const_ref_to_basic_string_char(CPPType *type); @@ -80,6 +87,8 @@ public: static bool is_bool(CPPType *type); static bool is_integer(CPPType *type); static bool is_unsigned_integer(CPPType *type); + static bool is_size(CPPType *type); + static bool is_ssize(CPPType *type); static bool is_short(CPPType *type); static bool is_unsigned_short(CPPType *type); static bool is_longlong(CPPType *type); @@ -94,6 +103,8 @@ public: static bool is_const_ref_to_pointer_to_base(CPPType *type); static bool is_pointer_to_PyObject(CPPType *type); static bool is_PyObject(CPPType *type); + static bool is_pointer_to_PyTypeObject(CPPType *type); + static bool is_PyTypeObject(CPPType *type); static bool is_pointer_to_PyStringObject(CPPType *type); static bool is_PyStringObject(CPPType *type); static bool is_pointer_to_PyUnicodeObject(CPPType *type); @@ -133,8 +144,8 @@ public: static bool has_protected_destructor(CPPType *type); - static bool IsExported(CPPType *type); - static bool IsLocal(CPPType *type); + static bool is_exported(CPPType *type); + static bool is_local(CPPType *type); }; diff --git a/dtool/src/interrogatedb/dtool_super_base.cxx b/dtool/src/interrogatedb/dtool_super_base.cxx index b92ce15f94..24911751a8 100644 --- a/dtool/src/interrogatedb/dtool_super_base.cxx +++ b/dtool/src/interrogatedb/dtool_super_base.cxx @@ -15,7 +15,7 @@ #include "py_panda.h" #ifdef HAVE_PYTHON - + class EmptyClass { }; Define_Module_Class_Private(dtoolconfig, DTOOL_SUPER_BASE, EmptyClass, DTOOL_SUPER_BASE111); @@ -30,7 +30,7 @@ PyMethodDef Dtool_Methods_DTOOL_SUPER_BASE[] = { { NULL, NULL } }; -static Py_hash_t DTool_HashKey_Methods_DTOOL_SUPER_BASE(PyObject *self) { +static Py_hash_t Dtool_HashKey_DTOOL_SUPER_BASE(PyObject *self) { void *local_this = DTOOL_Call_GetPointerThis(self); if (local_this == NULL) { return -1; @@ -38,7 +38,7 @@ static Py_hash_t DTool_HashKey_Methods_DTOOL_SUPER_BASE(PyObject *self) { return (Py_hash_t) local_this; }; -inline void Dtool_PyModuleClassInit_DTOOL_SUPER_BASE(PyObject *module) { +EXPCL_DTOOLCONFIG void Dtool_PyModuleClassInit_DTOOL_SUPER_BASE(PyObject *module) { static bool initdone = false; if (!initdone) { @@ -46,15 +46,6 @@ inline void Dtool_PyModuleClassInit_DTOOL_SUPER_BASE(PyObject *module) { Dtool_DTOOL_SUPER_BASE.As_PyTypeObject().tp_dict = PyDict_New(); PyDict_SetItemString(Dtool_DTOOL_SUPER_BASE.As_PyTypeObject().tp_dict, "DtoolClassDict", Dtool_DTOOL_SUPER_BASE.As_PyTypeObject().tp_dict); - // __hash__ - Dtool_DTOOL_SUPER_BASE.As_PyTypeObject().tp_hash = &DTool_HashKey_Methods_DTOOL_SUPER_BASE; -#if PY_MAJOR_VERSION >= 3 - // Python 3 removed the regular tp_compare function - there is only tp_richcompare. - Dtool_DTOOL_SUPER_BASE.As_PyTypeObject().tp_richcompare = &DTOOL_PyObject_RichCompare; -#else - Dtool_DTOOL_SUPER_BASE.As_PyTypeObject().tp_compare = &DTOOL_PyObject_Compare; -#endif - if (PyType_Ready(&Dtool_DTOOL_SUPER_BASE.As_PyTypeObject()) < 0) { PyErr_SetString(PyExc_TypeError, "PyType_Ready(Dtool_DTOOL_SUPER_BASE)"); return; @@ -83,9 +74,66 @@ int Dtool_Init_DTOOL_SUPER_BASE(PyObject *self, PyObject *args, PyObject *kwds) return -1; } -int Dtool_InitNoCoerce_DTOOL_SUPER_BASE(PyObject *self, PyObject *args) { - PyErr_SetString(PyExc_TypeError, "cannot init super base"); - return -1; -} +EXPORT_THIS Dtool_PyTypedObject Dtool_DTOOL_SUPER_BASE = { + { + PyVarObject_HEAD_INIT(NULL, 0) + "dtoolconfig.DTOOL_SUPER_BASE", + sizeof(Dtool_PyInstDef), + 0, + &Dtool_FreeInstance_DTOOL_SUPER_BASE, + 0, + 0, + 0, +#if PY_MAJOR_VERSION >= 3 + 0, +#else + &DTOOL_PyObject_Compare, +#endif + 0, + 0, + 0, + 0, + &Dtool_HashKey_DTOOL_SUPER_BASE, + 0, + 0, + PyObject_GenericGetAttr, + PyObject_GenericSetAttr, + 0, + (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_CHECKTYPES), + 0, + 0, + 0, +#if PY_MAJOR_VERSION >= 3 + &DTOOL_PyObject_RichCompare, +#else + 0, +#endif + 0, + 0, + 0, + Dtool_Methods_DTOOL_SUPER_BASE, + standard_type_members, + 0, + 0, + 0, + 0, + 0, + 0, + Dtool_Init_DTOOL_SUPER_BASE, + PyType_GenericAlloc, + Dtool_new_DTOOL_SUPER_BASE, + PyObject_Del, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + }, + Dtool_UpcastInterface_DTOOL_SUPER_BASE, + Dtool_DowncastInterface_DTOOL_SUPER_BASE, + TypeHandle::none(), +}; #endif // HAVE_PYTHON diff --git a/dtool/src/interrogatedb/interrogateType.I b/dtool/src/interrogatedb/interrogateType.I index 681efc39e9..7dc101d6db 100644 --- a/dtool/src/interrogatedb/interrogateType.I +++ b/dtool/src/interrogatedb/interrogateType.I @@ -207,6 +207,16 @@ is_const() const { return (_flags & F_const) != 0; } +//////////////////////////////////////////////////////////////////// +// Function: InterrogateType::is_typedef +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool InterrogateType:: +is_typedef() const { + return (_flags & F_typedef) != 0; +} + //////////////////////////////////////////////////////////////////// // Function: InterrogateType::get_wrapped_type // Access: Public @@ -217,6 +227,26 @@ get_wrapped_type() const { return _wrapped_type; } +//////////////////////////////////////////////////////////////////// +// Function: InterrogateType::is_array +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool InterrogateType:: +is_array() const { + return (_flags & F_array) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateType::get_array_size +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int InterrogateType:: +get_array_size() const { + return _array_size; +} + //////////////////////////////////////////////////////////////////// // Function: InterrogateType::is_enum // Access: Public diff --git a/dtool/src/interrogatedb/interrogateType.cxx b/dtool/src/interrogatedb/interrogateType.cxx index 2f79dddce2..3c69995c99 100644 --- a/dtool/src/interrogatedb/interrogateType.cxx +++ b/dtool/src/interrogatedb/interrogateType.cxx @@ -32,6 +32,7 @@ InterrogateType(InterrogateModuleDef *def) : _outer_class = 0; _atomic_token = AT_not_atomic; _wrapped_type = 0; + _array_size = 1; _destructor = 0; _cpptype = (CPPType *)NULL; @@ -111,6 +112,7 @@ operator = (const InterrogateType ©) { _outer_class = copy._outer_class; _atomic_token = copy._atomic_token; _wrapped_type = copy._wrapped_type; + _array_size = copy._array_size; _constructors = copy._constructors; _destructor = copy._destructor; _elements = copy._elements; @@ -165,6 +167,11 @@ output(ostream &out) const { out << _outer_class << " " << (int)_atomic_token << " " << _wrapped_type << " "; + + if (is_array()) { + out << _array_size << " "; + } + idf_output_vector(out, _constructors); out << _destructor << " "; idf_output_vector(out, _elements); @@ -197,6 +204,10 @@ input(istream &in) { _atomic_token = (AtomicToken)token; in >> _wrapped_type; + if (is_array()) { + in >> _array_size; + } + idf_input_vector(in, _constructors); in >> _destructor; diff --git a/dtool/src/interrogatedb/interrogateType.h b/dtool/src/interrogatedb/interrogateType.h index bcdd6ce2bb..8abf807f02 100644 --- a/dtool/src/interrogatedb/interrogateType.h +++ b/dtool/src/interrogatedb/interrogateType.h @@ -60,8 +60,12 @@ public: INLINE bool is_wrapped() const; INLINE bool is_pointer() const; INLINE bool is_const() const; + INLINE bool is_typedef() const; INLINE TypeIndex get_wrapped_type() const; + INLINE bool is_array() const; + INLINE int get_array_size() const; + INLINE bool is_enum() const; INLINE int number_of_enum_values() const; INLINE const string &get_enum_value_name(int n) const; @@ -132,6 +136,8 @@ private: F_nested = 0x040000, F_enum = 0x080000, F_unpublished = 0x100000, + F_typedef = 0x200000, + F_array = 0x400000, }; public: @@ -143,6 +149,7 @@ public: TypeIndex _outer_class; AtomicToken _atomic_token; TypeIndex _wrapped_type; + int _array_size; typedef vector Functions; Functions _constructors; diff --git a/dtool/src/interrogatedb/interrogate_interface.cxx b/dtool/src/interrogatedb/interrogate_interface.cxx index 2595a2a751..8254918a64 100644 --- a/dtool/src/interrogatedb/interrogate_interface.cxx +++ b/dtool/src/interrogatedb/interrogate_interface.cxx @@ -613,6 +613,12 @@ interrogate_type_is_const(TypeIndex type) { return InterrogateDatabase::get_ptr()->get_type(type).is_const(); } +bool +interrogate_type_is_typedef(TypeIndex type) { + //cerr << "interrogate_type_is_typedef(" << type << ")\n"; + return InterrogateDatabase::get_ptr()->get_type(type).is_typedef(); +} + TypeIndex interrogate_type_wrapped_type(TypeIndex type) { //cerr << "interrogate_type_wrapped_type(" << type << ")\n"; diff --git a/dtool/src/interrogatedb/interrogate_interface.h b/dtool/src/interrogatedb/interrogate_interface.h index 5d5352721a..c1e615556f 100644 --- a/dtool/src/interrogatedb/interrogate_interface.h +++ b/dtool/src/interrogatedb/interrogate_interface.h @@ -447,6 +447,7 @@ EXPCL_DTOOLCONFIG bool interrogate_type_is_short(TypeIndex type); EXPCL_DTOOLCONFIG bool interrogate_type_is_wrapped(TypeIndex type); EXPCL_DTOOLCONFIG bool interrogate_type_is_pointer(TypeIndex type); EXPCL_DTOOLCONFIG bool interrogate_type_is_const(TypeIndex type); +EXPCL_DTOOLCONFIG bool interrogate_type_is_typedef(TypeIndex type); EXPCL_DTOOLCONFIG TypeIndex interrogate_type_wrapped_type(TypeIndex type); // If interrogate_type_is_enum() returns true, this is an enumerated diff --git a/dtool/src/interrogatedb/py_panda.cxx b/dtool/src/interrogatedb/py_panda.cxx index f7606ec407..f90a938bd8 100644 --- a/dtool/src/interrogatedb/py_panda.cxx +++ b/dtool/src/interrogatedb/py_panda.cxx @@ -18,19 +18,26 @@ #ifdef HAVE_PYTHON PyMemberDef standard_type_members[] = { - {(char *)"this", T_ULONG, offsetof(Dtool_PyInstDef,_ptr_to_object),READONLY, (char *)"C++ 'this' pointer, if any"}, -// {(char *)"this_ownership", T_INT, offsetof(Dtool_PyInstDef, _memory_rules), READONLY, (char *)"C++ 'this' ownership rules"}, -// {(char *)"this_const", T_INT, offsetof(Dtool_PyInstDef, _is_const), READONLY, (char *)"C++ 'this' const flag"}, + {(char *)"this", (sizeof(void*) == sizeof(int)) ? T_UINT : T_ULONGLONG, offsetof(Dtool_PyInstDef, _ptr_to_object), READONLY, (char *)"C++ 'this' pointer, if any"}, + {(char *)"this_ownership", T_BOOL, offsetof(Dtool_PyInstDef, _memory_rules), READONLY, (char *)"C++ 'this' ownership rules"}, + {(char *)"this_const", T_BOOL, offsetof(Dtool_PyInstDef, _is_const), READONLY, (char *)"C++ 'this' const flag"}, // {(char *)"this_signature", T_INT, offsetof(Dtool_PyInstDef, _signature), READONLY, (char *)"A type check signature"}, {(char *)"this_metatype", T_OBJECT, offsetof(Dtool_PyInstDef, _My_Type), READONLY, (char *)"The dtool meta object"}, {NULL} /* Sentinel */ }; -//////////////////////////////////////////////////////////////////////// -/// Simple Recognition Functions.. -//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////// +// Function: DtoolCanThisBeAPandaInstance +// Description: Given a valid (non-NULL) PyObject, does a simple +// check to see if it might be an instance of a Panda +// type. It does this using a signature that is +// encoded on each instance. +//////////////////////////////////////////////////////////////////// bool DtoolCanThisBeAPandaInstance(PyObject *self) { // simple sanity check for the class type..size.. will stop basic foobars.. + // It is arguably better to use something like this: + // PyType_IsSubtype(Py_TYPE(self), &Dtool_DTOOL_SUPER_BASE._PyType) + // ...but probably not as fast. if (Py_TYPE(self)->tp_basicsize >= (int)sizeof(Dtool_PyInstDef)) { Dtool_PyInstDef *pyself = (Dtool_PyInstDef *) self; if (pyself->_signature == PY_PANDA_SIGNATURE) { @@ -56,101 +63,51 @@ void DTOOL_Call_ExtractThisPointerForType(PyObject *self, Dtool_PyTypedObject *c } //////////////////////////////////////////////////////////////////// -// Function: attempt_coercion -// Description: A helper function for DTOOL_Call_GetPointerThisClass, -// below. This attempts to coerce the given object to -// the indicated Panda object, by creating a temporary -// instance of the required Panda object. If -// successful, returns the "this" pointer of the -// temporary object; otherwise, returns NULL. +// Function: Dtool_Call_ExtractThisPointer +// Description: This is a support function for the Python bindings: +// it extracts the underlying C++ pointer of the given +// type for a given Python object. If it was of the +// wrong type, raises an AttributeError. //////////////////////////////////////////////////////////////////// -static void * -attempt_coercion(PyObject *self, Dtool_PyTypedObject *classdef, - PyObject **coerced) { - // The supplied parameter is not the required type. - if (coerced != NULL) { - // Attempt coercion: try to create a temporary instance of the - // required class using the supplied parameter. - // Because we want to use the special InitNoCoerce constructor - // here instead of the regular constructor (we don't want to risk - // recursive coercion on the nested type we're creating), we have - // to call the constructor with a few more steps. - PyObject *obj = NULL; - if (classdef->_PyType.tp_new != NULL) { - obj = classdef->_PyType.tp_new(&classdef->_PyType, self, NULL); - assert(obj != NULL); - - if (PyTuple_Check(self)) { - // A tuple was passed, which we assume are the constructor arguments. - if (classdef->_Dtool_InitNoCoerce(obj, self) != 0) { - Py_DECREF(obj); - obj = NULL; - } - } else { - // We need to pack the value into an args tuple. - PyObject *args = PyTuple_Pack(1, self); - if (classdef->_Dtool_InitNoCoerce(obj, args) != 0) { - Py_DECREF(obj); - obj = NULL; - } - Py_DECREF(args); - } - } - if (obj == NULL) { - // That didn't work; try to call a static "make" method instead. - // Presently, we don't bother filtering this for coercion, - // because none of our classes suffer from a recursion danger - // here. Maybe one day we will need to also construct a - // makeNoCoerce wrapper? - PyObject *make = PyObject_GetAttrString((PyObject *)classdef, "make"); - if (make != NULL) { - PyErr_Clear(); - if (PyTuple_Check(self)) { - obj = PyObject_CallObject(make, self); - } else { - obj = PyObject_CallFunctionObjArgs(make, self, NULL); - } - Py_DECREF(make); - } - } - if (obj != NULL) { - // Well, whaddaya know? The supplied parameter(s) suited - // the object's constructor. Now we have a temporary object - // that we can pass to the function. - Dtool_PyTypedObject *my_type = ((Dtool_PyInstDef *)obj)->_My_Type; - void *result = my_type->_Dtool_UpcastInterface(obj, classdef); - if (result != NULL) { - // Successfully coerced. Store the newly-allocated - // pointer, so the caller can release the coerced object - // at his leisure. We store it in a list, so that other - // parameters can accumulate there too. - if ((*coerced) == NULL) { - (*coerced) = PyList_New(0); - } - PyList_Append(*coerced, obj); - Py_DECREF(obj); - return result; - } - // Some problem getting the C++ pointer from our created - // temporary object. Weird. - Py_DECREF(obj); - } - - // Clear the error returned by the coercion constructor. It's not - // the error message we want to report. - PyErr_Clear(); +bool Dtool_Call_ExtractThisPointer(PyObject *self, Dtool_PyTypedObject &classdef, void **answer) { + if (self == NULL || !DtoolCanThisBeAPandaInstance(self)) { + Dtool_Raise_TypeError("C++ object is not yet constructed, or already destructed."); + return false; } - return NULL; + + *answer = ((Dtool_PyInstDef *)self)->_My_Type->_Dtool_UpcastInterface(self, &classdef); + return true; } -// Temporary function to preserve backward compatibility. -void * -DTOOL_Call_GetPointerThisClass(PyObject *self, Dtool_PyTypedObject *classdef, - int param, const string &function_name, bool const_ok, - PyObject **coerced) { - return DTOOL_Call_GetPointerThisClass(self, classdef, - param, function_name, const_ok, - coerced, true); +//////////////////////////////////////////////////////////////////// +// Function: Dtool_Call_ExtractThisPointer_NonConst +// Description: The same thing as Dtool_Call_ExtractThisPointer, +// except that it performs the additional check that +// the pointer is a non-const pointer. This is called +// by function wrappers for functions of which all +// overloads are non-const, and saves a bit of code. +// +// The extra method_name argument is used in formatting +// the error message. +//////////////////////////////////////////////////////////////////// +bool Dtool_Call_ExtractThisPointer_NonConst(PyObject *self, Dtool_PyTypedObject &classdef, + void **answer, const char *method_name) { + + if (self == NULL || !DtoolCanThisBeAPandaInstance(self)) { + Dtool_Raise_TypeError("C++ object is not yet constructed, or already destructed."); + return false; + } + + if (((Dtool_PyInstDef *)self)->_is_const) { + // All overloads of this function are non-const. + PyErr_Format(PyExc_TypeError, + "Cannot call %s() on a const object.", + method_name); + return false; + } + + *answer = ((Dtool_PyInstDef *)self)->_My_Type->_Dtool_UpcastInterface(self, &classdef); + return true; } //////////////////////////////////////////////////////////////////// @@ -176,18 +133,6 @@ DTOOL_Call_GetPointerThisClass(PyObject *self, Dtool_PyTypedObject *classdef, // declared non-const, and can therefore be called with // only a non-const "this" pointer. // -// If coerced is non-NULL, parameter coercion will be -// attempted. This means the supplied parameter may not -// exactly match the required type, but will satisfy the -// require type's constructor; and we will create -// temporary object(s) of the required type instead. In -// this case, coerced is a pointer to a PyList that will -// be filled with these temporary objects. If coerced -// is a pointer to a NULL PyObject, a new PyList will be -// created on the first successful coercion. If coerced -// itself is NULL, parameter coercion will not be -// attempted. -// // The return value is the C++ pointer that was // extracted, or NULL if there was a problem (in which // case the Python exception state will have been set). @@ -195,103 +140,36 @@ DTOOL_Call_GetPointerThisClass(PyObject *self, Dtool_PyTypedObject *classdef, void * DTOOL_Call_GetPointerThisClass(PyObject *self, Dtool_PyTypedObject *classdef, int param, const string &function_name, bool const_ok, - PyObject **coerced, bool report_errors) { - if (PyErr_Occurred()) { + bool report_errors) { + //if (PyErr_Occurred()) { + // return NULL; + //} + if (self == NULL) { + if (report_errors) { + return Dtool_Raise_TypeError("self is NULL"); + } return NULL; } - if (self != NULL) { - if (DtoolCanThisBeAPandaInstance(self)) { - Dtool_PyTypedObject *my_type = ((Dtool_PyInstDef *)self)->_My_Type; - void *result = my_type->_Dtool_UpcastInterface(self, classdef); - if (result != NULL) { - if (const_ok || !((Dtool_PyInstDef *)self)->_is_const) { - return result; - } - if (report_errors) { - ostringstream str; - str << function_name << "() argument " << param << " may not be const"; - string msg = str.str(); - PyErr_SetString(PyExc_TypeError, msg.c_str()); - } + if (DtoolCanThisBeAPandaInstance(self)) { + void *result = ((Dtool_PyInstDef *)self)->_My_Type->_Dtool_UpcastInterface(self, classdef); - } else { - if (report_errors) { - ostringstream str; - str << function_name << "() argument " << param << " must be "; - - PyObject *fname = PyObject_GetAttrString((PyObject *)classdef, "__name__"); - if (fname != (PyObject *)NULL) { -#if PY_MAJOR_VERSION >= 3 - str << PyUnicode_AsUTF8(fname); -#else - str << PyString_AsString(fname); -#endif - Py_DECREF(fname); - } else { - str << classdef->_name; - } - - PyObject *tname = PyObject_GetAttrString((PyObject *)Py_TYPE(self), "__name__"); - if (tname != (PyObject *)NULL) { -#if PY_MAJOR_VERSION >= 3 - str << ", not " << PyUnicode_AsUTF8(tname); -#else - str << ", not " << PyString_AsString(tname); -#endif - Py_DECREF(tname); - } else { - str << ", not " << my_type->_name; - } - - string msg = str.str(); - PyErr_SetString(PyExc_TypeError, msg.c_str()); - } - } - - } else { - // The parameter was not a Panda type. Can we coerce it to the - // appropriate type, by creating a temporary object? - void *result = attempt_coercion(self, classdef, coerced); - if (result != NULL) { + if (result != NULL) { + if (const_ok || !((Dtool_PyInstDef *)self)->_is_const) { return result; } - // Coercion failed. if (report_errors) { - ostringstream str; - str << function_name << "() argument " << param << " must be "; - - PyObject *fname = PyObject_GetAttrString((PyObject *)classdef, "__name__"); - if (fname != (PyObject *)NULL) { -#if PY_MAJOR_VERSION >= 3 - str << PyUnicode_AsUTF8(fname); -#else - str << PyString_AsString(fname); -#endif - Py_DECREF(fname); - } else { - str << classdef->_name; - } - - PyObject *tname = PyObject_GetAttrString((PyObject *)Py_TYPE(self), "__name__"); - if (tname != (PyObject *)NULL) { -#if PY_MAJOR_VERSION >= 3 - str << ", not " << PyUnicode_AsUTF8(tname); -#else - str << ", not " << PyString_AsString(tname); -#endif - Py_DECREF(tname); - } - - string msg = str.str(); - PyErr_SetString(PyExc_TypeError, msg.c_str()); + return PyErr_Format(PyExc_TypeError, + "%s() argument %d may not be const", + function_name.c_str(), param); } + return NULL; } - } else { - if (report_errors) { - PyErr_SetString(PyExc_TypeError, "self is NULL"); - } + } + + if (report_errors) { + return Dtool_Raise_ArgTypeError(self, param, function_name.c_str(), classdef->_PyType.tp_name); } return NULL; @@ -304,31 +182,175 @@ void *DTOOL_Call_GetPointerThis(PyObject *self) { return pyself->_ptr_to_object; } } - return NULL; } +#ifndef NDEBUG +//////////////////////////////////////////////////////////////////// +// Function: Dtool_CheckErrorOccurred +// Description: This is similar to a PyErr_Occurred() check, except +// that it also checks Notify to see if an assertion +// has occurred. If that is the case, then it raises +// an AssertionError. +// +// Returns true if there is an active exception, false +// otherwise. +// +// In the NDEBUG case, this is simply a #define to +// _PyErr_OCCURRED() (which is an undocumented inline +// version of PyErr_Occurred()). +//////////////////////////////////////////////////////////////////// +bool Dtool_CheckErrorOccurred() { + if (_PyErr_OCCURRED()) { + return true; + } + if (Notify::ptr()->has_assert_failed()) { + Dtool_Raise_AssertionError(); + return true; + } + return false; +} +#endif // NDEBUG + +//////////////////////////////////////////////////////////////////// +// Function: Dtool_Raise_AssertionError +// Description: Raises an AssertionError containing the last thrown +// assert message, and clears the assertion flag. +// Returns NULL. +//////////////////////////////////////////////////////////////////// +PyObject *Dtool_Raise_AssertionError() { + Notify *notify = Notify::ptr(); +#if PY_MAJOR_VERSION >= 3 + PyObject *message = PyUnicode_FromString(notify->get_assert_error_message().c_str()); +#else + PyObject *message = PyString_FromString(notify->get_assert_error_message().c_str()); +#endif + Py_INCREF(PyExc_AssertionError); + PyErr_Restore(PyExc_AssertionError, message, (PyObject *)NULL); + notify->clear_assert_failed(); + return NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: Dtool_Raise_TypeError +// Description: Raises a TypeError with the given message, and +// returns NULL. +//////////////////////////////////////////////////////////////////// +PyObject *Dtool_Raise_TypeError(const char *message) { + // PyErr_Restore is what PyErr_SetString would have ended up calling + // eventually anyway, so we might as well just get to the point. + Py_INCREF(PyExc_TypeError); +#if PY_MAJOR_VERSION >= 3 + PyErr_Restore(PyExc_TypeError, PyUnicode_FromString(message), (PyObject *)NULL); +#else + PyErr_Restore(PyExc_TypeError, PyString_FromString(message), (PyObject *)NULL); +#endif + return NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: Dtool_Raise_ArgTypeError +// Description: Raises a TypeError of the form: +// function_name() argument n must be type, not type +// for a given object passed to a function. +// +// Always returns NULL so that it can be conveniently +// used as a return expression for wrapper functions +// that return a PyObject pointer. +//////////////////////////////////////////////////////////////////// +PyObject *Dtool_Raise_ArgTypeError(PyObject *obj, int param, const char *function_name, const char *type_name) { +#if PY_MAJOR_VERSION >= 3 + PyObject *message = PyUnicode_FromFormat( +#else + PyObject *message = PyString_FromFormat( +#endif + "%s() argument %d must be %s, not %s", + function_name, param, type_name, + Py_TYPE(obj)->tp_name); + + Py_INCREF(PyExc_TypeError); + PyErr_Restore(PyExc_TypeError, message, (PyObject *)NULL); + return NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: Dtool_Raise_BadArgumentsError +// Description: Raises a TypeError of the form: +// Arguments must match: +// +// +// However, in release builds, this instead is defined +// to a function that just prints out a generic +// message, to help reduce the amount of strings in +// the compiled library. +// +// Always returns NULL so that it can be conveniently +// used as a return expression for wrapper functions +// that return a PyObject pointer. +//////////////////////////////////////////////////////////////////// +PyObject *_Dtool_Raise_BadArgumentsError() { + return Dtool_Raise_TypeError("arguments do not match any function overload"); +} + +//////////////////////////////////////////////////////////////////// +// Function: Dtool_Return_None +// Description: Convenience method that checks for exceptions, and +// if one occurred, returns NULL, otherwise Py_None. +//////////////////////////////////////////////////////////////////// +PyObject *_Dtool_Return_None() { + if (_PyErr_OCCURRED()) { + return NULL; + } +#ifndef NDEBUG + if (Notify::ptr()->has_assert_failed()) { + return Dtool_Raise_AssertionError(); + } +#endif + Py_INCREF(Py_None); + return Py_None; +} + +//////////////////////////////////////////////////////////////////// +// Function: Dtool_Return_Bool +// Description: Convenience method that checks for exceptions, and +// if one occurred, returns NULL, otherwise the given +// boolean value as a PyObject *. +//////////////////////////////////////////////////////////////////// +PyObject *Dtool_Return_Bool(bool value) { + if (_PyErr_OCCURRED()) { + return NULL; + } +#ifndef NDEBUG + if (Notify::ptr()->has_assert_failed()) { + return Dtool_Raise_AssertionError(); + } +#endif + PyObject *result = (value ? Py_True : Py_False); + Py_INCREF(result); + return result; +} + //////////////////////////////////////////////////////////////////////// // Function : DTool_CreatePyInstanceTyped // // this function relies on the behavior of typed objects in the panda system. // //////////////////////////////////////////////////////////////////////// -PyObject *DTool_CreatePyInstanceTyped(void *local_this_in, Dtool_PyTypedObject & known_class_type, bool memory_rules, bool is_const, int RunTimeType) { - if (local_this_in == NULL) { - // Let's not be stupid.. - PyErr_SetString(PyExc_TypeError, "C Function Return Null 'this'"); - return NULL; - } +PyObject *DTool_CreatePyInstanceTyped(void *local_this_in, Dtool_PyTypedObject &known_class_type, bool memory_rules, bool is_const, int type_index) { + // We can't do the NULL check here like in DTool_CreatePyInstance, since + // the caller will have to get the type index to pass to this function + // to begin with. That code probably would have crashed by now if it was + // really NULL for whatever reason. + nassertr(local_this_in != NULL, NULL); ///////////////////////////////////////////////////// // IF the class is possibly a run time typed object ///////////////////////////////////////////////////// - if (RunTimeType > 0) { + if (type_index > 0) { ///////////////////////////////////////////////////// // get best fit class... ///////////////////////////////////////////////////// - Dtool_PyTypedObject *target_class = Dtool_RuntimeTypeDtoolType(RunTimeType); + Dtool_PyTypedObject *target_class = Dtool_RuntimeTypeDtoolType(type_index); if (target_class != NULL) { ///////////////////////////////////////////////////// // cast to the type... @@ -343,7 +365,7 @@ PyObject *DTool_CreatePyInstanceTyped(void *local_this_in, Dtool_PyTypedObject & self->_ptr_to_object = new_local_this; self->_memory_rules = memory_rules; self->_is_const = is_const; - self->_signature = PY_PANDA_SIGNATURE; + //self->_signature = PY_PANDA_SIGNATURE; self->_My_Type = target_class; return (PyObject *)self; } @@ -360,7 +382,7 @@ PyObject *DTool_CreatePyInstanceTyped(void *local_this_in, Dtool_PyTypedObject & self->_ptr_to_object = local_this_in; self->_memory_rules = memory_rules; self->_is_const = is_const; - self->_signature = PY_PANDA_SIGNATURE; + //self->_signature = PY_PANDA_SIGNATURE; self->_My_Type = &known_class_type; } return (PyObject *)self; @@ -372,8 +394,10 @@ PyObject *DTool_CreatePyInstanceTyped(void *local_this_in, Dtool_PyTypedObject & //////////////////////////////////////////////////////////////////////// PyObject *DTool_CreatePyInstance(void *local_this, Dtool_PyTypedObject &in_classdef, bool memory_rules, bool is_const) { if (local_this == NULL) { - PyErr_SetString(PyExc_TypeError, "C Function Return Null 'this'"); - return NULL; + // This is actually a very common case, so let's allow this, but return + // Py_None consistently. This eliminates code in the wrappers. + Py_INCREF(Py_None); + return Py_None; } Dtool_PyTypedObject *classdef = &in_classdef; @@ -425,7 +449,7 @@ void RegisterRuntimeClass(Dtool_PyTypedObject *otype, int class_id) { if (class_id == 0) { interrogatedb_cat.warning() - << "Class " << otype->_name + << "Class " << otype->_PyType.tp_name << " has a zero TypeHandle value; check that init_type() is called.\n"; } else if (class_id > 0) { @@ -436,13 +460,14 @@ RegisterRuntimeClass(Dtool_PyTypedObject *otype, int class_id) { // There was already an entry in the dictionary for class_id. Dtool_PyTypedObject *other_type = (*result.first).second; interrogatedb_cat.warning() - << "Classes " << otype->_name << " and " << other_type->_name + << "Classes " << otype->_PyType.tp_name + << " and " << other_type->_PyType.tp_name << " share the same TypeHandle value (" << class_id << "); check class definitions.\n"; } else { GetRunTimeTypeList().insert(class_id); - otype->_Dtool_IsRunTimeCapable = true; + otype->_type = TypeRegistry::ptr()->find_type_by_id(class_id); } } } @@ -495,11 +520,10 @@ PyObject *Dtool_PyModuleInitHelper(LibraryDef *defs[], const char *modulename) { if (module == NULL) { #if PY_MAJOR_VERSION >= 3 - PyErr_SetString(PyExc_TypeError, "PyModule_Create returned NULL"); + return Dtool_Raise_TypeError("PyModule_Create returned NULL"); #else - PyErr_SetString(PyExc_TypeError, "Py_InitModule returned NULL"); + return Dtool_Raise_TypeError("Py_InitModule returned NULL"); #endif - return NULL; } // the constant inits... enums, classes ... @@ -508,7 +532,6 @@ PyObject *Dtool_PyModuleInitHelper(LibraryDef *defs[], const char *modulename) { } PyModule_AddIntConstant(module, "Dtool_PyNativeInterface", 1); - return module; } @@ -523,20 +546,26 @@ PyObject *Dtool_PyModuleInitHelper(LibraryDef *defs[], const char *modulename) { PyObject *Dtool_BorrowThisReference(PyObject *self, PyObject *args) { PyObject *from_in = NULL; PyObject *to_in = NULL; - if (PyArg_ParseTuple(args, "OO", &to_in, &from_in)) { + if (PyArg_UnpackTuple(args, "Dtool_BorrowThisReference", 2, 2, &to_in, &from_in)) { if (DtoolCanThisBeAPandaInstance(from_in) && DtoolCanThisBeAPandaInstance(to_in)) { - Dtool_PyInstDef * from = (Dtool_PyInstDef *) from_in; - Dtool_PyInstDef * to = (Dtool_PyInstDef *) to_in; + Dtool_PyInstDef *from = (Dtool_PyInstDef *) from_in; + Dtool_PyInstDef *to = (Dtool_PyInstDef *) to_in; + + //if (PyObject_TypeCheck(to_in, Py_TYPE(from_in))) { if (from->_My_Type == to->_My_Type) { to->_memory_rules = false; to->_is_const = from->_is_const; to->_ptr_to_object = from->_ptr_to_object; - return Py_BuildValue(""); + + Py_INCREF(Py_None); + return Py_None; } - PyErr_SetString(PyExc_TypeError, "Must Be Same Type??"); + + return PyErr_Format(PyExc_TypeError, "types %s and %s do not match", + Py_TYPE(from)->tp_name, Py_TYPE(to)->tp_name); } else { - PyErr_SetString(PyExc_TypeError, "One of these does not appear to be DTOOL Instance ??"); + return Dtool_Raise_TypeError("One of these does not appear to be DTOOL Instance ??"); } } return (PyObject *) NULL; @@ -551,16 +580,17 @@ PyObject *Dtool_AddToDictionary(PyObject *self1, PyObject *args) { PyObject *key; if (PyArg_ParseTuple(args, "OSO", &self, &key, &subject)) { PyObject *dict = ((PyTypeObject *)self)->tp_dict; - if (dict == NULL && !PyDict_Check(dict)) { - PyErr_SetString(PyExc_TypeError, "No dictionary On Object"); + if (dict == NULL || !PyDict_Check(dict)) { + return Dtool_Raise_TypeError("No dictionary On Object"); } else { - PyDict_SetItem(dict,key,subject); + PyDict_SetItem(dict, key, subject); } } if (PyErr_Occurred()) { return (PyObject *)NULL; } - return Py_BuildValue(""); + Py_INCREF(Py_None); + return Py_None; } /////////////////////////////////////////////////////////////////////////////////// @@ -725,7 +755,7 @@ PyObject *make_list_for_item(PyObject *self, const char *num_name, Py_DECREF(list); return NULL; } - PyList_SetItem(list, i, element); + PyList_SET_ITEM(list, i, element); } return list; } @@ -772,18 +802,15 @@ PyObject *map_deepcopy_to_copy(PyObject *self, PyObject *args) { // either a regular integer or a long integer, according // to whether the indicated value will fit. //////////////////////////////////////////////////////////////////// +#if PY_MAJOR_VERSION < 3 EXPCL_DTOOLCONFIG PyObject * PyLongOrInt_FromUnsignedLong(unsigned long value) { -#if PY_MAJOR_VERSION >= 3 - // Python 3 only has longs. - return PyLong_FromUnsignedLong(value); -#else if ((long)value < 0) { return PyLong_FromUnsignedLong(value); } else { return PyInt_FromLong((long)value); } -#endif } +#endif #endif // HAVE_PYTHON diff --git a/dtool/src/interrogatedb/py_panda.h b/dtool/src/interrogatedb/py_panda.h index 0811c6c795..16c1574cc8 100755 --- a/dtool/src/interrogatedb/py_panda.h +++ b/dtool/src/interrogatedb/py_panda.h @@ -13,12 +13,12 @@ //////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////// // Too do list .. -// We need a better dispatcher for the functions.. The behavior today is -// try one till it works or you run out of possibilities.. This is anything but optimal -// for performance and is treading on thin ice for function python or c++ will +// We need a better dispatcher for the functions.. The behavior today is +// try one till it works or you run out of possibilities.. This is anything but optimal +// for performance and is treading on thin ice for function python or c++ will // course there types to other types. // -// The linking step will produce allot of warnings +// The linking step will produce allot of warnings // warning LNK4049: locally defined symbol.. // // Get a second coder to review this file and the generated code .. @@ -40,6 +40,8 @@ #endif +#include "pnotify.h" + #if defined(HAVE_PYTHON) && !defined(CPPPARSER) #ifdef _POSIX_C_SOURCE @@ -102,21 +104,31 @@ inline PyObject* doPy_RETURN_FALSE() #define Py_TPFLAGS_CHECKTYPES 0 #endif -#if PY_MAJOR_VERSION < 3 +#if PY_MAJOR_VERSION >= 3 +// For writing code that will compile in both versions. +#define nb_nonzero nb_bool +#define nb_divide nb_true_divide +#define nb_inplace_divide nb_inplace_true_divide + +#define PyLongOrInt_Check(x) PyLong_Check(x) +#define PyLongOrInt_FromSize_t PyLong_FromSize_t +#define PyLongOrInt_FromLong PyLong_FromLong +#define PyLongOrInt_FromUnsignedLong PyLong_FromUnsignedLong +#define PyInt_Check PyLong_Check +#define PyInt_AsLong PyLong_AsLong +#define PyInt_AS_LONG PyLong_AS_LONG +#else +#define PyLongOrInt_Check(x) (PyInt_Check(x) || PyLong_Check(x)) +// PyInt_FromSize_t automatically picks the right type. +#define PyLongOrInt_FromSize_t PyInt_FromSize_t +#define PyLongOrInt_FromLong PyInt_FromLong + // For more portably defining hash functions. typedef long Py_hash_t; #endif -#if PY_MAJOR_VERSION >= 3 -#define nb_nonzero nb_bool -#define nb_divide nb_true_divide -#define nb_inplace_divide nb_inplace_true_divide -#endif - using namespace std; -#define PY_PANDA_SMALLER_FOOTPRINT 1 - /////////////////////////////////////////////////////////////////////////////////// // this is tempory .. untill this is glued better into the panda build system /////////////////////////////////////////////////////////////////////////////////// @@ -138,42 +150,39 @@ EXPCL_DTOOLCONFIG RunTimeTypeDictionary &GetRunTimeDictionary(); EXPCL_DTOOLCONFIG RunTimeTypeList &GetRunTimeTypeList(); ////////////////////////////////////////////////////////// -// used to stamp dtool instance.. +// used to stamp dtool instance.. #define PY_PANDA_SIGNATURE 0xbeaf -typedef void * ( * ConvertFunctionType )(PyObject *,Dtool_PyTypedObject * ); -typedef void * ( * ConvertFunctionType1 )(void *, Dtool_PyTypedObject *); -typedef void ( *FreeFunction )(PyObject *); -typedef void ( *PyModuleClassInit)(PyObject *module); -typedef int ( *InitNoCoerce)(PyObject *self, PyObject *args); +typedef void *(*UpcastFunction)(PyObject *,Dtool_PyTypedObject *); +typedef void *(*DowncastFunction)(void *, Dtool_PyTypedObject *); //inline Dtool_PyTypedObject * Dtool_RuntimeTypeDtoolType(int type); -inline void Dtool_Deallocate_General(PyObject * self); +//inline void Dtool_Deallocate_General(PyObject * self); //inline int DTOOL_PyObject_Compare(PyObject *v1, PyObject *v2); // //////////////////////////////////////////////////////////////////////// // THIS IS THE INSTANCE CONTAINER FOR ALL panda py objects.... //////////////////////////////////////////////////////////////////////// -#ifdef PY_PANDA_SMALLER_FOOTPRINT -// this should save 8 bytes per object .... struct Dtool_PyInstDef { PyObject_HEAD - void *_ptr_to_object; - struct Dtool_PyTypedObject *_My_Type; - unsigned short _signature ; - int _memory_rules : 1; // true if we own the pointer and should delete it or unref it - int _is_const : 1; // true if this is a "const" pointer. -}; -#else -struct Dtool_PyInstDef { - PyObject_HEAD - void *_ptr_to_object; - int _memory_rules; // true if we own the pointer and should delete it or unref it - int _is_const; // true if this is a "const" pointer. - unsigned long _signature; + // This is a pointer to the Dtool_PyTypedObject type. It's tempting + // not to store this and to instead use PY_TYPE(self) and upcast that, + // but that breaks when someone inherits from our class in Python. struct Dtool_PyTypedObject *_My_Type; + + // Pointer to the underlying C++ object. + void *_ptr_to_object; + + // This is always set to PY_PANDA_SIGNATURE, so that we can quickly + // detect whether an object is a Panda object. + unsigned short _signature; + + // True if we own the pointer and should delete it or unref it. + bool _memory_rules; + + // True if this is a "const" pointer. + bool _is_const; }; -#endif //////////////////////////////////////////////////////////////////////// // A Offset Dictionary Defining How to read the Above Object.. @@ -188,144 +197,85 @@ struct Dtool_PyTypedObject { PyTypeObject _PyType; // My Class Level Features.. - const char *_name; // cpp name for the object - bool _Dtool_IsRunTimeCapable; // derived from TypedObject - ConvertFunctionType _Dtool_UpcastInterface; // The Upcast Function By Slot - ConvertFunctionType1 _Dtool_DowncastInterface; // The Downcast Function By Slot - FreeFunction _Dtool_FreeInstance; - PyModuleClassInit _Dtool_ClassInit; // The module init function pointer - InitNoCoerce _Dtool_InitNoCoerce; // A variant of the constructor that does not attempt to perform coercion of its arguments. + UpcastFunction _Dtool_UpcastInterface; // The Upcast Function By Slot + DowncastFunction _Dtool_DowncastInterface; // The Downcast Function By Slot + + // May be TypeHandle::none() to indicate a non-TypedObject class. + TypeHandle _type; // some convenience functions.. inline PyTypeObject &As_PyTypeObject() { return _PyType; }; inline PyObject &As_PyObject() { return (PyObject &)_PyType; }; }; -//////////////////////////////////////////////////////////////////////// -// Macros from Hell.. May want to just add this to the code generator.. -//////////////////////////////////////////////////////////////////////// -#define Define_Dtool_PyTypedObject(MODULE_NAME, CLASS_NAME, PUBLIC_NAME) \ - EXPORT_THIS Dtool_PyTypedObject Dtool_##CLASS_NAME = \ - { \ - { \ - PyVarObject_HEAD_INIT(NULL, 0) \ - #MODULE_NAME "." #PUBLIC_NAME, /*type name with module */ \ - sizeof(Dtool_PyInstDef), /* tp_basicsize */ \ - 0, /* tp_itemsize */ \ - &Dtool_Deallocate_General, /* tp_dealloc */ \ - 0, /* tp_print */ \ - 0, /* tp_getattr */ \ - 0, /* tp_setattr */ \ - 0, /* tp_compare */ \ - 0, /* tp_repr */ \ - &Dtool_PyNumberMethods_##CLASS_NAME, /* tp_as_number */ \ - &Dtool_PySequenceMethods_##CLASS_NAME, /* tp_as_sequence */ \ - &Dtool_PyMappingMethods_##CLASS_NAME, /* tp_as_mapping */ \ - 0, /* tp_hash */ \ - 0, /* tp_call */ \ - 0, /* tp_str */ \ - PyObject_GenericGetAttr, /* tp_getattro */ \ - PyObject_GenericSetAttr, /* tp_setattro */ \ - &Dtool_PyBufferProcs_##CLASS_NAME, /* tp_as_buffer */ \ - (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_CHECKTYPES), /* tp_flags */ \ - 0, /* tp_doc */ \ - 0, /* tp_traverse */ \ - 0, /* tp_clear */ \ - 0, /* tp_richcompare */ \ - 0, /* tp_weaklistoffset */ \ - 0, /* tp_iter */ \ - 0, /* tp_iternext */ \ - Dtool_Methods_##CLASS_NAME, /* tp_methods */ \ - standard_type_members, /* tp_members */ \ - 0, /* tp_getset */ \ - 0, /* tp_base */ \ - 0, /* tp_dict */ \ - 0, /* tp_descr_get */ \ - 0, /* tp_descr_set */ \ - 0, /* tp_dictoffset */ \ - Dtool_Init_##CLASS_NAME, /* tp_init */ \ - PyType_GenericAlloc, /* tp_alloc */ \ - Dtool_new_##CLASS_NAME, /* tp_new */ \ - PyObject_Del, /* tp_free */ \ - }, \ - #CLASS_NAME, \ - false, \ - Dtool_UpcastInterface_##CLASS_NAME, \ - Dtool_DowncastInterface_##CLASS_NAME, \ - Dtool_FreeInstance_##CLASS_NAME, \ - Dtool_PyModuleClassInit_##CLASS_NAME, \ - Dtool_InitNoCoerce_##CLASS_NAME \ - }; - -#define Define_Dtool_Class(MODULE_NAME, CLASS_NAME, PUBLIC_NAME) \ - static PyNumberMethods Dtool_PyNumberMethods_##CLASS_NAME = {0}; \ - static PySequenceMethods Dtool_PySequenceMethods_##CLASS_NAME = {0}; \ - static PyMappingMethods Dtool_PyMappingMethods_##CLASS_NAME = {0}; \ - static PyBufferProcs Dtool_PyBufferProcs_##CLASS_NAME = {0}; \ - Define_Dtool_PyTypedObject(MODULE_NAME, CLASS_NAME, PUBLIC_NAME) +// This is now simply a forward declaration. The actual definition is created +// by the code generator. +#define Define_Dtool_Class(MODULE_NAME, CLASS_NAME, PUBLIC_NAME) \ + extern Dtool_PyTypedObject Dtool_##CLASS_NAME; //////////////////////////////////////////////////////////////////////// -// The Fast Deallocator.. for Our instances.. -//////////////////////////////////////////////////////////////////////// -inline void Dtool_Deallocate_General(PyObject * self) { - ((Dtool_PyInstDef *)self)->_My_Type->_Dtool_FreeInstance(self); - Py_TYPE(self)->tp_free(self); -} - -//////////////////////////////////////////////////////////////////////// -// More Macro(s) to Implement class functions.. Usually used if C++ needs type information +// More Macro(s) to Implement class functions.. Usually used if C++ needs type information //////////////////////////////////////////////////////////////////////// #define Define_Dtool_new(CLASS_NAME,CNAME)\ -PyObject *Dtool_new_##CLASS_NAME(PyTypeObject *type, PyObject *args, PyObject *kwds)\ -{\ - PyObject *self = type->tp_alloc(type, 0);\ - ((Dtool_PyInstDef *)self)->_signature = PY_PANDA_SIGNATURE;\ - ((Dtool_PyInstDef *)self)->_ptr_to_object = NULL;\ - ((Dtool_PyInstDef *)self)->_memory_rules = false;\ - ((Dtool_PyInstDef *)self)->_is_const = false;\ - ((Dtool_PyInstDef *)self)->_My_Type = &Dtool_##CLASS_NAME;\ - return self;\ +PyObject *Dtool_new_##CLASS_NAME(PyTypeObject *type, PyObject *args, PyObject *kwds) {\ + (void) args; (void) kwds;\ + PyObject *self = type->tp_alloc(type, 0);\ + ((Dtool_PyInstDef *)self)->_signature = PY_PANDA_SIGNATURE;\ + ((Dtool_PyInstDef *)self)->_My_Type = &Dtool_##CLASS_NAME;\ + return self;\ } +// The following used to be in the above macro, but it doesn't seem to +// be necessary as tp_alloc memsets the object to 0. + //((Dtool_PyInstDef *)self)->_ptr_to_object = NULL;\ + //((Dtool_PyInstDef *)self)->_memory_rules = false;\ + //((Dtool_PyInstDef *)self)->_is_const = false;\ + //////////////////////////////////////////////////////////////////////// /// Delete functions.. //////////////////////////////////////////////////////////////////////// #ifdef NDEBUG #define Define_Dtool_FreeInstance_Private(CLASS_NAME,CNAME)\ -static void Dtool_FreeInstance_##CLASS_NAME(PyObject *self)\ -{\ +static void Dtool_FreeInstance_##CLASS_NAME(PyObject *self) {\ + Py_TYPE(self)->tp_free(self);\ } #else // NDEBUG #define Define_Dtool_FreeInstance_Private(CLASS_NAME,CNAME)\ -static void Dtool_FreeInstance_##CLASS_NAME(PyObject *self)\ -{\ - if(((Dtool_PyInstDef *)self)->_ptr_to_object != NULL)\ - if(((Dtool_PyInstDef *)self)->_memory_rules)\ - {\ - cerr << "Detected leak for " << #CLASS_NAME \ - << " which interrogate cannot delete.\n"; \ - }\ +static void Dtool_FreeInstance_##CLASS_NAME(PyObject *self) {\ + if (((Dtool_PyInstDef *)self)->_ptr_to_object != NULL) {\ + if (((Dtool_PyInstDef *)self)->_memory_rules) {\ + cerr << "Detected leak for " << #CLASS_NAME \ + << " which interrogate cannot delete.\n"; \ + }\ + }\ + Py_TYPE(self)->tp_free(self);\ } #endif // NDEBUG #define Define_Dtool_FreeInstance(CLASS_NAME,CNAME)\ -static void Dtool_FreeInstance_##CLASS_NAME(PyObject *self)\ -{\ - if(((Dtool_PyInstDef *)self)->_ptr_to_object != NULL)\ - if(((Dtool_PyInstDef *)self)->_memory_rules)\ - {\ - delete ((CNAME *)((Dtool_PyInstDef *)self)->_ptr_to_object);\ - }\ +static void Dtool_FreeInstance_##CLASS_NAME(PyObject *self) {\ + if (((Dtool_PyInstDef *)self)->_ptr_to_object != NULL) {\ + if (((Dtool_PyInstDef *)self)->_memory_rules) {\ + delete ((CNAME *)((Dtool_PyInstDef *)self)->_ptr_to_object);\ + }\ + }\ + Py_TYPE(self)->tp_free(self);\ } #define Define_Dtool_FreeInstanceRef(CLASS_NAME,CNAME)\ -static void Dtool_FreeInstance_##CLASS_NAME(PyObject *self)\ -{\ - if(((Dtool_PyInstDef *)self)->_ptr_to_object != NULL)\ - if(((Dtool_PyInstDef *)self)->_memory_rules)\ - {\ - unref_delete((CNAME *)((Dtool_PyInstDef *)self)->_ptr_to_object);\ - }\ +static void Dtool_FreeInstance_##CLASS_NAME(PyObject *self) {\ + if (((Dtool_PyInstDef *)self)->_ptr_to_object != NULL) {\ + if (((Dtool_PyInstDef *)self)->_memory_rules) {\ + unref_delete((CNAME *)((Dtool_PyInstDef *)self)->_ptr_to_object);\ + }\ + }\ + Py_TYPE(self)->tp_free(self);\ +} + +#define Define_Dtool_Simple_FreeInstance(CLASS_NAME, CNAME)\ +static void Dtool_FreeInstance_##CLASS_NAME(PyObject *self) {\ + ((Dtool_InstDef_##CLASS_NAME *)self)->_value.~##CLASS_NAME();\ + Py_TYPE(self)->tp_free(self);\ } //////////////////////////////////////////////////////////////////////// @@ -333,38 +283,124 @@ static void Dtool_FreeInstance_##CLASS_NAME(PyObject *self)\ //////////////////////////////////////////////////////////////////////// EXPCL_DTOOLCONFIG bool DtoolCanThisBeAPandaInstance(PyObject *self); +/////////////////////////////////////////////////////////////////////////////// +// ** HACK ** allert.. +// +// Need to keep a runtime type dictionary ... that is forward declared of typed object. +// We rely on the fact that typed objects are uniquly defined by an integer. +// +/////////////////////////////////////////////////////////////////////////////// + +EXPCL_DTOOLCONFIG void RegisterRuntimeClass(Dtool_PyTypedObject *otype, int class_id); + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +EXPCL_DTOOLCONFIG Dtool_PyTypedObject *Dtool_RuntimeTypeDtoolType(int type); + //////////////////////////////////////////////////////////////////////// // Function : DTOOL_Call_ExtractThisPointerForType // -// These are the wrappers that allow for down and upcast from type .. +// These are the wrappers that allow for down and upcast from type .. // needed by the Dtool py interface.. Be very careful if you muck // with these as the generated code depends on how this is set // up.. //////////////////////////////////////////////////////////////////////// EXPCL_DTOOLCONFIG void DTOOL_Call_ExtractThisPointerForType(PyObject *self, Dtool_PyTypedObject * classdef, void ** answer); -EXPCL_DTOOLCONFIG void *DTOOL_Call_GetPointerThisClass(PyObject *self, Dtool_PyTypedObject *classdef, int param, const string &function_name, bool const_ok, PyObject **coerced, bool report_errors); - -EXPCL_DTOOLCONFIG void *DTOOL_Call_GetPointerThisClass(PyObject *self, Dtool_PyTypedObject *classdef, int param, const string &function_name, bool const_ok, PyObject **coerced); +EXPCL_DTOOLCONFIG void *DTOOL_Call_GetPointerThisClass(PyObject *self, Dtool_PyTypedObject *classdef, int param, const string &function_name, bool const_ok, bool report_errors); EXPCL_DTOOLCONFIG void *DTOOL_Call_GetPointerThis(PyObject *self); +EXPCL_DTOOLCONFIG bool Dtool_Call_ExtractThisPointer(PyObject *self, Dtool_PyTypedObject &classdef, void **answer); + +EXPCL_DTOOLCONFIG bool Dtool_Call_ExtractThisPointer_NonConst(PyObject *self, Dtool_PyTypedObject &classdef, + void **answer, const char *method_name); + +template INLINE bool DTOOL_Call_ExtractThisPointer(PyObject *self, T *&into) { + if (DtoolCanThisBeAPandaInstance(self)) { + Dtool_PyTypedObject *target_class = Dtool_RuntimeTypeDtoolType(get_type_handle(T).get_index()); + if (target_class != NULL) { + into = (T*) ((Dtool_PyInstDef *)self)->_My_Type->_Dtool_UpcastInterface(self, target_class); + return (into != NULL); + } + } + into = NULL; + return false; +} + +// Functions related to error reporting. + +#ifdef NDEBUG +// _PyErr_OCCURRED is an undocumented inline version of PyErr_Occurred. +#define Dtool_CheckErrorOccurred() (_PyErr_OCCURRED() != NULL) +#else +EXPCL_DTOOLCONFIG bool Dtool_CheckErrorOccurred(); +#endif + +EXPCL_DTOOLCONFIG PyObject *Dtool_Raise_AssertionError(); +EXPCL_DTOOLCONFIG PyObject *Dtool_Raise_TypeError(const char *message); +EXPCL_DTOOLCONFIG PyObject *Dtool_Raise_ArgTypeError(PyObject *obj, int param, const char *function_name, const char *type_name); + +EXPCL_DTOOLCONFIG PyObject *_Dtool_Raise_BadArgumentsError(); +#ifdef NDEBUG +// Define it to a function that just prints a generic message. +#define Dtool_Raise_BadArgumentsError(x) _Dtool_Raise_BadArgumentsError() +#else +// Expand this to a TypeError listing all of the overloads. +#define Dtool_Raise_BadArgumentsError(x) Dtool_Raise_TypeError("Arguments must match:\n" x) +#endif + +EXPCL_DTOOLCONFIG PyObject *_Dtool_Return_None(); +EXPCL_DTOOLCONFIG PyObject *Dtool_Return_Bool(bool value); + +#ifdef NDEBUG +#define Dtool_Return_None() (_PyErr_OCCURRED() != NULL ? NULL : (Py_INCREF(Py_None), Py_None)) +#else +#define Dtool_Return_None() _Dtool_Return_None() +#endif + //////////////////////////////////////////////////////////////////////// // Function : DTool_CreatePyInstanceTyped // -// this function relies on the behavior of typed objects in the panda system. +// this function relies on the behavior of typed objects in the panda system. // //////////////////////////////////////////////////////////////////////// EXPCL_DTOOLCONFIG PyObject *DTool_CreatePyInstanceTyped(void *local_this_in, Dtool_PyTypedObject &known_class_type, bool memory_rules, bool is_const, int RunTimeType); //////////////////////////////////////////////////////////////////////// -// DTool_CreatePyInstance .. wrapper function to finalize the existance of a general +// DTool_CreatePyInstance .. wrapper function to finalize the existance of a general // dtool py instance.. //////////////////////////////////////////////////////////////////////// EXPCL_DTOOLCONFIG PyObject *DTool_CreatePyInstance(void *local_this, Dtool_PyTypedObject &in_classdef, bool memory_rules, bool is_const); +// These template methods allow use when the Dtool_PyTypedObject is not known. +// They require a get_class_type() to be defined for the class. +template INLINE PyObject *DTool_CreatePyInstance(const T *obj, bool memory_rules) { + Dtool_PyTypedObject *known_class = Dtool_RuntimeTypeDtoolType(get_type_handle(T).get_index()); + nassertr(known_class != NULL, NULL); + return DTool_CreatePyInstance((void*) obj, *known_class, memory_rules, true); +} + +template INLINE PyObject *DTool_CreatePyInstance(T *obj, bool memory_rules) { + Dtool_PyTypedObject *known_class = Dtool_RuntimeTypeDtoolType(get_type_handle(T).get_index()); + nassertr(known_class != NULL, NULL); + return DTool_CreatePyInstance((void*) obj, *known_class, memory_rules, false); +} + +template INLINE PyObject *DTool_CreatePyInstanceTyped(const T *obj, bool memory_rules) { + Dtool_PyTypedObject *known_class = Dtool_RuntimeTypeDtoolType(get_type_handle(T).get_index()); + nassertr(known_class != NULL, NULL); + return DTool_CreatePyInstanceTyped((void*) obj, *known_class, memory_rules, true, obj->get_type().get_index()); +} + +template INLINE PyObject *DTool_CreatePyInstanceTyped(T *obj, bool memory_rules) { + Dtool_PyTypedObject *known_class = Dtool_RuntimeTypeDtoolType(get_type_handle(T).get_index()); + nassertr(known_class != NULL, NULL); + return DTool_CreatePyInstanceTyped((void*) obj, *known_class, memory_rules, false, obj->get_type().get_index()); +} + /////////////////////////////////////////////////////////////////////////////// -// Macro(s) class definition .. Used to allocate storage and +// Macro(s) class definition .. Used to allocate storage and // init some values for a Dtool Py Type object. ///////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// @@ -372,13 +408,10 @@ EXPCL_DTOOLCONFIG PyObject *DTool_CreatePyInstance(void *local_this, Dtool_PyTyp #define Define_Module_Class_Internal(MODULE_NAME,CLASS_NAME,CNAME)\ extern EXPORT_THIS Dtool_PyTypedObject Dtool_##CLASS_NAME;\ -extern struct PyMethodDef Dtool_Methods_##CLASS_NAME[];\ int Dtool_Init_##CLASS_NAME(PyObject *self, PyObject *args, PyObject *kwds);\ -int Dtool_InitNoCoerce_##CLASS_NAME(PyObject *self, PyObject *args);\ PyObject * Dtool_new_##CLASS_NAME(PyTypeObject *type, PyObject *args, PyObject *kwds);\ void * Dtool_UpcastInterface_##CLASS_NAME(PyObject *self, Dtool_PyTypedObject *requested_type);\ -void * Dtool_DowncastInterface_##CLASS_NAME(void *self, Dtool_PyTypedObject *requested_type);\ -void Dtool_PyModuleClassInit_##CLASS_NAME(PyObject *module); +void * Dtool_DowncastInterface_##CLASS_NAME(void *self, Dtool_PyTypedObject *requested_type); /////////////////////////////////////////////////////////////////////////////// #define Define_Module_Class(MODULE_NAME,CLASS_NAME,CNAME,PUBLIC_NAME)\ @@ -408,14 +441,13 @@ Define_Dtool_new(CLASS_NAME,CNAME)\ Define_Dtool_FreeInstanceRef(CLASS_NAME,CNAME)\ Define_Dtool_Class(MODULE_NAME,CLASS_NAME,PUBLIC_NAME) - /////////////////////////////////////////////////////////////////////////////// /// Th Finalizer for simple instances.. /////////////////////////////////////////////////////////////////////////////// EXPCL_DTOOLCONFIG int DTool_PyInit_Finalize(PyObject *self, void *This, Dtool_PyTypedObject *type, bool memory_rules, bool is_const); /////////////////////////////////////////////////////////////////////////////// -/// A heler function to glu methed definition together .. that can not be done at +/// A heler function to glu methed definition together .. that can not be done at // code generation time becouse of multiple generation passes in interigate.. // /////////////////////////////////////////////////////////////////////////////// @@ -424,22 +456,8 @@ typedef std::map MethodDefmap; EXPCL_DTOOLCONFIG void Dtool_Accum_MethDefs(PyMethodDef in[], MethodDefmap &themap); /////////////////////////////////////////////////////////////////////////////// -// ** HACK ** allert.. -// -// Need to keep a runtime type dictionary ... that is forward declared of typed object. -// We rely on the fact that typed objects are uniquly defined by an integer. -// -/////////////////////////////////////////////////////////////////////////////// - -EXPCL_DTOOLCONFIG void RegisterRuntimeClass(Dtool_PyTypedObject * otype, int class_id); - -/////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////// -EXPCL_DTOOLCONFIG Dtool_PyTypedObject *Dtool_RuntimeTypeDtoolType(int type); - -/////////////////////////////////////////////////////////////////////////////// -//// We need a way to runtime merge compile units into a python "Module" .. this is done with the -/// fallowing structors and code.. along with the support of interigate_module +//// We need a way to runtime merge compile units into a python "Module" .. this is done with the +/// fallowing structors and code.. along with the support of interigate_module /////////////////////////////////////////////////////////////////////////////// struct LibraryDef { typedef void (*ConstantFunction)(PyObject *); @@ -456,7 +474,7 @@ EXPCL_DTOOLCONFIG PyObject *Dtool_PyModuleInitHelper(LibraryDef *defs[], const c #endif /////////////////////////////////////////////////////////////////////////////// -/// HACK.... Be carefull +/// HACK.... Be carefull // // Dtool_BorrowThisReference // This function can be used to grab the "THIS" pointer from an object and use it @@ -476,7 +494,7 @@ EXPCL_DTOOLCONFIG long DTool_HashKey(PyObject * inst) { long outcome = (long)inst; PyObject * func = PyObject_GetAttrString(inst, "__hash__"); - if (func == NULL) + if (func == NULL) { if(DtoolCanThisBeAPandaInstance(inst)) if(((Dtool_PyInstDef *)inst)->_ptr_to_object != NULL) @@ -488,13 +506,13 @@ EXPCL_DTOOLCONFIG long DTool_HashKey(PyObject * inst) Py_DECREF(func); if (res == NULL) return -1; - if (PyInt_Check(res)) + if (PyInt_Check(res)) { outcome = PyInt_AsLong(res); if (outcome == -1) outcome = -2; } - else + else { PyErr_SetString(PyExc_TypeError, "__hash__() should return an int"); @@ -531,11 +549,15 @@ copy_from_copy_constructor(PyObject *self); EXPCL_DTOOLCONFIG PyObject * map_deepcopy_to_copy(PyObject *self, PyObject *args); +#if PY_MAJOR_VERSION < 3 +// In the Python 3 case, it is defined as a macro, at the beginning of this file. EXPCL_DTOOLCONFIG PyObject * PyLongOrInt_FromUnsignedLong(unsigned long value); +#endif EXPCL_DTOOLCONFIG extern struct Dtool_PyTypedObject Dtool_DTOOL_SUPER_BASE; +EXPCL_DTOOLCONFIG extern void Dtool_PyModuleClassInit_DTOOL_SUPER_BASE(PyObject *module); #endif // HAVE_PYTHON && !CPPPARSER -#endif // PY_PANDA_H_ +#endif // PY_PANDA_H_ diff --git a/dtool/src/parser-inc/Python.h b/dtool/src/parser-inc/Python.h index 329c8ed5bd..de17409f53 100755 --- a/dtool/src/parser-inc/Python.h +++ b/dtool/src/parser-inc/Python.h @@ -26,14 +26,31 @@ typedef _object PyObject; struct _typeobject; typedef _typeobject PyTypeObject; -struct PyStringObject; -struct PyUnicodeObject; +typedef struct {} PyStringObject; +typedef struct {} PyUnicodeObject; class PyThreadState; typedef int Py_ssize_t; struct Py_buffer; +// We need to define these accurately since interrogate may want to +// write these out to default value assignments. +PyObject _Py_NoneStruct; +PyObject _Py_TrueStruct; +#define Py_None (&_Py_NoneStruct) +#define Py_True ((PyObject *) &_Py_TrueStruct) + +#if PY_MAJOR_VERSION >= 3 +PyObject _Py_ZeroStruct; +#define Py_False ((PyObject *) &_Py_ZeroStruct) +#else +PyObject _Py_FalseStruct; +#define Py_False ((PyObject *) &_Py_FalseStruct) +#endif + // This file defines PY_VERSION_HEX, which is used in some places. #include "patchlevel.h" +typedef void *visitproc; + #endif // PYTHON_H diff --git a/dtool/src/parser-inc/cg.h b/dtool/src/parser-inc/cg.h index 5af0889578..a0adab732b 100755 --- a/dtool/src/parser-inc/cg.h +++ b/dtool/src/parser-inc/cg.h @@ -20,11 +20,12 @@ #ifndef CG_H #define CG_H -typedef int CGcontext; -typedef int CGprogram; -typedef int CGparameter; -typedef int CGprofile; -typedef int CGerror; +typedef struct _CGcontext *CGcontext; +typedef struct _CGcontext *CGcontext; +typedef struct _CGprogram *CGprogram; +typedef struct _CGparameter *CGparameter; + +typedef enum {} CGprofile; +typedef enum {} CGerror; #endif - diff --git a/dtool/src/parser-inc/iostream b/dtool/src/parser-inc/iostream index 4afc03ddef..87fd92ebcd 100644 --- a/dtool/src/parser-inc/iostream +++ b/dtool/src/parser-inc/iostream @@ -36,6 +36,13 @@ __published: }; enum openmode { }; + // Don't define these lest interrogate get tempted to actually + // substitute in the values, which are implementation-defined. + static const openmode app; + static const openmode binary; + static const openmode in; + static const openmode out; + static const openmode trunc; }; class ios : public ios_base { __published: diff --git a/dtool/src/parser-inc/ssl.h b/dtool/src/parser-inc/ssl.h index 24e968ed5f..d43aca6090 100644 --- a/dtool/src/parser-inc/ssl.h +++ b/dtool/src/parser-inc/ssl.h @@ -10,6 +10,6 @@ struct X509; struct X509_STORE; struct X509_NAME; struct SSL; -#define STACK_OF(num) STACK +#define STACK_OF(type) struct stack_st_##type #endif diff --git a/dtool/src/parser-inc/stdtypedefs.h b/dtool/src/parser-inc/stdtypedefs.h index 14db248dd5..34b9b0b4c6 100644 --- a/dtool/src/parser-inc/stdtypedefs.h +++ b/dtool/src/parser-inc/stdtypedefs.h @@ -38,7 +38,11 @@ typedef unsigned long ulong; typedef unsigned short ushort; typedef unsigned char uchar; +#ifdef __cplusplus +#define NULL 0 +#else #define NULL ((void *)0) +#endif typedef int fd_set; diff --git a/dtool/src/parser-inc/string b/dtool/src/parser-inc/string index f2a3c33e9e..0ec8e184ba 100644 --- a/dtool/src/parser-inc/string +++ b/dtool/src/parser-inc/string @@ -25,6 +25,9 @@ template class basic_string { public: + typedef typename size_t size_type; + static const size_t npos; + basic_string(); basic_string(const basic_string ©); void operator = (const basic_string ©); diff --git a/dtool/src/parser-inc/windows.h b/dtool/src/parser-inc/windows.h index 334b62c5fb..22f9f2ffb7 100644 --- a/dtool/src/parser-inc/windows.h +++ b/dtool/src/parser-inc/windows.h @@ -26,7 +26,7 @@ typedef long DWORD; typedef long LONG; typedef long UINT; typedef unsigned long ULONG; -typedef signed __int64 LONGLONG; +typedef signed long long LONGLONG; typedef long HRESULT; typedef int CRITICAL_SECTION; typedef int HANDLE; @@ -45,12 +45,12 @@ typedef struct _STICKYKEYS STICKYKEYS; typedef struct _TOGGLEKEYS TOGGLEKEYS; typedef struct _FILTERKEYS FILTERKEYS; -#define CALLBACK +#define CALLBACK #define WINAPI union LARGE_INTEGER { - __int64 QuadPart; + long long QuadPart; }; class IGraphBuilder; diff --git a/dtool/src/parser-inc/winsock2.h b/dtool/src/parser-inc/winsock2.h index 474a821987..3033be48f2 100644 --- a/dtool/src/parser-inc/winsock2.h +++ b/dtool/src/parser-inc/winsock2.h @@ -2,16 +2,8 @@ #define _WINSOCK2API_ #define _WINSOCKAPI_ /* Prevent inclusion of winsock.h in windows.h */ -typedef int SOCKET ; +typedef int SOCKET; -struct sockaddr_in -{ -}; - - -typedef struct fd_set { - unsigned int fd_count; /* how many are SET? */ - SOCKET fd_array[10]; /* an array of SOCKETs */ -} fd_set; +struct sockaddr_in; #endif diff --git a/dtool/src/prc/notify.cxx b/dtool/src/prc/notify.cxx index b8fe219e9b..97964b775f 100644 --- a/dtool/src/prc/notify.cxx +++ b/dtool/src/prc/notify.cxx @@ -182,54 +182,6 @@ get_assert_handler() const { return _assert_handler; } -//////////////////////////////////////////////////////////////////// -// Function: Notify::has_assert_failed -// Access: Public -// Description: Returns true if an assertion test has failed (and not -// been ignored) since the last call to -// clear_assert_failed(). -// -// When an assertion test fails, the assert handler -// may decide either to abort, return, or ignore the -// assertion. Naturally, if it decides to abort, this -// flag is irrelevant. If it chooses to ignore the -// assertion, the flag is not set. However, if the -// assert handler chooses to return out of the -// function (the normal case), it will also set this -// flag to indicate that an assertion failure has -// occurred. -// -// This will also be the behavior in the absence of a -// user-defined assert handler. -//////////////////////////////////////////////////////////////////// -bool Notify:: -has_assert_failed() const { - return _assert_failed; -} - -//////////////////////////////////////////////////////////////////// -// Function: Notify::get_assert_error_message -// Access: Public -// Description: Returns the error message that corresponds to the -// assertion that most recently failed. -//////////////////////////////////////////////////////////////////// -const string &Notify:: -get_assert_error_message() const { - return _assert_error_message; -} - -//////////////////////////////////////////////////////////////////// -// Function: Notify::clear_assert_failed -// Access: Public -// Description: Resets the assert_failed flag that is set whenever an -// assertion test fails. See has_assert_failed(). -//////////////////////////////////////////////////////////////////// -void Notify:: -clear_assert_failed() { - _assert_failed = false; -} - - //////////////////////////////////////////////////////////////////// // Function: Notify::get_top_category // Access: Public @@ -269,7 +221,7 @@ get_category(const string &basename, NotifyCategory *parent_category) { } } - pair result = + pair result = _categories.insert(Categories::value_type(fullname, (NotifyCategory *)NULL)); bool inserted = result.second; @@ -458,7 +410,7 @@ assert_failure(const char *expression, int line, // the debugger? abort() doesn't do it. We used to be able to // assert(false), but in VC++ 7 that just throws an exception, and // an uncaught exception just exits, without offering to open the - // debugger. + // debugger. // DebugBreak() seems to be provided for this purpose, but it // doesn't seem to work properly either, since we don't seem to @@ -567,7 +519,7 @@ config_initialized() { dup2(logfile_fd, STDOUT_FILENO); dup2(logfile_fd, STDERR_FILENO); close(logfile_fd); - + set_ostream_ptr(&cerr, false); } #else diff --git a/dtool/src/prc/pnotify.I b/dtool/src/prc/pnotify.I index 4960718d48..06919b8ae8 100644 --- a/dtool/src/prc/pnotify.I +++ b/dtool/src/prc/pnotify.I @@ -12,3 +12,50 @@ // //////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: Notify::has_assert_failed +// Access: Public +// Description: Returns true if an assertion test has failed (and not +// been ignored) since the last call to +// clear_assert_failed(). +// +// When an assertion test fails, the assert handler +// may decide either to abort, return, or ignore the +// assertion. Naturally, if it decides to abort, this +// flag is irrelevant. If it chooses to ignore the +// assertion, the flag is not set. However, if the +// assert handler chooses to return out of the +// function (the normal case), it will also set this +// flag to indicate that an assertion failure has +// occurred. +// +// This will also be the behavior in the absence of a +// user-defined assert handler. +//////////////////////////////////////////////////////////////////// +INLINE bool Notify:: +has_assert_failed() const { + return _assert_failed; +} + +//////////////////////////////////////////////////////////////////// +// Function: Notify::get_assert_error_message +// Access: Public +// Description: Returns the error message that corresponds to the +// assertion that most recently failed. +//////////////////////////////////////////////////////////////////// +INLINE const string &Notify:: +get_assert_error_message() const { + return _assert_error_message; +} + +//////////////////////////////////////////////////////////////////// +// Function: Notify::clear_assert_failed +// Access: Public +// Description: Resets the assert_failed flag that is set whenever an +// assertion test fails. See has_assert_failed(). +//////////////////////////////////////////////////////////////////// +INLINE void Notify:: +clear_assert_failed() { + _assert_failed = false; +} diff --git a/dtool/src/prc/pnotify.h b/dtool/src/prc/pnotify.h index b45747860b..53dd7d11f7 100644 --- a/dtool/src/prc/pnotify.h +++ b/dtool/src/prc/pnotify.h @@ -51,9 +51,9 @@ PUBLISHED: bool has_assert_handler() const; AssertHandler *get_assert_handler() const; - bool has_assert_failed() const; - const string &get_assert_error_message() const; - void clear_assert_failed(); + INLINE bool has_assert_failed() const; + INLINE const string &get_assert_error_message() const; + INLINE void clear_assert_failed(); NotifyCategory *get_top_category(); NotifyCategory *get_category(const string &basename, diff --git a/dtool/src/pystub/pystub.cxx b/dtool/src/pystub/pystub.cxx index 305eabd596..2c0dd9303b 100644 --- a/dtool/src/pystub/pystub.cxx +++ b/dtool/src/pystub/pystub.cxx @@ -79,6 +79,7 @@ extern "C" { EXPCL_PYSTUB int PyLong_AsUnsignedLongLong(...); EXPCL_PYSTUB int PyLong_FromLong(...); EXPCL_PYSTUB int PyLong_FromLongLong(...); + EXPCL_PYSTUB int PyLong_FromSize_t(...); EXPCL_PYSTUB int PyLong_FromUnsignedLong(...); EXPCL_PYSTUB int PyLong_FromUnsignedLongLong(...); EXPCL_PYSTUB int PyLong_Type(...); @@ -122,6 +123,7 @@ extern "C" { EXPCL_PYSTUB int PySequence_Tuple(...); EXPCL_PYSTUB int PyString_AsString(...); EXPCL_PYSTUB int PyString_AsStringAndSize(...); + EXPCL_PYSTUB int PyString_FromFormat(...); EXPCL_PYSTUB int PyString_FromString(...); EXPCL_PYSTUB int PyString_FromStringAndSize(...); EXPCL_PYSTUB int PyString_InternFromString(...); @@ -153,6 +155,8 @@ extern "C" { EXPCL_PYSTUB int PyUnicode_AsUTF8(...); EXPCL_PYSTUB int PyUnicode_AsUTF8AndSize(...); EXPCL_PYSTUB int PyUnicode_AsWideChar(...); + EXPCL_PYSTUB int PyUnicode_AsWideCharString(...); + EXPCL_PYSTUB int PyUnicode_FromFormat(...); EXPCL_PYSTUB int PyUnicode_FromString(...); EXPCL_PYSTUB int PyUnicode_FromStringAndSize(...); EXPCL_PYSTUB int PyUnicode_FromWideChar(...); @@ -192,6 +196,7 @@ extern "C" { EXPCL_PYSTUB extern void *PyExc_SystemExit; EXPCL_PYSTUB extern void *PyExc_TypeError; EXPCL_PYSTUB extern void *PyExc_ValueError; + EXPCL_PYSTUB extern void *_PyThreadState_Current; EXPCL_PYSTUB extern void *_Py_FalseStruct; EXPCL_PYSTUB extern void *_Py_NoneStruct; EXPCL_PYSTUB extern void *_Py_NotImplementedStruct; @@ -264,6 +269,7 @@ int PyLong_AsUnsignedLong(...) { return 0; } int PyLong_AsUnsignedLongLong(...) { return 0; } int PyLong_FromLong(...) { return 0; } int PyLong_FromLongLong(...) { return 0; } +int PyLong_FromSize_t(...) { return 0; } int PyLong_FromUnsignedLong(...) { return 0; } int PyLong_FromUnsignedLongLong(...) { return 0; } int PyLong_Type(...) { return 0; } @@ -307,6 +313,7 @@ int PySequence_Size(...) { return 0; } int PySequence_Tuple(...) { return 0; } int PyString_AsString(...) { return 0; } int PyString_AsStringAndSize(...) { return 0; } +int PyString_FromFormat(...) { return 0; } int PyString_FromString(...) { return 0; } int PyString_FromStringAndSize(...) { return 0; } int PyString_InternFromString(...) { return 0; } @@ -338,6 +345,8 @@ int PyUnicodeUCS4_GetSize(...) { return 0; } int PyUnicode_AsUTF8(...) { return 0; } int PyUnicode_AsUTF8AndSize(...) { return 0; } int PyUnicode_AsWideChar(...) { return 0; } +int PyUnicode_AsWideCharString(...) { return 0; } +int PyUnicode_FromFormat(...) { return 0; } int PyUnicode_FromString(...) { return 0; } int PyUnicode_FromStringAndSize(...) { return 0; } int PyUnicode_FromWideChar(...) { return 0; } @@ -382,6 +391,7 @@ void *PyExc_StopIteration = (void *)NULL; void *PyExc_SystemExit = (void *)NULL; void *PyExc_TypeError = (void *)NULL; void *PyExc_ValueError = (void *)NULL; +void *_PyThreadState_Current = (void *)NULL; void *_Py_FalseStruct = (void *)NULL; void *_Py_NoneStruct = (void *)NULL; void *_Py_NotImplementedStruct = (void *)NULL; diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index 6782dddb3e..a19a8b9d55 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -1262,7 +1262,7 @@ def CompileIgate(woutd,wsrc,opts): cmd += ' -srcdir %s -I%s -Dvolatile -Dmutable' % (srcdir, srcdir) if (COMPILER=="MSVC"): - cmd += ' -DCPPPARSER -D__STDC__=1 -D__cplusplus -D__inline -longlong __int64 -D_X86_ -DWIN32_VC -DWIN32 -D_WIN32' + cmd += ' -DCPPPARSER -D__STDC__=1 -D__cplusplus -D__inline -D_X86_ -DWIN32_VC -DWIN32 -D_WIN32' if GetTargetArch() == 'x64': cmd += ' -DWIN64_VC -DWIN64 -D_WIN64' # NOTE: this 1600 value is the version number for VC2010. diff --git a/makepanda/makepandacore.py b/makepanda/makepandacore.py index 0c9df74508..74d448ada3 100644 --- a/makepanda/makepandacore.py +++ b/makepanda/makepandacore.py @@ -2463,13 +2463,16 @@ def CopyPythonTree(dstdir, srcdir, lib2to3_fixers=[]): for fixer in lib2to3_fixers: lib2to3_args += ['-f', fixer] + exclude_files = set(VCS_FILES) + exclude_files.add('panda3d.py') + refactor = [] for entry in os.listdir(srcdir): srcpth = os.path.join(srcdir, entry) dstpth = os.path.join(dstdir, entry) - if (os.path.isfile(srcpth)): + if os.path.isfile(srcpth): base, ext = os.path.splitext(entry) - if entry not in VCS_FILES and ext not in SUFFIX_INC + ['.pyc', '.pyo']: + if entry not in exclude_files and ext not in SUFFIX_INC + ['.pyc', '.pyo']: if (NeedsBuild([dstpth], [srcpth])): WriteBinaryFile(dstpth, ReadBinaryFile(srcpth)) diff --git a/panda/src/display/graphicsWindowInputDevice.I b/panda/src/display/graphicsWindowInputDevice.I index fe04d98b42..3763d7baa4 100644 --- a/panda/src/display/graphicsWindowInputDevice.I +++ b/panda/src/display/graphicsWindowInputDevice.I @@ -84,7 +84,7 @@ get_raw_pointer() const { //////////////////////////////////////////////////////////////////// // Function: GraphicsWindowInputDevice::set_device_index // Access: Public -// Description: Set the device index. This is reported in pointer +// Description: Set the device index. This is reported in pointer // events. The device index will be equal to the position // of the GraphicsWindowInputDevice in the window's list. //////////////////////////////////////////////////////////////////// @@ -117,9 +117,116 @@ disable_pointer_events() { _pointer_events.clear(); } +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindowInputDevice::button_down +// Access: Published +// Description: Records that the indicated button has been depressed. +//////////////////////////////////////////////////////////////////// +INLINE void GraphicsWindowInputDevice:: +button_down(ButtonHandle button) { + button_down(button, ClockObject::get_global_clock()->get_frame_time()); +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindowInputDevice::button_resume_down +// Access: Published +// Description: Records that the indicated button was depressed +// earlier, and we only just detected the event after +// the fact. This is mainly useful for tracking the +// state of modifier keys. +//////////////////////////////////////////////////////////////////// +INLINE void GraphicsWindowInputDevice:: +button_resume_down(ButtonHandle button) { + button_resume_down(button, ClockObject::get_global_clock()->get_frame_time()); +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindowInputDevice::button_up +// Access: Published +// Description: Records that the indicated button has been released. +//////////////////////////////////////////////////////////////////// +INLINE void GraphicsWindowInputDevice:: +button_up(ButtonHandle button) { + button_up(button, ClockObject::get_global_clock()->get_frame_time()); +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindowInputDevice::keystroke +// Access: Published +// Description: Records that the indicated keystroke has been +// generated. +//////////////////////////////////////////////////////////////////// +INLINE void GraphicsWindowInputDevice:: +keystroke(int keycode) { + keystroke(keycode, ClockObject::get_global_clock()->get_frame_time()); +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindowInputDevice::focus_lost +// Access: Published +// Description: This should be called when the window focus is lost, +// so that we may miss upcoming button events +// (especially "up" events) for the next period of time. +// It generates keyboard and mouse "up" events for those +// buttons that we previously sent unpaired "down" +// events, so that the Panda application will believe +// all buttons are now released. +//////////////////////////////////////////////////////////////////// +INLINE void GraphicsWindowInputDevice:: +focus_lost() { + focus_lost(ClockObject::get_global_clock()->get_frame_time()); +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindowInputDevice::raw_button_down +// Access: Published +// Description: Records that the indicated button has been depressed. +//////////////////////////////////////////////////////////////////// +INLINE void GraphicsWindowInputDevice:: +raw_button_down(ButtonHandle button) { + raw_button_down(button, ClockObject::get_global_clock()->get_frame_time()); +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindowInputDevice::raw_button_up +// Access: Published +// Description: Records that the indicated button has been released. +//////////////////////////////////////////////////////////////////// +INLINE void GraphicsWindowInputDevice:: +raw_button_up(ButtonHandle button) { + raw_button_up(button, ClockObject::get_global_clock()->get_frame_time()); +} + //////////////////////////////////////////////////////////////////// // Function: GraphicsWindowInputDevice::set_pointer_in_window -// Access: Public +// Access: Published +// Description: To be called by a particular kind of GraphicsWindow +// to indicate that the pointer is within the window, at +// the given pixel coordinates. +//////////////////////////////////////////////////////////////////// +INLINE void GraphicsWindowInputDevice:: +set_pointer_in_window(double x, double y) { + // mutex is handled in set pointer .. convience function + set_pointer(true, x, y, ClockObject::get_global_clock()->get_frame_time()); +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindowInputDevice::set_pointer_out_of_window +// Access: Published +// Description: To be called by a particular kind of GraphicsWindow +// to indicate that the pointer is no longer within the +// window. +//////////////////////////////////////////////////////////////////// +INLINE void GraphicsWindowInputDevice:: +set_pointer_out_of_window() { + // mutex is handled in set pointer .. convience function + set_pointer(false, _mouse_data._xpos, _mouse_data._ypos, + ClockObject::get_global_clock()->get_frame_time()); +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindowInputDevice::set_pointer_in_window +// Access: Published // Description: To be called by a particular kind of GraphicsWindow // to indicate that the pointer is within the window, at // the given pixel coordinates. @@ -132,7 +239,7 @@ set_pointer_in_window(double x, double y, double time) { //////////////////////////////////////////////////////////////////// // Function: GraphicsWindowInputDevice::set_pointer_out_of_window -// Access: Public +// Access: Published // Description: To be called by a particular kind of GraphicsWindow // to indicate that the pointer is no longer within the // window. diff --git a/panda/src/display/graphicsWindowInputDevice.cxx b/panda/src/display/graphicsWindowInputDevice.cxx index bbf49c10ee..a1d4950205 100644 --- a/panda/src/display/graphicsWindowInputDevice.cxx +++ b/panda/src/display/graphicsWindowInputDevice.cxx @@ -94,7 +94,7 @@ pointer_and_keyboard(GraphicsWindow *host, const string &name) { // Description: //////////////////////////////////////////////////////////////////// GraphicsWindowInputDevice:: -GraphicsWindowInputDevice(const GraphicsWindowInputDevice ©) +GraphicsWindowInputDevice(const GraphicsWindowInputDevice ©) { *this = copy; } @@ -105,7 +105,7 @@ GraphicsWindowInputDevice(const GraphicsWindowInputDevice ©) // Description: //////////////////////////////////////////////////////////////////// void GraphicsWindowInputDevice:: -operator = (const GraphicsWindowInputDevice ©) +operator = (const GraphicsWindowInputDevice ©) { LightMutexHolder holder(_lock); LightMutexHolder holder1(copy._lock); @@ -165,7 +165,7 @@ get_button_event() { // Function: GraphicsWindowInputDevice::has_pointer_event // Access: Public // Description: Returns true if this device has a pending pointer -// event (a mouse movement), or false otherwise. If +// event (a mouse movement), or false otherwise. If // this returns true, the particular event may be // extracted via get_pointer_event(). //////////////////////////////////////////////////////////////////// @@ -234,7 +234,7 @@ disable_pointer_mode() { //////////////////////////////////////////////////////////////////// // Function: GraphicsWindowInputDevice::set_pointer -// Access: Public +// Access: Published // Description: Records that a mouse movement has taken place. //////////////////////////////////////////////////////////////////// void GraphicsWindowInputDevice:: @@ -264,7 +264,7 @@ set_pointer(bool inwin, double x, double y, double time) { } else { _mouse_data = _true_mouse_data; } - + if (_enable_pointer_events) { int seq = _event_sequence++; if (_pointer_events == 0) { @@ -279,7 +279,7 @@ set_pointer(bool inwin, double x, double y, double time) { //////////////////////////////////////////////////////////////////// // Function: GraphicsWindowInputDevice::button_down -// Access: Public +// Access: Published // Description: Records that the indicated button has been depressed. //////////////////////////////////////////////////////////////////// void GraphicsWindowInputDevice:: @@ -291,7 +291,7 @@ button_down(ButtonHandle button, double time) { //////////////////////////////////////////////////////////////////// // Function: GraphicsWindowInputDevice::button_resume_down -// Access: Public +// Access: Published // Description: Records that the indicated button was depressed // earlier, and we only just detected the event after // the fact. This is mainly useful for tracking the @@ -300,14 +300,13 @@ button_down(ButtonHandle button, double time) { void GraphicsWindowInputDevice:: button_resume_down(ButtonHandle button, double time) { LightMutexHolder holder(_lock); - _button_events.push_back(ButtonEvent(button, ButtonEvent::T_resume_down, time) -); + _button_events.push_back(ButtonEvent(button, ButtonEvent::T_resume_down, time)); _buttons_held.insert(button); } //////////////////////////////////////////////////////////////////// // Function: GraphicsWindowInputDevice::button_up -// Access: Public +// Access: Published // Description: Records that the indicated button has been released. //////////////////////////////////////////////////////////////////// void GraphicsWindowInputDevice:: @@ -319,7 +318,7 @@ button_up(ButtonHandle button, double time) { //////////////////////////////////////////////////////////////////// // Function: GraphicsWindowInputDevice::keystroke -// Access: Public +// Access: Published // Description: Records that the indicated keystroke has been // generated. //////////////////////////////////////////////////////////////////// @@ -331,24 +330,24 @@ keystroke(int keycode, double time) { //////////////////////////////////////////////////////////////////// // Function: GraphicsWindowInputDevice::candidate -// Access: Public +// Access: Published // Description: Records that the indicated candidate string has been // highlighted. This is used to implement IME support // for typing in international languages, especially // Chinese/Japanese/Korean. //////////////////////////////////////////////////////////////////// void GraphicsWindowInputDevice:: -candidate(const wstring &candidate_string, size_t highlight_start, +candidate(const wstring &candidate_string, size_t highlight_start, size_t highlight_end, size_t cursor_pos) { LightMutexHolder holder(_lock); - _button_events.push_back(ButtonEvent(candidate_string, + _button_events.push_back(ButtonEvent(candidate_string, highlight_start, highlight_end, cursor_pos)); } //////////////////////////////////////////////////////////////////// // Function: GraphicsWindowInputDevice::focus_lost -// Access: Public +// Access: Published // Description: This should be called when the window focus is lost, // so that we may miss upcoming button events // (especially "up" events) for the next period of time. @@ -369,7 +368,7 @@ focus_lost(double time) { //////////////////////////////////////////////////////////////////// // Function: GraphicsWindowInputDevice::raw_button_down -// Access: Public +// Access: Published // Description: Records that the indicated button has been depressed. //////////////////////////////////////////////////////////////////// void GraphicsWindowInputDevice:: @@ -380,7 +379,7 @@ raw_button_down(ButtonHandle button, double time) { //////////////////////////////////////////////////////////////////// // Function: GraphicsWindowInputDevice::raw_button_up -// Access: Public +// Access: Published // Description: Records that the indicated button has been released. //////////////////////////////////////////////////////////////////// void GraphicsWindowInputDevice:: diff --git a/panda/src/display/graphicsWindowInputDevice.h b/panda/src/display/graphicsWindowInputDevice.h index af66662e64..e76d48c5bc 100644 --- a/panda/src/display/graphicsWindowInputDevice.h +++ b/panda/src/display/graphicsWindowInputDevice.h @@ -76,18 +76,28 @@ public: PUBLISHED: // The following interface is for the various kinds of // GraphicsWindows to record the data incoming on the device. - void button_down(ButtonHandle button, double time = ClockObject::get_global_clock()->get_frame_time()); - void button_resume_down(ButtonHandle button, double time = ClockObject::get_global_clock()->get_frame_time()); - void button_up(ButtonHandle button, double time = ClockObject::get_global_clock()->get_frame_time()); - void keystroke(int keycode, double time = ClockObject::get_global_clock()->get_frame_time()); - void candidate(const wstring &candidate_string, size_t highlight_start, - size_t highlight_end, size_t cursor_pos); - void focus_lost(double time = ClockObject::get_global_clock()->get_frame_time()); - void raw_button_down(ButtonHandle button, double time = ClockObject::get_global_clock()->get_frame_time()); - void raw_button_up(ButtonHandle button, double time = ClockObject::get_global_clock()->get_frame_time()); + INLINE void button_down(ButtonHandle button); + INLINE void button_resume_down(ButtonHandle button); + INLINE void button_up(ButtonHandle button); + INLINE void keystroke(int keycode); + INLINE void focus_lost(); + INLINE void raw_button_down(ButtonHandle button); + INLINE void raw_button_up(ButtonHandle button); + INLINE void set_pointer_in_window(double x, double y); + INLINE void set_pointer_out_of_window(); - INLINE void set_pointer_in_window(double x, double y, double time = ClockObject::get_global_clock()->get_frame_time()); - INLINE void set_pointer_out_of_window(double time = ClockObject::get_global_clock()->get_frame_time()); + void button_down(ButtonHandle button, double time); + void button_resume_down(ButtonHandle button, double time); + void button_up(ButtonHandle button, double time); + void keystroke(int keycode, double time); + void candidate(const wstring &candidate_string, size_t highlight_start, + size_t highlight_end, size_t cursor_pos); + void focus_lost(double time); + void raw_button_down(ButtonHandle button, double time); + void raw_button_up(ButtonHandle button, double time); + + INLINE void set_pointer_in_window(double x, double y, double time); + INLINE void set_pointer_out_of_window(double time); void set_pointer(bool inwin, double x, double y, double time); public: diff --git a/panda/src/distort/nonlinearImager.cxx b/panda/src/distort/nonlinearImager.cxx index fb44addff8..7a17a61b8f 100644 --- a/panda/src/distort/nonlinearImager.cxx +++ b/panda/src/distort/nonlinearImager.cxx @@ -183,7 +183,7 @@ get_num_screens() const { //////////////////////////////////////////////////////////////////// NodePath NonlinearImager:: get_screen(int index) const { - nassertr(index >= 0 && index < (int)_screens.size(), (ProjectionScreen *)NULL); + nassertr(index >= 0 && index < (int)_screens.size(), NodePath()); return _screens[index]._screen; } @@ -215,7 +215,7 @@ get_buffer(int index) const { void NonlinearImager:: set_texture_size(int index, int width, int height) { nassertv(index >= 0 && index < (int)_screens.size()); - + Screen &screen = _screens[index]; screen._tex_width = width; diff --git a/panda/src/egg/eggNurbsSurface.h b/panda/src/egg/eggNurbsSurface.h index cc55d70730..e658943435 100644 --- a/panda/src/egg/eggNurbsSurface.h +++ b/panda/src/egg/eggNurbsSurface.h @@ -77,6 +77,7 @@ PUBLISHED: virtual void write(ostream &out, int indent_level) const; +public: Curves _curves_on_surface; Trims _trims; diff --git a/panda/src/event/pythonTask.I b/panda/src/event/pythonTask.I index d17a60ad58..3d45c2762e 100644 --- a/panda/src/event/pythonTask.I +++ b/panda/src/event/pythonTask.I @@ -12,3 +12,42 @@ // //////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: PythonTask::set_delay +// Access: Public +// Description: If None is passed, calls clear_delay, otherwise +// sets the delay time. See AsyncTask::set_delay() +// and AsyncTask::clear_delay(). +//////////////////////////////////////////////////////////////////// +INLINE void PythonTask:: +set_delay(PyObject *delay) { + if (delay == Py_None) { + AsyncTask::clear_delay(); + return; + } + + PyObject *value = PyNumber_Float(delay); + if (value == NULL) { + return; + } + + AsyncTask::set_delay(PyFloat_AS_DOUBLE(value)); + Py_DECREF(value); +} + +//////////////////////////////////////////////////////////////////// +// Function: PythonTask::get_delay +// Access: Public +// Description: Returns the delay time if set, None otherwise. +// See AsyncTask::has_delay() and AsyncTask::get_delay(). +//////////////////////////////////////////////////////////////////// +INLINE PyObject *PythonTask:: +get_delay() const { + if (AsyncTask::has_delay()) { + return PyFloat_FromDouble(AsyncTask::get_delay()); + } else { + Py_INCREF(Py_None); + return Py_None; + } +} diff --git a/panda/src/event/pythonTask.cxx b/panda/src/event/pythonTask.cxx index 53be111dfa..a5ea42c1a7 100644 --- a/panda/src/event/pythonTask.cxx +++ b/panda/src/event/pythonTask.cxx @@ -51,17 +51,11 @@ PythonTask(PyObject *function, const string &name) : set_upon_death(Py_None); set_owner(Py_None); - _dict = PyDict_New(); + __dict__ = PyDict_New(); #ifndef SIMPLE_THREADS - // Ensure that the Python threading system is initialized and ready - // to go. + // Ensure that the Python threading system is initialized and ready to go. #ifdef WITH_THREAD // This symbol defined within Python.h - -#if PY_VERSION_HEX >= 0x03020000 - Py_Initialize(); -#endif - PyEval_InitThreads(); #endif #endif @@ -76,7 +70,7 @@ PythonTask:: ~PythonTask() { Py_DECREF(_function); Py_DECREF(_args); - Py_DECREF(_dict); + Py_DECREF(__dict__); Py_XDECREF(_generator); Py_XDECREF(_owner); Py_XDECREF(_upon_death); @@ -123,7 +117,7 @@ void PythonTask:: set_args(PyObject *args, bool append_task) { Py_XDECREF(_args); _args = NULL; - + if (args == Py_None) { // None means no arguments; create an empty tuple. _args = PyTuple_New(0); @@ -254,53 +248,36 @@ get_owner() { // arbitrary data to the Task object. //////////////////////////////////////////////////////////////////// int PythonTask:: -__setattr__(const string &attr_name, PyObject *v) { +__setattr__(PyObject *self, PyObject *attr, PyObject *v) { + if (PyObject_GenericSetAttr(self, attr, v) == 0) { + return 0; + } + + if (!PyErr_ExceptionMatches(PyExc_AttributeError)) { + return -1; + } + + PyErr_Clear(); + if (task_cat.is_debug()) { PyObject *str = PyObject_Repr(v); - task_cat.debug() - << *this << ": task." << attr_name << " = " + task_cat.debug() + << *this << ": task." #if PY_MAJOR_VERSION >= 3 + << PyUnicode_AsUTF8(attr) << " = " << PyUnicode_AsUTF8(str) << "\n"; #else - << PyString_AsString(str) << "\n"; + << PyString_AsString(attr) << " = " + << PyString_AsString(str) << "\n"; #endif Py_DECREF(str); } - if (attr_name == "delayTime") { - if (v == Py_None) { - clear_delay(); - } else { - double delay = PyFloat_AsDouble(v); - if (!PyErr_Occurred()) { - set_delay(delay); - } - } - - } else if (attr_name == "name") { -#if PY_MAJOR_VERSION >= 3 - char *name = PyUnicode_AsUTF8(v); -#else - char *name = PyString_AsString(v); -#endif - if (name != (char *)NULL) { - set_name(name); - } - - } else if (attr_name == "id" || attr_name == "time" || - attr_name == "frame" || attr_name == "wakeTime") { - nassert_raise("Cannot set constant value"); - return true; - - } else { - return PyDict_SetItemString(_dict, attr_name.c_str(), v); - } - - return 0; + return PyDict_SetItem(__dict__, attr, v); } //////////////////////////////////////////////////////////////////// -// Function: PythonTask::__setattr__ +// Function: PythonTask::__delattr__ // Access: Published // Description: Maps from an expression like "del task.attr_name". // This is customized here so we can support some @@ -309,8 +286,32 @@ __setattr__(const string &attr_name, PyObject *v) { // arbitrary data to the Task object. //////////////////////////////////////////////////////////////////// int PythonTask:: -__setattr__(const string &attr_name) { - return PyDict_DelItemString(_dict, attr_name.c_str()); +__delattr__(PyObject *self, PyObject *attr) { + if (PyObject_GenericSetAttr(self, attr, NULL) == 0) { + return 0; + } + + if (!PyErr_ExceptionMatches(PyExc_AttributeError)) { + return -1; + } + + PyErr_Clear(); + + if (PyDict_DelItem(__dict__, attr) == -1) { + // PyDict_DelItem does not raise an exception. +#if PY_MAJOR_VERSION < 3 + PyErr_Format(PyExc_AttributeError, + "'PythonTask' object has no attribute '%.400s'", + PyString_AS_STRING(attr)); +#else + PyErr_Format(PyExc_AttributeError, + "'PythonTask' object has no attribute '%U'", + attr); +#endif + return -1; + } + + return 0; } //////////////////////////////////////////////////////////////////// @@ -323,43 +324,64 @@ __setattr__(const string &attr_name) { // arbitrary data to the Task object. //////////////////////////////////////////////////////////////////// PyObject *PythonTask:: -__getattr__(const string &attr_name) const { - if (attr_name == "time") { - return PyFloat_FromDouble(get_elapsed_time()); +__getattr__(PyObject *attr) const { + // Note that with the new Interrogate behavior, this method + // behaves more like the Python __getattr__ rather than being + // directly assigned to the tp_getattro slot (a la __getattribute__). + // So, we won't get here when the attribute has already been found + // via other methods. - } else if (attr_name == "name") { -#if PY_MAJOR_VERSION >= 3 - return PyUnicode_FromString(get_name().c_str()); + PyObject *item = PyDict_GetItem(__dict__, attr); + + if (item == NULL) { + // PyDict_GetItem does not raise an exception. +#if PY_MAJOR_VERSION < 3 + PyErr_Format(PyExc_AttributeError, + "'PythonTask' object has no attribute '%.400s'", + PyString_AS_STRING(attr)); #else - return PyString_FromString(get_name().c_str()); + PyErr_Format(PyExc_AttributeError, + "'PythonTask' object has no attribute '%U'", + attr); #endif - - } else if (attr_name == "wakeTime") { - return PyFloat_FromDouble(get_wake_time()); - - } else if (attr_name == "delayTime") { - if (!has_delay()) { - Py_RETURN_NONE; - } - return PyFloat_FromDouble(get_delay()); - - } else if (attr_name == "frame") { -#if PY_MAJOR_VERSION >= 3 - return PyLong_FromLong(get_elapsed_frames()); -#else - return PyInt_FromLong(get_elapsed_frames()); -#endif - - } else if (attr_name == "id") { -#if PY_MAJOR_VERSION >= 3 - return PyLong_FromLong(_task_id); -#else - return PyInt_FromLong(_task_id); -#endif - - } else { - return PyMapping_GetItemString(_dict, (char *)attr_name.c_str()); + return NULL; } + + // PyDict_GetItem returns a borrowed reference. + Py_INCREF(item); + return item; +} + +//////////////////////////////////////////////////////////////////// +// Function: PythonTask::__traverse__ +// Access: Published +// Description: Called by Python to implement cycle detection. +//////////////////////////////////////////////////////////////////// +int PythonTask:: +__traverse__(visitproc visit, void *arg) { + Py_VISIT(_function); + Py_VISIT(_args); + Py_VISIT(_upon_death); + Py_VISIT(_owner); + Py_VISIT(__dict__); + Py_VISIT(_generator); + return 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: PythonTask::__clear__ +// Access: Published +// Description: Called by Python to implement cycle breaking. +//////////////////////////////////////////////////////////////////// +int PythonTask:: +__clear__() { + Py_CLEAR(_function); + Py_CLEAR(_args); + Py_CLEAR(_upon_death); + Py_CLEAR(_owner); + Py_CLEAR(__dict__); + Py_CLEAR(_generator); + return 0; } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/event/pythonTask.h b/panda/src/event/pythonTask.h index 294af18387..999b5e6454 100644 --- a/panda/src/event/pythonTask.h +++ b/panda/src/event/pythonTask.h @@ -20,6 +20,8 @@ #include "asyncTask.h" #ifdef HAVE_PYTHON +#include "py_panda.h" + //////////////////////////////////////////////////////////////////// // Class : PythonTask // Description : This class exists to allow association of a Python @@ -43,9 +45,47 @@ PUBLISHED: void set_owner(PyObject *owner); PyObject *get_owner(); - int __setattr__(const string &attr_name, PyObject *v); - int __setattr__(const string &attr_name); - PyObject *__getattr__(const string &attr_name) const; + int __setattr__(PyObject *self, PyObject *attr, PyObject *v); + int __delattr__(PyObject *self, PyObject *attr); + PyObject *__getattr__(PyObject *attr) const; + + int __traverse__(visitproc visit, void *arg); + int __clear__(); + + INLINE void set_delay(PyObject *delay); + INLINE PyObject *get_delay() const; + +PUBLISHED: + // The name of this task. + MAKE_PROPERTY(name, get_name, set_name); + + // The amount of seconds that have elapsed since the task was + // started, according to the task manager's clock. + MAKE_PROPERTY(time, get_elapsed_time); + + // If this task has been added to an AsyncTaskManager with a delay + // in effect, this contains the time at which the task is expected + // to awaken. It has no meaning of the task has not yet been added + // to a queue, or if there was no delay in effect at the time the + // task was added. + // + // If the task's status is not S_sleeping, this contains 0.0. + MAKE_PROPERTY(wake_time, get_wake_time); + + // The delay value that has been set on this task, if any, or None. + MAKE_PROPERTY(delay_time, get_delay, set_delay); + + // The number of frames that have elapsed since the task was + // started, according to the task manager's clock. + MAKE_PROPERTY(frame, get_elapsed_frames); + + // This is a number guaranteed to be unique for each different + // AsyncTask object in the universe. + MAKE_PROPERTY(id, get_task_id); + + // This is a special variable to hold the instance dictionary in + // which custom variables may be stored. + PyObject *__dict__; protected: virtual bool is_runnable(); @@ -67,7 +107,6 @@ private: PyObject *_upon_death; PyObject *_owner; bool _registered_to_owner; - PyObject *_dict; PyObject *_generator; diff --git a/panda/src/express/filename_ext.cxx b/panda/src/express/filename_ext.cxx new file mode 100644 index 0000000000..6c4017a9c8 --- /dev/null +++ b/panda/src/express/filename_ext.cxx @@ -0,0 +1,69 @@ +// Filename: filename_ext.cxx +// Created by: rdb (17Sep14) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#include "filename_ext.h" + +#ifdef HAVE_PYTHON +//////////////////////////////////////////////////////////////////// +// Function: Extension::__reduce__ +// Access: Published +// Description: This special Python method is implement to provide +// support for the pickle module. +//////////////////////////////////////////////////////////////////// +PyObject *Extension:: +__reduce__(PyObject *self) const { + // We should return at least a 2-tuple, (Class, (args)): the + // necessary class object whose constructor we should call + // (e.g. this), and the arguments necessary to reconstruct this + // object. + PyTypeObject *this_class = Py_TYPE(self); + if (this_class == NULL) { + return NULL; + } + + PyObject *result = Py_BuildValue("(O(s))", this_class, _this->c_str()); + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: Extension::scan_directory +// Access: Published +// Description: This variant on scan_directory returns a Python list +// of strings on success, or None on failure. +//////////////////////////////////////////////////////////////////// +PyObject *Extension:: +scan_directory() const { + vector_string contents; + if (!_this->scan_directory(contents)) { + Py_INCREF(Py_None); + return Py_None; + } + + PyObject *result = PyList_New(contents.size()); + for (size_t i = 0; i < contents.size(); ++i) { + const string &filename = contents[i]; +#if PY_MAJOR_VERSION >= 3 + // This function expects UTF-8. + PyObject *str = PyUnicode_FromStringAndSize(filename.data(), filename.size()); +#else + PyObject *str = PyString_FromStringAndSize(filename.data(), filename.size()); +#endif + PyList_SET_ITEM(result, i, str); + } + + return result; +} +#endif // HAVE_PYTHON + + diff --git a/panda/src/express/filename_ext.h b/panda/src/express/filename_ext.h new file mode 100644 index 0000000000..7c7ef0dc17 --- /dev/null +++ b/panda/src/express/filename_ext.h @@ -0,0 +1,41 @@ +// Filename: filename_ext.h +// Created by: rdb (17Sep14) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#ifndef FILENAME_EXT_H +#define FILENAME_EXT_H + +#include "dtoolbase.h" + +#ifdef HAVE_PYTHON + +#include "extension.h" +#include "filename.h" +#include "py_panda.h" + +//////////////////////////////////////////////////////////////////// +// Class : Extension +// Description : This class defines the extension methods for +// Filename, which are called instead of +// any C++ methods with the same prototype. +//////////////////////////////////////////////////////////////////// +template<> +class Extension : public ExtensionBase { +public: + PyObject *__reduce__(PyObject *self) const; + PyObject *scan_directory() const; +}; + +#endif // HAVE_PYTHON + +#endif // FILENAME_EXT_H diff --git a/panda/src/express/globPattern_ext.cxx b/panda/src/express/globPattern_ext.cxx new file mode 100644 index 0000000000..41c4773bd1 --- /dev/null +++ b/panda/src/express/globPattern_ext.cxx @@ -0,0 +1,45 @@ +// Filename: globPattern_ext.cxx +// Created by: rdb (17Sep14) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#include "globPattern_ext.h" + +#ifdef HAVE_PYTHON + +//////////////////////////////////////////////////////////////////// +// Function: Extension::match_files +// Access: Published +// Description: This variant on match_files returns a Python list +// of strings. +//////////////////////////////////////////////////////////////////// +PyObject *Extension:: +match_files(const Filename &cwd) const { + vector_string contents; + _this->match_files(contents, cwd); + + PyObject *result = PyList_New(contents.size()); + for (size_t i = 0; i < contents.size(); ++i) { + const string &filename = contents[i]; +#if PY_MAJOR_VERSION >= 3 + // This function expects UTF-8. + PyObject *str = PyUnicode_FromStringAndSize(filename.data(), filename.size()); +#else + PyObject *str = PyString_FromStringAndSize(filename.data(), filename.size()); +#endif + PyList_SET_ITEM(result, i, str); + } + + return result; +} + +#endif // HAVE_PYTHON diff --git a/panda/src/express/globPattern_ext.h b/panda/src/express/globPattern_ext.h new file mode 100644 index 0000000000..fe9d7ca6ed --- /dev/null +++ b/panda/src/express/globPattern_ext.h @@ -0,0 +1,40 @@ +// Filename: globPattern_ext.h +// Created by: rdb (17Sep14) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#ifndef GLOBPATTERN_EXT_H +#define GLOBPATTERN_EXT_H + +#include "dtoolbase.h" + +#ifdef HAVE_PYTHON + +#include "extension.h" +#include "globPattern.h" +#include "py_panda.h" + +//////////////////////////////////////////////////////////////////// +// Class : Extension +// Description : This class defines the extension methods for +// GlobPattern, which are called instead of +// any C++ methods with the same prototype. +//////////////////////////////////////////////////////////////////// +template<> +class Extension : public ExtensionBase { +public: + PyObject *match_files(const Filename &cwd = Filename()) const; +}; + +#endif // HAVE_PYTHON + +#endif // GLOBPATTERN_EXT_H diff --git a/panda/src/express/multifile.h b/panda/src/express/multifile.h index b52fc6b825..d9842e07c6 100644 --- a/panda/src/express/multifile.h +++ b/panda/src/express/multifile.h @@ -84,27 +84,14 @@ PUBLISHED: int compression_level); #ifdef HAVE_OPENSSL - class EXPCL_PANDAEXPRESS CertRecord { - public: - CertRecord(X509 *cert); - CertRecord(const CertRecord ©); - ~CertRecord(); - void operator = (const CertRecord &other); - X509 *_cert; - }; - typedef pvector CertChain; - - bool add_signature(const Filename &certificate, + bool add_signature(const Filename &certificate, const Filename &chain, const Filename &pkey, const string &password = ""); - bool add_signature(const Filename &composite, + bool add_signature(const Filename &composite, const string &password = ""); - bool add_signature(X509 *certificate, STACK_OF(X509) *chain, EVP_PKEY *pkey); - bool add_signature(const CertChain &chain, EVP_PKEY *pkey); int get_num_signatures() const; - const CertChain &get_signature(int n) const; string get_signature_subject_name(int n) const; string get_signature_friendly_name(int n) const; string get_signature_public_key(int n) const; @@ -152,6 +139,23 @@ PUBLISHED: INLINE const string &get_header_prefix() const; public: +#ifdef HAVE_OPENSSL + class CertRecord { + public: + INLINE CertRecord(X509 *cert); + INLINE CertRecord(const CertRecord ©); + INLINE ~CertRecord(); + INLINE void operator = (const CertRecord &other); + X509 *_cert; + }; + typedef pvector CertChain; + + bool add_signature(X509 *certificate, STACK_OF(X509) *chain, EVP_PKEY *pkey); + bool add_signature(const CertChain &chain, EVP_PKEY *pkey); + + const CertChain &get_signature(int n) const; +#endif // HAVE_OPENSSL + bool read_subfile(int index, string &result); bool read_subfile(int index, pvector &result); diff --git a/panda/src/express/p3express_ext_composite.cxx b/panda/src/express/p3express_ext_composite.cxx index 88dd271158..7956bce4de 100644 --- a/panda/src/express/p3express_ext_composite.cxx +++ b/panda/src/express/p3express_ext_composite.cxx @@ -1,4 +1,7 @@ +#include "filename_ext.cxx" +#include "globPattern_ext.cxx" #include "memoryUsagePointers_ext.cxx" #include "ramfile_ext.cxx" #include "streamReader_ext.cxx" +#include "typeHandle_ext.cxx" #include "virtualFileSystem_ext.cxx" diff --git a/panda/src/express/pointerTo.I b/panda/src/express/pointerTo.I index caaccbcd42..bce23ad027 100644 --- a/panda/src/express/pointerTo.I +++ b/panda/src/express/pointerTo.I @@ -109,6 +109,22 @@ operator T * () const { return (To *)(this->_void_ptr); } +//////////////////////////////////////////////////////////////////// +// Function: PointerTo::cheat +// Access: Public +// Description: Returns a reference to the underlying pointer. This +// is a very unsafe method. It's only used by some +// interrogate code. If you think this method might be +// useful to you, you're probably wrong. +// +// Promise me you won't use this, okay? +//////////////////////////////////////////////////////////////////// +template +INLINE T *&PointerTo:: +cheat() { + return (To *&)(this->_void_ptr); +} + //////////////////////////////////////////////////////////////////// // Function: PointerTo::p // Access: Published @@ -274,13 +290,28 @@ operator -> () const { // don't care which way it goes because either will be // correct. //////////////////////////////////////////////////////////////////// - template INLINE ConstPointerTo:: operator const T * () const { return (To *)(this->_void_ptr); } +//////////////////////////////////////////////////////////////////// +// Function: ConstPointerTo::cheat +// Access: Public +// Description: Returns a reference to the underlying pointer. This +// is a very unsafe method. It's only used by some +// interrogate code. If you think this method might be +// useful to you, you're probably wrong. +// +// Promise me you won't use this, okay? +//////////////////////////////////////////////////////////////////// +template +INLINE const T *&ConstPointerTo:: +cheat() { + return (const To *&)(this->_void_ptr); +} + //////////////////////////////////////////////////////////////////// // Function: ConstPointerTo::p // Access: Published diff --git a/panda/src/express/pointerTo.h b/panda/src/express/pointerTo.h index 1597b94d41..e1775b6d8e 100644 --- a/panda/src/express/pointerTo.h +++ b/panda/src/express/pointerTo.h @@ -95,6 +95,8 @@ public: // MSVC.NET 2005 insists that we use T *, and not To *, here. INLINE operator T *() const; + INLINE T *&cheat(); + PUBLISHED: // When downcasting to a derived class from a PointerTo, // C++ would normally require you to cast twice: once to an actual @@ -160,6 +162,8 @@ public: INLINE const To *operator -> () const; INLINE operator const T *() const; + INLINE const T *&cheat(); + PUBLISHED: INLINE const To *p() const; diff --git a/panda/src/express/referenceCount.I b/panda/src/express/referenceCount.I index ff6d29ef1b..c71886f46d 100644 --- a/panda/src/express/referenceCount.I +++ b/panda/src/express/referenceCount.I @@ -89,7 +89,7 @@ operator = (const ReferenceCount &) { //////////////////////////////////////////////////////////////////// // Function: ReferenceCount::Destructor // Access: Public, Virtual -// Description: +// Description: //////////////////////////////////////////////////////////////////// ReferenceCount:: ~ReferenceCount() { @@ -182,7 +182,7 @@ ref() const { nassertv(test_ref_count_integrity()); #endif - AtomicAdjust::inc(((ReferenceCount *)this)->_ref_count); + AtomicAdjust::inc(_ref_count); } //////////////////////////////////////////////////////////////////// @@ -221,10 +221,9 @@ unref() const { // directly? Are you sure you can't use PointerTo's? nassertr(_ref_count > 0, 0); #endif - return AtomicAdjust::dec(((ReferenceCount *)this)->_ref_count); + return AtomicAdjust::dec(_ref_count); } - //////////////////////////////////////////////////////////////////// // Function: ReferenceCount::test_ref_count_integrity // Access: Published @@ -360,7 +359,7 @@ unref_delete(RefCountType *ptr) { if (!ptr->unref()) { // If the reference count has gone to zero, delete the object. delete ptr; - } + } } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/express/referenceCount.h b/panda/src/express/referenceCount.h index 16beb2b0b4..bb3526e2fa 100644 --- a/panda/src/express/referenceCount.h +++ b/panda/src/express/referenceCount.h @@ -71,7 +71,7 @@ private: void create_weak_list(); private: - enum { + enum { // We use this value as a flag to indicate an object has been // indicated as a local object, and should not be deleted except // by its own destructor. Really, any nonzero value would do, but @@ -87,7 +87,7 @@ private: deleted_ref_count = -100, }; - AtomicAdjust::Integer _ref_count; + mutable AtomicAdjust::Integer _ref_count; AtomicAdjust::Pointer _weak_list; // WeakReferenceList * public: diff --git a/panda/src/express/typeHandle_ext.cxx b/panda/src/express/typeHandle_ext.cxx new file mode 100644 index 0000000000..14ca0fa402 --- /dev/null +++ b/panda/src/express/typeHandle_ext.cxx @@ -0,0 +1,38 @@ +// Filename: typeHandle_ext.cxx +// Created by: rdb (17Sep14) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#include "typeHandle_ext.h" + +#ifdef HAVE_PYTHON + +//////////////////////////////////////////////////////////////////// +// Function: TypeHandle::make +// Access: Published, Static +// Description: Constructs a TypeHandle from a Python class object. +// Useful for automatic coercion, to allow a class +// object to be passed wherever a TypeHandle is +// expected. +//////////////////////////////////////////////////////////////////// +TypeHandle Extension:: +make(PyTypeObject *tp) { + if (!PyType_IsSubtype(tp, &Dtool_DTOOL_SUPER_BASE._PyType)) { + PyErr_SetString(PyExc_TypeError, "a Panda type is required"); + return TypeHandle::none(); + } + + Dtool_PyTypedObject *dtool_tp = (Dtool_PyTypedObject *) tp; + return dtool_tp->_type; +} + +#endif diff --git a/panda/src/express/typeHandle_ext.h b/panda/src/express/typeHandle_ext.h new file mode 100644 index 0000000000..edfae0efa0 --- /dev/null +++ b/panda/src/express/typeHandle_ext.h @@ -0,0 +1,40 @@ +// Filename: typeHandle_ext.h +// Created by: rdb (17Sep14) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#ifndef TYPEHANDLE_EXT_H +#define TYPEHANDLE_EXT_H + +#include "dtoolbase.h" + +#ifdef HAVE_PYTHON + +#include "extension.h" +#include "typeHandle.h" +#include "py_panda.h" + +//////////////////////////////////////////////////////////////////// +// Class : Extension +// Description : This class defines the extension methods for +// TypeHandle, which are called instead of +// any C++ methods with the same prototype. +//////////////////////////////////////////////////////////////////// +template<> +class Extension : public ExtensionBase { +public: + static TypeHandle make(PyTypeObject *tp); +}; + +#endif // HAVE_PYTHON + +#endif // TYPEHANDLE_EXT_H diff --git a/panda/src/express/zStreamBuf.cxx b/panda/src/express/zStreamBuf.cxx index 8a27ab5a0d..1ea01fe889 100644 --- a/panda/src/express/zStreamBuf.cxx +++ b/panda/src/express/zStreamBuf.cxx @@ -19,13 +19,13 @@ #include "pnotify.h" #include "config_express.h" -#if !defined(USE_MEMORY_NOWRAPPERS) +#if !defined(USE_MEMORY_NOWRAPPERS) && !defined(CPPPARSER) // Define functions that hook zlib into panda's memory allocation system. static void * do_zlib_alloc(voidpf opaque, uInt items, uInt size) { return PANDA_MALLOC_ARRAY(items * size); } -static void +static void do_zlib_free(voidpf opaque, voidpf address) { PANDA_FREE_ARRAY(address); } diff --git a/panda/src/framework/windowFramework.cxx b/panda/src/framework/windowFramework.cxx index becd06ee18..85d78be52b 100644 --- a/panda/src/framework/windowFramework.cxx +++ b/panda/src/framework/windowFramework.cxx @@ -79,8 +79,6 @@ WindowFramework:: WindowFramework(PandaFramework *panda_framework) : _panda_framework(panda_framework) { - _alight = (AmbientLight *)NULL; - _dlight = (DirectionalLight *)NULL; _got_keyboard = false; _got_trackball = false; _got_lights = false; @@ -107,8 +105,6 @@ WindowFramework(const WindowFramework ©, DisplayRegion *display_region) : _window(copy._window), _display_region_3d(display_region) { - _alight = (AmbientLight *)NULL; - _dlight = (DirectionalLight *)NULL; _got_keyboard = false; _got_trackball = false; _got_lights = false; @@ -206,8 +202,8 @@ close_window() { _render_2d.remove_node(); _mouse.remove_node(); - _alight = (AmbientLight *)NULL; - _dlight = (DirectionalLight *)NULL; + _alight.clear(); + _dlight.clear(); _got_keyboard = false; _got_trackball = false; _got_lights = false; diff --git a/panda/src/gobj/internalName.h b/panda/src/gobj/internalName.h index 079f7a29da..a8fde6729f 100644 --- a/panda/src/gobj/internalName.h +++ b/panda/src/gobj/internalName.h @@ -191,8 +191,8 @@ INLINE ostream &operator << (ostream &out, const InternalName &tcn); // by the compiler when passed to such a function. //////////////////////////////////////////////////////////////////// #ifdef CPPPARSER -// This construct confuses interrogate, so we give it a typedef. -typedef const InternalName *CPT_InternalName; +// The construct below confuses interrogate, so we give it a typedef. +typedef ConstPointerTo CPT_InternalName; #else class CPT_InternalName : public ConstPointerTo { public: diff --git a/panda/src/gobj/material.h b/panda/src/gobj/material.h index 7fe26b0e82..a34eac6a34 100644 --- a/panda/src/gobj/material.h +++ b/panda/src/gobj/material.h @@ -33,13 +33,13 @@ class FactoryParams; //////////////////////////////////////////////////////////////////// class EXPCL_PANDA_GOBJ Material : public TypedWritableReferenceCount, public Namable { PUBLISHED: - INLINE Material(const string &name = ""); + INLINE explicit Material(const string &name = ""); INLINE Material(const Material ©); void operator = (const Material ©); INLINE ~Material(); INLINE static Material *get_default(); - + INLINE bool has_ambient() const; INLINE const LColor &get_ambient() const; void set_ambient(const LColor &color); @@ -88,7 +88,7 @@ private: PN_stdfloat _shininess; static PT(Material) _default; - + enum Flags { F_ambient = 0x001, F_diffuse = 0x002, diff --git a/panda/src/gobj/texture.h b/panda/src/gobj/texture.h index ea06f34e91..1320935860 100644 --- a/panda/src/gobj/texture.h +++ b/panda/src/gobj/texture.h @@ -210,10 +210,12 @@ PUBLISHED: }; PUBLISHED: - Texture(const string &name = string()); + explicit Texture(const string &name = string()); + protected: Texture(const Texture ©); void operator = (const Texture ©); + PUBLISHED: virtual ~Texture(); diff --git a/panda/src/linmath/aa_luse.h b/panda/src/linmath/aa_luse.h index 68f77d9636..4c809fba0e 100644 --- a/panda/src/linmath/aa_luse.h +++ b/panda/src/linmath/aa_luse.h @@ -45,6 +45,7 @@ #define LCAST(numeric_type, object) lcast_to((numeric_type *)0, object) +BEGIN_PUBLISH // Now we define some handy typedefs for these classes. typedef LPoint3f LVertexf; @@ -185,4 +186,6 @@ typedef LQuaterniond Quat; #endif // STDFLOAT_DOUBLE +END_PUBLISH + #endif diff --git a/panda/src/linmath/compose_matrix_src.h b/panda/src/linmath/compose_matrix_src.h index 5c77c9aa74..807c13956e 100644 --- a/panda/src/linmath/compose_matrix_src.h +++ b/panda/src/linmath/compose_matrix_src.h @@ -30,7 +30,7 @@ compose_matrix(FLOATNAME(LMatrix4) &mat, CoordinateSystem cs = CS_default); INLINE_LINMATH void -compose_matrix(FLOATNAME(LMatrix4) &mat, +compose_matrix(FLOATNAME(LMatrix4) &mat, const FLOATTYPE components[num_matrix_components], CoordinateSystem cs = CS_default); @@ -50,7 +50,8 @@ decompose_matrix(const FLOATNAME(LMatrix4) &mat, CoordinateSystem cs = CS_default); INLINE_LINMATH bool -decompose_matrix(const FLOATNAME(LMatrix4) &mat, FLOATTYPE components[num_matrix_components], +decompose_matrix(const FLOATNAME(LMatrix4) &mat, + FLOATTYPE components[num_matrix_components], CoordinateSystem CS = CS_default); @@ -106,7 +107,8 @@ compose_matrix_old_hpr(FLOATNAME(LMatrix4) &mat, CoordinateSystem cs = CS_default); INLINE_LINMATH void -compose_matrix_old_hpr(FLOATNAME(LMatrix4) &mat, const FLOATTYPE components[num_matrix_components], +compose_matrix_old_hpr(FLOATNAME(LMatrix4) &mat, + const FLOATTYPE components[num_matrix_components], CoordinateSystem cs = CS_default); EXPCL_PANDA_LINMATH bool @@ -125,7 +127,8 @@ decompose_matrix_old_hpr(const FLOATNAME(LMatrix4) &mat, CoordinateSystem cs = CS_default); INLINE_LINMATH bool -decompose_matrix_old_hpr(const FLOATNAME(LMatrix4) &mat, FLOATTYPE components[num_matrix_components], +decompose_matrix_old_hpr(const FLOATNAME(LMatrix4) &mat, + FLOATTYPE components[num_matrix_components], CoordinateSystem CS = CS_default); @@ -145,7 +148,8 @@ compose_matrix_new_hpr(FLOATNAME(LMatrix4) &mat, CoordinateSystem cs = CS_default); INLINE_LINMATH void -compose_matrix_new_hpr(FLOATNAME(LMatrix4) &mat, const FLOATTYPE components[num_matrix_components], +compose_matrix_new_hpr(FLOATNAME(LMatrix4) &mat, + const FLOATTYPE components[num_matrix_components], CoordinateSystem cs = CS_default); EXPCL_PANDA_LINMATH bool @@ -164,7 +168,8 @@ decompose_matrix_new_hpr(const FLOATNAME(LMatrix4) &mat, CoordinateSystem cs = CS_default); INLINE_LINMATH bool -decompose_matrix_new_hpr(const FLOATNAME(LMatrix4) &mat, FLOATTYPE components[num_matrix_components], +decompose_matrix_new_hpr(const FLOATNAME(LMatrix4) &mat, + FLOATTYPE components[num_matrix_components], CoordinateSystem CS = CS_default); diff --git a/panda/src/linmath/lmatrix3_ext_src.I b/panda/src/linmath/lmatrix3_ext_src.I index ec359f6c40..cf917c34fa 100644 --- a/panda/src/linmath/lmatrix3_ext_src.I +++ b/panda/src/linmath/lmatrix3_ext_src.I @@ -13,17 +13,6 @@ //////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////// -// Function: LMatrix3::Row::__setitem__ -// Access: Public -// Description: -//////////////////////////////////////////////////////////////////// -INLINE_LINMATH void Extension:: -__setitem__(int i, FLOATTYPE v) { - nassertv(i >= 0 && i < 3); - _this->_row[i] = v; -} - //////////////////////////////////////////////////////////////////// // Function: LMatrix3::__reduce__ // Access: Published diff --git a/panda/src/linmath/lmatrix3_ext_src.h b/panda/src/linmath/lmatrix3_ext_src.h index ca05d20322..dee0eb434d 100644 --- a/panda/src/linmath/lmatrix3_ext_src.h +++ b/panda/src/linmath/lmatrix3_ext_src.h @@ -13,18 +13,6 @@ //////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////// -// Class : Extension -// Description : This class defines the extension methods for -// LMatrix3::Row, which are called instead of -// any C++ methods with the same prototype. -//////////////////////////////////////////////////////////////////// -template<> -class Extension : public ExtensionBase { -public: - INLINE_LINMATH void __setitem__(int i, FLOATTYPE v); -}; - //////////////////////////////////////////////////////////////////// // Class : Extension // Description : This class defines the extension methods for diff --git a/panda/src/linmath/lmatrix3_src.I b/panda/src/linmath/lmatrix3_src.I index 402dbbaba4..1fd8c74fa9 100644 --- a/panda/src/linmath/lmatrix3_src.I +++ b/panda/src/linmath/lmatrix3_src.I @@ -459,7 +459,7 @@ begin() { //////////////////////////////////////////////////////////////////// INLINE_LINMATH FLOATNAME(LMatrix3)::iterator FLOATNAME(LMatrix3):: end() { - return begin() + get_num_components(); + return begin() + num_components; } //////////////////////////////////////////////////////////////////// @@ -481,7 +481,7 @@ begin() const { //////////////////////////////////////////////////////////////////// INLINE_LINMATH FLOATNAME(LMatrix3)::const_iterator FLOATNAME(LMatrix3):: end() const { - return begin() + get_num_components(); + return begin() + num_components; } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/linmath/lmatrix3_src.h b/panda/src/linmath/lmatrix3_src.h index c132a58e1e..4d48ab782a 100644 --- a/panda/src/linmath/lmatrix3_src.h +++ b/panda/src/linmath/lmatrix3_src.h @@ -24,10 +24,16 @@ class FLOATNAME(LMatrix4); //////////////////////////////////////////////////////////////////// class EXPCL_PANDA_LINMATH FLOATNAME(LMatrix3) { public: + typedef FLOATTYPE numeric_type; typedef const FLOATTYPE *iterator; typedef const FLOATTYPE *const_iterator; PUBLISHED: + enum { + num_components = 9, + is_int = 0 + }; + // These helper classes are used to support two-level operator []. class Row { private: @@ -35,7 +41,6 @@ PUBLISHED: PUBLISHED: INLINE_LINMATH FLOATTYPE operator [](int i) const; INLINE_LINMATH FLOATTYPE &operator [](int i); - EXTENSION(INLINE_LINMATH void __setitem__(int i, FLOATTYPE v)); INLINE_LINMATH static int size(); public: FLOATTYPE *_row; diff --git a/panda/src/linmath/lmatrix4_ext_src.I b/panda/src/linmath/lmatrix4_ext_src.I index 91a8122426..a9f5992dd5 100644 --- a/panda/src/linmath/lmatrix4_ext_src.I +++ b/panda/src/linmath/lmatrix4_ext_src.I @@ -13,17 +13,6 @@ //////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////// -// Function: LMatrix4::Row::__setitem__ -// Access: Public -// Description: -//////////////////////////////////////////////////////////////////// -INLINE_LINMATH void Extension:: -__setitem__(int i, FLOATTYPE v) { - nassertv(i >= 0 && i < 4); - _this->_row[i] = v; -} - //////////////////////////////////////////////////////////////////// // Function: LMatrix4::__reduce__ // Access: Published diff --git a/panda/src/linmath/lmatrix4_ext_src.h b/panda/src/linmath/lmatrix4_ext_src.h index fc03cc29dc..405ebdd9c5 100644 --- a/panda/src/linmath/lmatrix4_ext_src.h +++ b/panda/src/linmath/lmatrix4_ext_src.h @@ -13,18 +13,6 @@ //////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////// -// Class : Extension -// Description : This class defines the extension methods for -// LMatrix4::Row, which are called instead of -// any C++ methods with the same prototype. -//////////////////////////////////////////////////////////////////// -template<> -class Extension : public ExtensionBase { -public: - INLINE_LINMATH void __setitem__(int i, FLOATTYPE v); -}; - //////////////////////////////////////////////////////////////////// // Class : Extension // Description : This class defines the extension methods for diff --git a/panda/src/linmath/lmatrix4_src.I b/panda/src/linmath/lmatrix4_src.I index 9dc886c22b..be772cab48 100644 --- a/panda/src/linmath/lmatrix4_src.I +++ b/panda/src/linmath/lmatrix4_src.I @@ -166,7 +166,7 @@ INLINE_LINMATH FLOATNAME(LMatrix4) &FLOATNAME(LMatrix4):: operator = (const FLOATNAME(UnalignedLMatrix4) ©) { TAU_PROFILE("void LMatrix4::operator = (const UnalignedLMatrix4 &)", " ", TAU_USER); - memcpy(&_m(0, 0), copy.get_data(), sizeof(FLOATTYPE) * get_num_components()); + memcpy(&_m(0, 0), copy.get_data(), sizeof(FLOATTYPE) * num_components); return *this; } @@ -1848,7 +1848,7 @@ FLOATNAME(UnalignedLMatrix4)(const FLOATNAME(UnalignedLMatrix4) ©) : _m(copy //////////////////////////////////////////////////////////////////// INLINE_LINMATH FLOATNAME(UnalignedLMatrix4) &FLOATNAME(UnalignedLMatrix4):: operator = (const FLOATNAME(LMatrix4) ©) { - memcpy(&_m(0, 0), copy.get_data(), sizeof(FLOATTYPE) * get_num_components()); + memcpy(&_m(0, 0), copy.get_data(), sizeof(FLOATTYPE) * num_components); return *this; } diff --git a/panda/src/linmath/lmatrix4_src.h b/panda/src/linmath/lmatrix4_src.h index 370b82d6c0..7a8e90564b 100644 --- a/panda/src/linmath/lmatrix4_src.h +++ b/panda/src/linmath/lmatrix4_src.h @@ -20,10 +20,16 @@ class FLOATNAME(UnalignedLMatrix4); //////////////////////////////////////////////////////////////////// class EXPCL_PANDA_LINMATH ALIGN_LINMATH FLOATNAME(LMatrix4) { public: + typedef FLOATTYPE numeric_type; typedef const FLOATTYPE *iterator; typedef const FLOATTYPE *const_iterator; PUBLISHED: + enum { + num_components = 16, + is_int = 0 + }; + // These helper classes are used to support two-level operator []. class Row { private: @@ -31,7 +37,6 @@ PUBLISHED: PUBLISHED: INLINE_LINMATH FLOATTYPE operator [](int i) const; INLINE_LINMATH FLOATTYPE &operator [](int i); - EXTENSION(INLINE_LINMATH void __setitem__(int i, FLOATTYPE v)); INLINE_LINMATH static int size(); public: FLOATTYPE *_row; @@ -320,6 +325,10 @@ private: //////////////////////////////////////////////////////////////////// class EXPCL_PANDA_LINMATH FLOATNAME(UnalignedLMatrix4) { PUBLISHED: + enum { + num_components = 16 + }; + INLINE_LINMATH FLOATNAME(UnalignedLMatrix4)(); INLINE_LINMATH FLOATNAME(UnalignedLMatrix4)(const FLOATNAME(LMatrix4) ©); INLINE_LINMATH FLOATNAME(UnalignedLMatrix4)(const FLOATNAME(UnalignedLMatrix4) ©); diff --git a/panda/src/linmath/lvecBase2_ext_src.I b/panda/src/linmath/lvecBase2_ext_src.I index 2d771a9d0a..1498b8a32b 100644 --- a/panda/src/linmath/lvecBase2_ext_src.I +++ b/panda/src/linmath/lvecBase2_ext_src.I @@ -28,17 +28,6 @@ #define PY_AS_FLOATTYPE PyFloat_AsDouble #endif -//////////////////////////////////////////////////////////////////// -// Function: LVecBase2::__setitem__ -// Access: Public -// Description: -//////////////////////////////////////////////////////////////////// -INLINE_LINMATH void Extension:: -__setitem__(int i, FLOATTYPE v) { - nassertv(i >= 0 && i < 2); - _this->_v(i) = v; -} - //////////////////////////////////////////////////////////////////// // Function: LVecBase2::python_repr // Access: Published diff --git a/panda/src/linmath/lvecBase2_ext_src.h b/panda/src/linmath/lvecBase2_ext_src.h index 0e7cba9a4f..15ea7fa0c3 100644 --- a/panda/src/linmath/lvecBase2_ext_src.h +++ b/panda/src/linmath/lvecBase2_ext_src.h @@ -25,7 +25,6 @@ public: INLINE_LINMATH PyObject *__reduce__(PyObject *self) const; INLINE_LINMATH PyObject *__getattr__(const string &attr_name) const; INLINE_LINMATH int __setattr__(PyObject *self, const string &attr_name, PyObject *assign); - INLINE_LINMATH void __setitem__(int i, FLOATTYPE v); INLINE_LINMATH void python_repr(ostream &out, const string &class_name) const; INLINE_LINMATH FLOATNAME(LVecBase2) __pow__(FLOATTYPE exponent) const; diff --git a/panda/src/linmath/lvecBase2_src.I b/panda/src/linmath/lvecBase2_src.I index 9b453102a1..14057fbd50 100644 --- a/panda/src/linmath/lvecBase2_src.I +++ b/panda/src/linmath/lvecBase2_src.I @@ -298,7 +298,7 @@ begin() { //////////////////////////////////////////////////////////////////// INLINE_LINMATH FLOATNAME(LVecBase2)::iterator FLOATNAME(LVecBase2):: end() { - return begin() + get_num_components(); + return begin() + num_components; } //////////////////////////////////////////////////////////////////// @@ -320,7 +320,7 @@ begin() const { //////////////////////////////////////////////////////////////////// INLINE_LINMATH FLOATNAME(LVecBase2)::const_iterator FLOATNAME(LVecBase2):: end() const { - return begin() + get_num_components(); + return begin() + num_components; } //////////////////////////////////////////////////////////////////// @@ -756,8 +756,12 @@ componentwise_mult(const FLOATNAME(LVecBase2) &other) { INLINE_LINMATH FLOATNAME(LVecBase2) FLOATNAME(LVecBase2):: fmax(const FLOATNAME(LVecBase2) &other) const { TAU_PROFILE("LVecBase2::fmax()", " ", TAU_USER); +#ifdef HAVE_EIGEN + return FLOATNAME(LVecBase2)(_v.cwiseMax(other._v)); +#else return FLOATNAME(LVecBase2)(_v(0) > other._v(0) ? _v(0) : other._v(0), _v(1) > other._v(1) ? _v(1) : other._v(1)); +#endif } //////////////////////////////////////////////////////////////////// @@ -768,8 +772,12 @@ fmax(const FLOATNAME(LVecBase2) &other) const { INLINE_LINMATH FLOATNAME(LVecBase2) FLOATNAME(LVecBase2):: fmin(const FLOATNAME(LVecBase2) &other) const { TAU_PROFILE("LVecBase2::fmin()", " ", TAU_USER); +#ifdef HAVE_EIGEN + return FLOATNAME(LVecBase2)(_v.cwiseMin(other._v)); +#else return FLOATNAME(LVecBase2)(_v(0) < other._v(0) ? _v(0) : other._v(0), _v(1) < other._v(1) ? _v(1) : other._v(1)); +#endif } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/linmath/lvecBase2_src.h b/panda/src/linmath/lvecBase2_src.h index 6cba9eea6e..7983c04e7d 100644 --- a/panda/src/linmath/lvecBase2_src.h +++ b/panda/src/linmath/lvecBase2_src.h @@ -20,9 +20,20 @@ //////////////////////////////////////////////////////////////////// class EXPCL_PANDA_LINMATH FLOATNAME(LVecBase2) { PUBLISHED: + typedef FLOATTYPE numeric_type; typedef const FLOATTYPE *iterator; typedef const FLOATTYPE *const_iterator; + enum { + num_components = 2, + +#ifdef FLOATTYPE_IS_INT + is_int = 1 +#else + is_int = 0 +#endif + }; + INLINE_LINMATH FLOATNAME(LVecBase2)(); INLINE_LINMATH FLOATNAME(LVecBase2)(const FLOATNAME(LVecBase2) ©); INLINE_LINMATH FLOATNAME(LVecBase2) &operator = (const FLOATNAME(LVecBase2) ©); @@ -43,7 +54,6 @@ PUBLISHED: INLINE_LINMATH FLOATTYPE operator [](int i) const; INLINE_LINMATH FLOATTYPE &operator [](int i); - EXTENSION(INLINE_LINMATH void __setitem__(int i, FLOATTYPE v)); INLINE_LINMATH static int size(); INLINE_LINMATH bool is_nan() const; @@ -130,7 +140,7 @@ PUBLISHED: INLINE_LINMATH FLOATNAME(LVecBase2) fmin(const FLOATNAME(LVecBase2) &other) const; INLINE_LINMATH bool almost_equal(const FLOATNAME(LVecBase2) &other, - FLOATTYPE threshold) const; + FLOATTYPE threshold) const; INLINE_LINMATH bool almost_equal(const FLOATNAME(LVecBase2) &other) const; INLINE_LINMATH void output(ostream &out) const; diff --git a/panda/src/linmath/lvecBase3_ext_src.I b/panda/src/linmath/lvecBase3_ext_src.I index 438e8ca4ec..2aa1f0420a 100644 --- a/panda/src/linmath/lvecBase3_ext_src.I +++ b/panda/src/linmath/lvecBase3_ext_src.I @@ -28,17 +28,6 @@ #define PY_AS_FLOATTYPE PyFloat_AsDouble #endif -//////////////////////////////////////////////////////////////////// -// Function: LVecBase3::__setitem__ -// Access: Public -// Description: -//////////////////////////////////////////////////////////////////// -INLINE_LINMATH void Extension:: -__setitem__(int i, FLOATTYPE v) { - nassertv(i >= 0 && i < 3); - _this->_v(i) = v; -} - //////////////////////////////////////////////////////////////////// // Function: LVecBase3::python_repr // Access: Published diff --git a/panda/src/linmath/lvecBase3_ext_src.h b/panda/src/linmath/lvecBase3_ext_src.h index cc86087833..69e7ed929d 100644 --- a/panda/src/linmath/lvecBase3_ext_src.h +++ b/panda/src/linmath/lvecBase3_ext_src.h @@ -25,7 +25,6 @@ public: INLINE_LINMATH PyObject *__reduce__(PyObject *self) const; INLINE_LINMATH PyObject *__getattr__(const string &attr_name) const; INLINE_LINMATH int __setattr__(PyObject *self, const string &attr_name, PyObject *assign); - INLINE_LINMATH void __setitem__(int i, FLOATTYPE v); INLINE_LINMATH void python_repr(ostream &out, const string &class_name) const; INLINE_LINMATH FLOATNAME(LVecBase3) __pow__(FLOATTYPE exponent) const; diff --git a/panda/src/linmath/lvecBase3_src.I b/panda/src/linmath/lvecBase3_src.I index 31e24b2801..41a0d9ccce 100644 --- a/panda/src/linmath/lvecBase3_src.I +++ b/panda/src/linmath/lvecBase3_src.I @@ -12,6 +12,7 @@ // //////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////// // Function: LVecBase3::Default Constructor // Access: Public @@ -381,7 +382,7 @@ begin() { //////////////////////////////////////////////////////////////////// INLINE_LINMATH FLOATNAME(LVecBase3)::iterator FLOATNAME(LVecBase3):: end() { - return begin() + get_num_components(); + return begin() + num_components; } //////////////////////////////////////////////////////////////////// @@ -403,7 +404,7 @@ begin() const { //////////////////////////////////////////////////////////////////// INLINE_LINMATH FLOATNAME(LVecBase3)::const_iterator FLOATNAME(LVecBase3):: end() const { - return begin() + get_num_components(); + return begin() + num_components; } //////////////////////////////////////////////////////////////////// @@ -933,9 +934,13 @@ componentwise_mult(const FLOATNAME(LVecBase3) &other) { INLINE_LINMATH FLOATNAME(LVecBase3) FLOATNAME(LVecBase3):: fmax(const FLOATNAME(LVecBase3) &other) const { TAU_PROFILE("LVecBase3::fmax()", " ", TAU_USER); +#ifdef HAVE_EIGEN + return FLOATNAME(LVecBase3)(_v.cwiseMax(other._v)); +#else return FLOATNAME(LVecBase3)(_v(0) > other._v(0) ? _v(0) : other._v(0), _v(1) > other._v(1) ? _v(1) : other._v(1), _v(2) > other._v(2) ? _v(2) : other._v(2)); +#endif } //////////////////////////////////////////////////////////////////// @@ -946,9 +951,13 @@ fmax(const FLOATNAME(LVecBase3) &other) const { INLINE_LINMATH FLOATNAME(LVecBase3) FLOATNAME(LVecBase3):: fmin(const FLOATNAME(LVecBase3) &other) const { TAU_PROFILE("LVecBase3::fmin()", " ", TAU_USER); +#ifdef HAVE_EIGEN + return FLOATNAME(LVecBase3)(_v.cwiseMin(other._v)); +#else return FLOATNAME(LVecBase3)(_v(0) < other._v(0) ? _v(0) : other._v(0), _v(1) < other._v(1) ? _v(1) : other._v(1), _v(2) < other._v(2) ? _v(2) : other._v(2)); +#endif } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/linmath/lvecBase3_src.h b/panda/src/linmath/lvecBase3_src.h index ce7c09c011..e6567428c2 100644 --- a/panda/src/linmath/lvecBase3_src.h +++ b/panda/src/linmath/lvecBase3_src.h @@ -20,9 +20,20 @@ //////////////////////////////////////////////////////////////////// class EXPCL_PANDA_LINMATH FLOATNAME(LVecBase3) { PUBLISHED: + typedef FLOATTYPE numeric_type; typedef const FLOATTYPE *iterator; typedef const FLOATTYPE *const_iterator; + enum { + num_components = 3, + +#ifdef FLOATTYPE_IS_INT + is_int = 1 +#else + is_int = 0 +#endif + }; + INLINE_LINMATH FLOATNAME(LVecBase3)(); INLINE_LINMATH FLOATNAME(LVecBase3)(const FLOATNAME(LVecBase3) ©); INLINE_LINMATH FLOATNAME(LVecBase3) &operator = (const FLOATNAME(LVecBase3) ©); @@ -45,7 +56,6 @@ PUBLISHED: INLINE_LINMATH FLOATTYPE operator [](int i) const; INLINE_LINMATH FLOATTYPE &operator [](int i); - EXTENSION(INLINE_LINMATH void __setitem__(int i, FLOATTYPE v)); INLINE_LINMATH static int size(); INLINE_LINMATH bool is_nan() const; @@ -145,7 +155,7 @@ PUBLISHED: INLINE_LINMATH void cross_into(const FLOATNAME(LVecBase3) &other); INLINE_LINMATH bool almost_equal(const FLOATNAME(LVecBase3) &other, - FLOATTYPE threshold) const; + FLOATTYPE threshold) const; INLINE_LINMATH bool almost_equal(const FLOATNAME(LVecBase3) &other) const; INLINE_LINMATH void output(ostream &out) const; diff --git a/panda/src/linmath/lvecBase4.h b/panda/src/linmath/lvecBase4.h index 0e144630e4..268da58c60 100644 --- a/panda/src/linmath/lvecBase4.h +++ b/panda/src/linmath/lvecBase4.h @@ -24,6 +24,8 @@ #include "checksumHashGenerator.h" #include "lvecBase2.h" #include "lvecBase3.h" +#include "lpoint3.h" +#include "lvector3.h" #include "cmath.h" #include "nearly_zero.h" diff --git a/panda/src/linmath/lvecBase4_ext_src.I b/panda/src/linmath/lvecBase4_ext_src.I index 444ce5f7a2..b1e624dc6f 100644 --- a/panda/src/linmath/lvecBase4_ext_src.I +++ b/panda/src/linmath/lvecBase4_ext_src.I @@ -28,17 +28,6 @@ #define PY_AS_FLOATTYPE PyFloat_AsDouble #endif -//////////////////////////////////////////////////////////////////// -// Function: LVecBase4::__setitem__ -// Access: Public -// Description: -//////////////////////////////////////////////////////////////////// -INLINE_LINMATH void Extension:: -__setitem__(int i, FLOATTYPE v) { - nassertv(i >= 0 && i < 4); - _this->_v(i) = v; -} - //////////////////////////////////////////////////////////////////// // Function: LVecBase4::python_repr // Access: Published @@ -258,17 +247,6 @@ __ipow__(PyObject *self, FLOATTYPE exponent) { return self; } -//////////////////////////////////////////////////////////////////// -// Function: UnalignedLVecBase4::__setitem__ -// Access: Public -// Description: -//////////////////////////////////////////////////////////////////// -INLINE_LINMATH void Extension:: -__setitem__(int i, FLOATTYPE v) { - nassertv(i >= 0 && i < 4); - _this->_v(i) = v; -} - #undef PYNUMBER_FLOATTYPE #undef PY_FROM_FLOATTYPE #undef PY_AS_FLOATTYPE diff --git a/panda/src/linmath/lvecBase4_ext_src.h b/panda/src/linmath/lvecBase4_ext_src.h index edcecb4dbf..e25341c50a 100644 --- a/panda/src/linmath/lvecBase4_ext_src.h +++ b/panda/src/linmath/lvecBase4_ext_src.h @@ -25,23 +25,10 @@ public: INLINE_LINMATH PyObject *__reduce__(PyObject *self) const; INLINE_LINMATH PyObject *__getattr__(const string &attr_name) const; INLINE_LINMATH int __setattr__(PyObject *self, const string &attr_name, PyObject *assign); - INLINE_LINMATH void __setitem__(int i, FLOATTYPE v); INLINE_LINMATH void python_repr(ostream &out, const string &class_name) const; INLINE_LINMATH FLOATNAME(LVecBase4) __pow__(FLOATTYPE exponent) const; INLINE_LINMATH PyObject *__ipow__(PyObject *self, FLOATTYPE exponent); }; -//////////////////////////////////////////////////////////////////// -// Class : Extension -// Description : This class defines the extension methods for -// UnalignedLVecBase4, which are called instead of -// any C++ methods with the same prototype. -//////////////////////////////////////////////////////////////////// -template<> -class Extension : public ExtensionBase { -public: - INLINE_LINMATH void __setitem__(int i, FLOATTYPE v); -}; - #include "lvecBase4_ext_src.I" diff --git a/panda/src/linmath/lvecBase4_src.I b/panda/src/linmath/lvecBase4_src.I index db5923295e..f8c8422c58 100644 --- a/panda/src/linmath/lvecBase4_src.I +++ b/panda/src/linmath/lvecBase4_src.I @@ -106,6 +106,28 @@ FLOATNAME(LVecBase4)(const FLOATNAME(LVecBase3) ©, FLOATTYPE w) { set(copy[0], copy[1], copy[2], w); } +//////////////////////////////////////////////////////////////////// +// Function: LVecBase4::Constructor +// Access: Published +// Description: Constructs an LVecBase4 from an LPoint3. The w +// coordinate is set to 1.0. +//////////////////////////////////////////////////////////////////// +INLINE_LINMATH FLOATNAME(LVecBase4):: +FLOATNAME(LVecBase4)(const FLOATNAME(LPoint3) &point) { + set(point[0], point[1], point[2], 1); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase4::Constructor +// Access: Published +// Description: Constructs an LVecBase4 from an LVector3. The w +// coordinate is set to 0.0. +//////////////////////////////////////////////////////////////////// +INLINE_LINMATH FLOATNAME(LVecBase4):: +FLOATNAME(LVecBase4)(const FLOATNAME(LVector3) &vector) { + set(vector[0], vector[1], vector[2], 0); +} + //////////////////////////////////////////////////////////////////// // Function: LVecBase4::Destructor // Access: Published @@ -407,7 +429,7 @@ begin() { //////////////////////////////////////////////////////////////////// INLINE_LINMATH FLOATNAME(LVecBase4)::iterator FLOATNAME(LVecBase4):: end() { - return begin() + get_num_components(); + return begin() + num_components; } //////////////////////////////////////////////////////////////////// @@ -429,7 +451,7 @@ begin() const { //////////////////////////////////////////////////////////////////// INLINE_LINMATH FLOATNAME(LVecBase4)::const_iterator FLOATNAME(LVecBase4):: end() const { - return begin() + get_num_components(); + return begin() + num_components; } //////////////////////////////////////////////////////////////////// @@ -917,10 +939,14 @@ componentwise_mult(const FLOATNAME(LVecBase4) &other) { INLINE_LINMATH FLOATNAME(LVecBase4) FLOATNAME(LVecBase4):: fmax(const FLOATNAME(LVecBase4) &other) const { TAU_PROFILE("LVecBase4::fmax()", " ", TAU_USER); +#ifdef HAVE_EIGEN + return FLOATNAME(LVecBase4)(_v.cwiseMax(other._v)); +#else return FLOATNAME(LVecBase4)(_v(0) > other._v(0) ? _v(0) : other._v(0), _v(1) > other._v(1) ? _v(1) : other._v(1), _v(2) > other._v(2) ? _v(2) : other._v(2), _v(3) > other._v(3) ? _v(3) : other._v(3)); +#endif } //////////////////////////////////////////////////////////////////// @@ -931,10 +957,14 @@ fmax(const FLOATNAME(LVecBase4) &other) const { INLINE_LINMATH FLOATNAME(LVecBase4) FLOATNAME(LVecBase4):: fmin(const FLOATNAME(LVecBase4) &other) const { TAU_PROFILE("LVecBase4::fmin()", " ", TAU_USER); +#ifdef HAVE_EIGEN + return FLOATNAME(LVecBase4)(_v.cwiseMin(other._v)); +#else return FLOATNAME(LVecBase4)(_v(0) < other._v(0) ? _v(0) : other._v(0), _v(1) < other._v(1) ? _v(1) : other._v(1), _v(2) < other._v(2) ? _v(2) : other._v(2), _v(3) < other._v(3) ? _v(3) : other._v(3)); +#endif } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/linmath/lvecBase4_src.h b/panda/src/linmath/lvecBase4_src.h index e8b48ede7e..ff226b747f 100644 --- a/panda/src/linmath/lvecBase4_src.h +++ b/panda/src/linmath/lvecBase4_src.h @@ -14,6 +14,8 @@ class FLOATNAME(LVecBase2); class FLOATNAME(LVecBase3); +class FLOATNAME(LPoint3); +class FLOATNAME(LVector3); class FLOATNAME(UnalignedLVecBase4); //////////////////////////////////////////////////////////////////// @@ -23,9 +25,20 @@ class FLOATNAME(UnalignedLVecBase4); //////////////////////////////////////////////////////////////////// class EXPCL_PANDA_LINMATH ALIGN_LINMATH FLOATNAME(LVecBase4) { PUBLISHED: + typedef FLOATTYPE numeric_type; typedef const FLOATTYPE *iterator; typedef const FLOATTYPE *const_iterator; + enum { + num_components = 4, + +#ifdef FLOATTYPE_IS_INT + is_int = 1 +#else + is_int = 0 +#endif + }; + INLINE_LINMATH FLOATNAME(LVecBase4)(); INLINE_LINMATH FLOATNAME(LVecBase4)(const FLOATNAME(LVecBase4) ©); INLINE_LINMATH FLOATNAME(LVecBase4)(const FLOATNAME(UnalignedLVecBase4) ©); @@ -35,6 +48,8 @@ PUBLISHED: INLINE_LINMATH FLOATNAME(LVecBase4)(FLOATTYPE fill_value); INLINE_LINMATH FLOATNAME(LVecBase4)(FLOATTYPE x, FLOATTYPE y, FLOATTYPE z, FLOATTYPE w); INLINE_LINMATH FLOATNAME(LVecBase4)(const FLOATNAME(LVecBase3) ©, FLOATTYPE w); + INLINE_LINMATH FLOATNAME(LVecBase4)(const FLOATNAME(LPoint3) &point); + INLINE_LINMATH FLOATNAME(LVecBase4)(const FLOATNAME(LVector3) &vector); ALLOC_DELETED_CHAIN(FLOATNAME(LVecBase4)); INLINE_LINMATH static const FLOATNAME(LVecBase4) &zero(); @@ -51,7 +66,6 @@ PUBLISHED: INLINE_LINMATH FLOATTYPE operator [](int i) const; INLINE_LINMATH FLOATTYPE &operator [](int i); - EXTENSION(INLINE_LINMATH void __setitem__(int i, FLOATTYPE v)); INLINE_LINMATH static int size(); INLINE_LINMATH bool is_nan() const; @@ -80,6 +94,7 @@ PUBLISHED: INLINE_LINMATH const FLOATTYPE *get_data() const; INLINE_LINMATH int get_num_components() const; + INLINE_LINMATH void extract_data(float*){}; public: INLINE_LINMATH iterator begin(); @@ -144,7 +159,7 @@ PUBLISHED: INLINE_LINMATH FLOATNAME(LVecBase4) fmin(const FLOATNAME(LVecBase4) &other) const; INLINE_LINMATH bool almost_equal(const FLOATNAME(LVecBase4) &other, - FLOATTYPE threshold) const; + FLOATTYPE threshold) const; INLINE_LINMATH bool almost_equal(const FLOATNAME(LVecBase4) &other) const; INLINE_LINMATH void output(ostream &out) const; @@ -198,6 +213,16 @@ private: //////////////////////////////////////////////////////////////////// class EXPCL_PANDA_LINMATH FLOATNAME(UnalignedLVecBase4) { PUBLISHED: + enum { + num_components = 4, + +#ifdef FLOATTYPE_IS_INT + is_int = 1 +#else + is_int = 0 +#endif + }; + INLINE_LINMATH FLOATNAME(UnalignedLVecBase4)(); INLINE_LINMATH FLOATNAME(UnalignedLVecBase4)(const FLOATNAME(LVecBase4) ©); INLINE_LINMATH FLOATNAME(UnalignedLVecBase4)(const FLOATNAME(UnalignedLVecBase4) ©); @@ -209,14 +234,13 @@ PUBLISHED: INLINE_LINMATH FLOATTYPE operator [](int i) const; INLINE_LINMATH FLOATTYPE &operator [](int i); - - EXTENSION(INLINE_LINMATH void __setitem__(int i, FLOATTYPE v)); INLINE_LINMATH static int size(); INLINE_LINMATH const FLOATTYPE *get_data() const; INLINE_LINMATH int get_num_components() const; public: + typedef FLOATTYPE numeric_type; typedef UNALIGNED_LINMATH_MATRIX(FLOATTYPE, 1, 4) UVector4; UVector4 _v; diff --git a/panda/src/mathutil/config_mathutil.N b/panda/src/mathutil/config_mathutil.N index 1c3ec9e9a6..94c5de0123 100644 --- a/panda/src/mathutil/config_mathutil.N +++ b/panda/src/mathutil/config_mathutil.N @@ -4,67 +4,67 @@ noinclude plane_src.h forcetype PointerToBase > forcetype PointerToArrayBase -forcetype PTA_LMatrix4f -forcetype CPTA_LMatrix4f +forcetype PointerToArray +forcetype ConstPointerToArray forcetype PointerToBase > forcetype PointerToArrayBase -forcetype PTA_LMatrix3f -forcetype CPTA_LMatrix3f +forcetype PointerToArray +forcetype ConstPointerToArray forcetype PointerToBase > forcetype PointerToArrayBase -forcetype PTA_LVecBase4f -forcetype CPTA_LVecBase4f +forcetype PointerToArray +forcetype ConstPointerToArray forcetype PointerToBase > forcetype PointerToArrayBase -forcetype PTA_LVecBase3f -forcetype CPTA_LVecBase3f +forcetype PointerToArray +forcetype ConstPointerToArray forcetype PointerToBase > forcetype PointerToArrayBase -forcetype PTA_LVecBase2f -forcetype CPTA_LVecBase2f +forcetype PointerToArray +forcetype ConstPointerToArray forcetype PointerToBase > forcetype PointerToArrayBase -forcetype PTA_LMatrix4d -forcetype CPTA_LMatrix4d +forcetype PointerToArray +forcetype ConstPointerToArray forcetype PointerToBase > forcetype PointerToArrayBase -forcetype PTA_LMatrix3d -forcetype CPTA_LMatrix3d +forcetype PointerToArray +forcetype ConstPointerToArray forcetype PointerToBase > forcetype PointerToArrayBase -forcetype PTA_LVecBase4d -forcetype CPTA_LVecBase4d +forcetype PointerToArray +forcetype ConstPointerToArray forcetype PointerToBase > forcetype PointerToArrayBase -forcetype PTA_LVecBase3d -forcetype CPTA_LVecBase3d +forcetype PointerToArray +forcetype ConstPointerToArray forcetype PointerToBase > forcetype PointerToArrayBase -forcetype PTA_LVecBase2d -forcetype CPTA_LVecBase2d +forcetype PointerToArray +forcetype ConstPointerToArray forcetype PointerToBase > forcetype PointerToArrayBase -forcetype PTA_LVecBase4i -forcetype CPTA_LVecBase4i +forcetype PointerToArray +forcetype ConstPointerToArray forcetype PointerToBase > forcetype PointerToArrayBase -forcetype PTA_LVecBase3i -forcetype CPTA_LVecBase3i +forcetype PointerToArray +forcetype ConstPointerToArray forcetype PointerToBase > forcetype PointerToArrayBase -forcetype PTA_LVecBase2i -forcetype CPTA_LVecBase2i +forcetype PointerToArray +forcetype ConstPointerToArray forceinclude "pointerToArray_ext.h" diff --git a/panda/src/pgraph/nodePath.I b/panda/src/pgraph/nodePath.I index ea347495cf..ce0228174a 100644 --- a/panda/src/pgraph/nodePath.I +++ b/panda/src/pgraph/nodePath.I @@ -136,6 +136,19 @@ operator = (NodePath &&from) NOEXCEPT { } #endif // USE_MOVE_SEMANTICS +//////////////////////////////////////////////////////////////////// +// Function: NodePath::clear +// Access: Published +// Description: Sets this NodePath to the empty NodePath. It will +// no longer point to any node. +//////////////////////////////////////////////////////////////////// +INLINE void NodePath:: +clear() { + _head.clear(); + _backup_key = 0; + _error_type = ET_ok; +} + //////////////////////////////////////////////////////////////////// // Function: NodePath::not_found named constructor // Access: Published, Static diff --git a/panda/src/pgraph/nodePath.h b/panda/src/pgraph/nodePath.h index 8163e78767..74ce52c3a8 100644 --- a/panda/src/pgraph/nodePath.h +++ b/panda/src/pgraph/nodePath.h @@ -174,14 +174,15 @@ PUBLISHED: }; INLINE NodePath(); - INLINE NodePath(const string &top_node_name, Thread *current_thread = Thread::get_current_thread()); - INLINE NodePath(PandaNode *node, Thread *current_thread = Thread::get_current_thread()); + INLINE explicit NodePath(const string &top_node_name, Thread *current_thread = Thread::get_current_thread()); + INLINE explicit NodePath(PandaNode *node, Thread *current_thread = Thread::get_current_thread()); INLINE static NodePath any_path(PandaNode *node, Thread *current_thread = Thread::get_current_thread()); NodePath(const NodePath &parent, PandaNode *child_node, Thread *current_thread = Thread::get_current_thread()); INLINE NodePath(const NodePath ©); INLINE void operator = (const NodePath ©); + INLINE void clear(); #ifdef USE_MOVE_SEMANTICS INLINE NodePath(NodePath &&from) NOEXCEPT; diff --git a/panda/src/putil/Sources.pp b/panda/src/putil/Sources.pp index 8c72159364..9092bb4292 100644 --- a/panda/src/putil/Sources.pp +++ b/panda/src/putil/Sources.pp @@ -50,8 +50,7 @@ indirectCompareSort.I indirectCompareSort.h \ indirectCompareTo.I indirectCompareTo.h \ ioPtaDatagramFloat.h ioPtaDatagramInt.h \ - ioPtaDatagramShort.h keyboardButton.h lineStream.I \ - lineStream.h lineStreamBuf.I lineStreamBuf.h \ + ioPtaDatagramShort.h keyboardButton.h \ linkedListNode.I linkedListNode.h \ load_prc_file.h \ loaderOptions.I loaderOptions.h \ @@ -103,7 +102,7 @@ globalPointerRegistry.cxx \ ioPtaDatagramFloat.cxx \ ioPtaDatagramInt.cxx ioPtaDatagramShort.cxx \ - keyboardButton.cxx lineStream.cxx lineStreamBuf.cxx \ + keyboardButton.cxx \ linkedListNode.cxx \ load_prc_file.cxx \ loaderOptions.cxx \ @@ -162,8 +161,7 @@ indirectCompareSort.I indirectCompareSort.h \ indirectCompareTo.I indirectCompareTo.h \ ioPtaDatagramFloat.h ioPtaDatagramInt.h \ - ioPtaDatagramShort.h iterator_types.h keyboardButton.h lineStream.I \ - lineStream.h lineStreamBuf.I lineStreamBuf.h \ + ioPtaDatagramShort.h iterator_types.h keyboardButton.h \ linkedListNode.I linkedListNode.h \ load_prc_file.h \ loaderOptions.I loaderOptions.h \ diff --git a/panda/src/putil/bitArray.I b/panda/src/putil/bitArray.I index 2389b39406..151277e6cf 100755 --- a/panda/src/putil/bitArray.I +++ b/panda/src/putil/bitArray.I @@ -142,7 +142,7 @@ INLINE BitArray:: // generic programming algorithms can use BitMask or // BitArray interchangeably. //////////////////////////////////////////////////////////////////// -INLINE bool BitArray:: +CONSTEXPR bool BitArray:: has_max_num_bits() { return false; } @@ -160,10 +160,9 @@ has_max_num_bits() { // is defined so generic programming algorithms can use // BitMask or BitArray interchangeably. //////////////////////////////////////////////////////////////////// -INLINE int BitArray:: +CONSTEXPR int BitArray:: get_max_num_bits() { - nassertr(false, 0); - return 0; + return INT_MAX; } //////////////////////////////////////////////////////////////////// @@ -174,7 +173,7 @@ get_max_num_bits() { // limits the maximum number of bits that may be queried // or set at once by extract() and store(). //////////////////////////////////////////////////////////////////// -INLINE int BitArray:: +CONSTEXPR int BitArray:: get_num_bits_per_word() { return num_bits_per_word; } @@ -393,7 +392,7 @@ get_word(int n) const { // array. //////////////////////////////////////////////////////////////////// INLINE void BitArray:: -set_word(int n, MaskType value) { +set_word(int n, WordType value) { nassertv(n >= 0); ensure_has_word(n); _array[n] = value; diff --git a/panda/src/putil/bitArray.h b/panda/src/putil/bitArray.h index 3a1a85f187..9019f2a6f5 100755 --- a/panda/src/putil/bitArray.h +++ b/panda/src/putil/bitArray.h @@ -43,9 +43,10 @@ class EXPCL_PANDA_PUTIL BitArray { public: typedef BitMaskNative MaskType; typedef MaskType::WordType WordType; - enum { num_bits_per_word = MaskType::num_bits }; PUBLISHED: + enum { num_bits_per_word = MaskType::num_bits }; + INLINE BitArray(); INLINE BitArray(WordType init_value); INLINE BitArray(const BitArray ©); @@ -60,10 +61,10 @@ PUBLISHED: INLINE ~BitArray(); - INLINE static bool has_max_num_bits(); - INLINE static int get_max_num_bits(); + CONSTEXPR static bool has_max_num_bits(); + CONSTEXPR static int get_max_num_bits(); - INLINE static int get_num_bits_per_word(); + CONSTEXPR static int get_num_bits_per_word(); INLINE int get_num_bits() const; INLINE bool get_bit(int index) const; INLINE void set_bit(int index); @@ -91,7 +92,7 @@ PUBLISHED: INLINE int get_num_words() const; INLINE MaskType get_word(int n) const; - INLINE void set_word(int n, MaskType value); + INLINE void set_word(int n, WordType value); void invert_in_place(); bool has_bits_in_common(const BitArray &other) const; diff --git a/panda/src/putil/bitMask.I b/panda/src/putil/bitMask.I index 89a2ca37e6..19a603e7e8 100644 --- a/panda/src/putil/bitMask.I +++ b/panda/src/putil/bitMask.I @@ -164,7 +164,7 @@ INLINE BitMask:: // BitMask or BitArray interchangeably. //////////////////////////////////////////////////////////////////// template -INLINE bool BitMask:: +CONSTEXPR bool BitMask:: has_max_num_bits() { return true; } @@ -183,7 +183,7 @@ has_max_num_bits() { // can use BitMask or BitArray interchangeably. //////////////////////////////////////////////////////////////////// template -INLINE int BitMask:: +CONSTEXPR int BitMask:: get_max_num_bits() { return num_bits; } @@ -195,7 +195,7 @@ get_max_num_bits() { // bitmask. //////////////////////////////////////////////////////////////////// template -INLINE int BitMask:: +CONSTEXPR int BitMask:: get_num_bits() { return num_bits; } diff --git a/panda/src/putil/bitMask.h b/panda/src/putil/bitMask.h index 95c8ea0a6e..64fcdedff0 100644 --- a/panda/src/putil/bitMask.h +++ b/panda/src/putil/bitMask.h @@ -35,9 +35,10 @@ template class BitMask { public: typedef WType WordType; - enum { num_bits = nbits }; PUBLISHED: + enum { num_bits = nbits }; + INLINE BitMask(); INLINE BitMask(WordType init_value); INLINE BitMask(const BitMask ©); @@ -51,10 +52,10 @@ PUBLISHED: INLINE ~BitMask(); - INLINE static bool has_max_num_bits(); - INLINE static int get_max_num_bits(); + CONSTEXPR static bool has_max_num_bits(); + CONSTEXPR static int get_max_num_bits(); - INLINE static int get_num_bits(); + CONSTEXPR static int get_num_bits(); INLINE bool get_bit(int index) const; INLINE void set_bit(int index); INLINE void clear_bit(int index); diff --git a/panda/src/putil/config_util.N b/panda/src/putil/config_util.N index eb48a5c511..4bcc299b1e 100644 --- a/panda/src/putil/config_util.N +++ b/panda/src/putil/config_util.N @@ -1,19 +1,22 @@ +defconstruct ButtonHandle new ButtonHandle(0) + ignoremember _factory ignoremember get_factory ignoretype FactoryBase ignoretype Factory -forcetype string -renametype string CString +# We want to keep our bindings consistent for both 32-bit and 64-bit +# builds (to support multi-arch builds), so we don't export this. +ignoretype BitMaskNative -forcetype DoubleBitMaskNative -forcetype QuadBitMaskNative +forcetype basic_string +renametype basic_string CString forcetype PointerToBase > forcetype PointerToArrayBase -forcetype PTA_ushort -forcetype CPTA_ushort +forcetype PointerToArray +forcetype ConstPointerToArray # This is so the extensions for PTA_ushort are made available. forceinclude "pointerToArray_ext.h" diff --git a/panda/src/putil/doubleBitMask.I b/panda/src/putil/doubleBitMask.I index 05febb27e2..790fb1c744 100644 --- a/panda/src/putil/doubleBitMask.I +++ b/panda/src/putil/doubleBitMask.I @@ -152,7 +152,7 @@ INLINE DoubleBitMask:: // DoubleBitMask or BitArray interchangeably. //////////////////////////////////////////////////////////////////// template -INLINE bool DoubleBitMask:: +CONSTEXPR bool DoubleBitMask:: has_max_num_bits() { return true; } @@ -171,7 +171,7 @@ has_max_num_bits() { // can use DoubleBitMask or BitArray interchangeably. //////////////////////////////////////////////////////////////////// template -INLINE int DoubleBitMask:: +CONSTEXPR int DoubleBitMask:: get_max_num_bits() { return num_bits; } @@ -183,7 +183,7 @@ get_max_num_bits() { // doubleBitMask. //////////////////////////////////////////////////////////////////// template -INLINE int DoubleBitMask:: +CONSTEXPR int DoubleBitMask:: get_num_bits() { return num_bits; } diff --git a/panda/src/putil/doubleBitMask.h b/panda/src/putil/doubleBitMask.h index 2e9b768428..4d4dfcf3b8 100644 --- a/panda/src/putil/doubleBitMask.h +++ b/panda/src/putil/doubleBitMask.h @@ -31,16 +31,16 @@ template class DoubleBitMask { public: - typedef BMType BitMaskType; typedef TYPENAME BMType::WordType WordType; -#ifndef CPPPARSER // interrogate has a problem with these lines. + +PUBLISHED: + typedef BMType BitMaskType; + enum { half_bits = BitMaskType::num_bits, num_bits = BitMaskType::num_bits * 2, }; -#endif // CPPPARSER -PUBLISHED: INLINE DoubleBitMask(); INLINE DoubleBitMask(const DoubleBitMask ©); INLINE DoubleBitMask &operator = (const DoubleBitMask ©); @@ -53,10 +53,10 @@ PUBLISHED: INLINE ~DoubleBitMask(); - INLINE static bool has_max_num_bits(); - INLINE static int get_max_num_bits(); + CONSTEXPR static bool has_max_num_bits(); + CONSTEXPR static int get_max_num_bits(); - INLINE static int get_num_bits(); + CONSTEXPR static int get_num_bits(); INLINE bool get_bit(int index) const; INLINE void set_bit(int index); INLINE void clear_bit(int index); diff --git a/panda/src/putil/keyboardButton.cxx b/panda/src/putil/keyboardButton.cxx index 6b728096da..e683e37d0a 100644 --- a/panda/src/putil/keyboardButton.cxx +++ b/panda/src/putil/keyboardButton.cxx @@ -29,23 +29,6 @@ ascii_key(char ascii_equivalent) { return ButtonRegistry::ptr()->find_ascii_button(ascii_equivalent); } -//////////////////////////////////////////////////////////////////// -// Function: KeyboardButton::ascii_key -// Access: Public, Static -// Description: Returns the ButtonHandle associated with the -// particular ASCII character (taken from the first -// character of the indicated string), if there is one, -// or ButtonHandle::none() if there is not. -//////////////////////////////////////////////////////////////////// -ButtonHandle KeyboardButton:: -ascii_key(const string &ascii_equivalent) { - if (ascii_equivalent.empty()) { - return ButtonHandle::none(); - } else { - return ButtonRegistry::ptr()->find_ascii_button(ascii_equivalent[0]); - } -} - #define DEFINE_KEYBD_BUTTON_HANDLE(KeyName) \ static ButtonHandle _##KeyName; \ ButtonHandle KeyboardButton::KeyName() { return _##KeyName; } diff --git a/panda/src/putil/keyboardButton.h b/panda/src/putil/keyboardButton.h index cd74d7d023..499a087e0e 100644 --- a/panda/src/putil/keyboardButton.h +++ b/panda/src/putil/keyboardButton.h @@ -28,7 +28,6 @@ class EXPCL_PANDA_PUTIL KeyboardButton { PUBLISHED: static ButtonHandle ascii_key(char ascii_equivalent); - static ButtonHandle ascii_key(const string &ascii_equivalent); static ButtonHandle space(); static ButtonHandle backspace(); diff --git a/panda/src/putil/p3putil_composite2.cxx b/panda/src/putil/p3putil_composite2.cxx index 36c9062c16..1f3b46718d 100644 --- a/panda/src/putil/p3putil_composite2.cxx +++ b/panda/src/putil/p3putil_composite2.cxx @@ -2,8 +2,6 @@ #include "ioPtaDatagramInt.cxx" #include "ioPtaDatagramShort.cxx" #include "keyboardButton.cxx" -#include "lineStream.cxx" -#include "lineStreamBuf.cxx" #include "linkedListNode.cxx" #include "load_prc_file.cxx" #include "loaderOptions.cxx" diff --git a/panda/src/rocket/rocketRegion.cxx b/panda/src/rocket/rocketRegion.cxx index 433de05a92..6f6bac761e 100644 --- a/panda/src/rocket/rocketRegion.cxx +++ b/panda/src/rocket/rocketRegion.cxx @@ -55,7 +55,9 @@ RocketRegion(GraphicsOutput *window, const LVecBase4 &dr_dimensions, _lens->set_film_size(dimensions.x, -dimensions.y); _lens->set_film_offset(dimensions.x * 0.5, dimensions.y * 0.5); _lens->set_near_far(-1, 1); - set_camera(new Camera(context_name, _lens)); + + PT(Camera) cam = new Camera(context_name, _lens); + set_camera(NodePath(cam)); } ////////////////////////////////////////////////////////////////////