Browse Source

interrogate: support MAKE_MAP_PROPERTY

rdb 8 years ago
parent
commit
bf190f7306

File diff suppressed because it is too large
+ 1727 - 1686
dtool/src/cppparser/cppBison.cxx.prebuilt


+ 175 - 100
dtool/src/cppparser/cppBison.yxx

@@ -397,6 +397,7 @@ pop_struct() {
 %type <u.type> class_derivation_name
 %type <u.type> class_derivation_name
 %type <u.type> enum_element_type
 %type <u.type> enum_element_type
 %type <u.type> maybe_trailing_return_type
 %type <u.type> maybe_trailing_return_type
+%type <u.identifier> maybe_comma_identifier
 /*%type <u.type> typedefname*/
 /*%type <u.type> typedefname*/
 %type <u.identifier> name
 %type <u.identifier> name
 %type <u.identifier> name_no_final
 %type <u.identifier> name_no_final
@@ -548,202 +549,264 @@ declaration:
 {
 {
   current_scope->set_current_vis(V_private);
   current_scope->set_current_vis(V_private);
 }
 }
-        | KW_MAKE_PROPERTY '(' name ',' IDENTIFIER ')' ';'
-{
-
-  CPPDeclaration *getter = $5->find_symbol(current_scope, global_scope, current_lexer);
-  if (getter == (CPPDeclaration *)NULL || getter->get_subtype() != CPPDeclaration::ST_function_group) {
-    yyerror("Reference to non-existent or invalid getter: " + $5->get_fully_scoped_name(), @5);
-  }
-
-  CPPMakeProperty *make_property = new CPPMakeProperty($3, getter->as_function_group(), NULL, current_scope, @1.file);
-  current_scope->add_declaration(make_property, global_scope, current_lexer, @1);
-}
-        | KW_MAKE_PROPERTY '(' name ',' IDENTIFIER ',' IDENTIFIER ')' ';'
+        | KW_MAKE_PROPERTY '(' name ',' IDENTIFIER maybe_comma_identifier ')' ';'
 {
 {
   CPPDeclaration *getter = $5->find_symbol(current_scope, global_scope, current_lexer);
   CPPDeclaration *getter = $5->find_symbol(current_scope, global_scope, current_lexer);
-  if (getter == (CPPDeclaration *)NULL || getter->get_subtype() != CPPDeclaration::ST_function_group) {
+  if (getter == nullptr || getter->get_subtype() != CPPDeclaration::ST_function_group) {
     yyerror("Reference to non-existent or invalid getter: " + $5->get_fully_scoped_name(), @5);
     yyerror("Reference to non-existent or invalid getter: " + $5->get_fully_scoped_name(), @5);
-
   } else {
   } else {
-    CPPDeclaration *setter = $7->find_symbol(current_scope, global_scope, current_lexer);
-    CPPFunctionGroup *setter_func = NULL;
-
-    if (setter == (CPPDeclaration *)NULL || setter->get_subtype() != CPPDeclaration::ST_function_group) {
-      yyerror("Reference to non-existent or invalid setter: " + $7->get_fully_scoped_name(), @7);
-    } else {
-      setter_func = setter->as_function_group();
+    CPPMakeProperty *make_property = new CPPMakeProperty($3, CPPMakeProperty::T_normal, current_scope, @1.file);
+    make_property->_get_function = getter->as_function_group();
+
+    if ($6 != nullptr) {
+      CPPDeclaration *setter = $6->find_symbol(current_scope, global_scope, current_lexer);
+      if (setter == nullptr || setter->get_subtype() != CPPDeclaration::ST_function_group) {
+        yyerror("Reference to non-existent or invalid setter: " + $6->get_fully_scoped_name(), @6);
+      } else {
+        make_property->_set_function = setter->as_function_group();
+      }
     }
     }
 
 
-    CPPMakeProperty *make_property = new CPPMakeProperty($3, getter->as_function_group(),
-                                                         setter_func, current_scope, @1.file);
     current_scope->add_declaration(make_property, global_scope, current_lexer, @1);
     current_scope->add_declaration(make_property, global_scope, current_lexer, @1);
   }
   }
 }
 }
         | KW_MAKE_PROPERTY '(' name ',' IDENTIFIER ',' IDENTIFIER ',' IDENTIFIER ')' ';'
         | KW_MAKE_PROPERTY '(' name ',' IDENTIFIER ',' IDENTIFIER ',' IDENTIFIER ')' ';'
 {
 {
   CPPDeclaration *getter = $5->find_symbol(current_scope, global_scope, current_lexer);
   CPPDeclaration *getter = $5->find_symbol(current_scope, global_scope, current_lexer);
-  if (getter == (CPPDeclaration *)NULL || getter->get_subtype() != CPPDeclaration::ST_function_group) {
+  if (getter == nullptr || getter->get_subtype() != CPPDeclaration::ST_function_group) {
     yyerror("Reference to non-existent or invalid getter: " + $5->get_fully_scoped_name(), @5);
     yyerror("Reference to non-existent or invalid getter: " + $5->get_fully_scoped_name(), @5);
 
 
   } else {
   } else {
-    CPPDeclaration *setter = $7->find_symbol(current_scope, global_scope, current_lexer);
-    CPPFunctionGroup *setter_func = NULL;
+    CPPMakeProperty *make_property = new CPPMakeProperty($3, CPPMakeProperty::T_normal, current_scope, @1.file);
+    make_property->_get_function = getter->as_function_group();
 
 
-    if (setter == (CPPDeclaration *)NULL || setter->get_subtype() != CPPDeclaration::ST_function_group) {
+    CPPDeclaration *setter = $7->find_symbol(current_scope, global_scope, current_lexer);
+    if (setter == nullptr || setter->get_subtype() != CPPDeclaration::ST_function_group) {
       yyerror("Reference to non-existent or invalid setter: " + $7->get_fully_scoped_name(), @7);
       yyerror("Reference to non-existent or invalid setter: " + $7->get_fully_scoped_name(), @7);
     } else {
     } else {
-      setter_func = setter->as_function_group();
+      make_property->_set_function = setter->as_function_group();
     }
     }
 
 
     CPPDeclaration *deleter = $9->find_symbol(current_scope, global_scope, current_lexer);
     CPPDeclaration *deleter = $9->find_symbol(current_scope, global_scope, current_lexer);
-    if (deleter == (CPPDeclaration *)NULL || deleter->get_subtype() != CPPDeclaration::ST_function_group) {
+    if (deleter == nullptr || deleter->get_subtype() != CPPDeclaration::ST_function_group) {
       yyerror("reference to non-existent or invalid delete method: " + $9->get_fully_scoped_name(), @9);
       yyerror("reference to non-existent or invalid delete method: " + $9->get_fully_scoped_name(), @9);
-      deleter = NULL;
-    }
-
-    CPPMakeProperty *make_property = new CPPMakeProperty($3, getter->as_function_group(),
-                                                         setter_func, current_scope, @1.file);
-    if (deleter) {
+    } else {
       make_property->_del_function = deleter->as_function_group();
       make_property->_del_function = deleter->as_function_group();
     }
     }
+
     current_scope->add_declaration(make_property, global_scope, current_lexer, @1);
     current_scope->add_declaration(make_property, global_scope, current_lexer, @1);
   }
   }
 }
 }
         | KW_MAKE_SEQ_PROPERTY '(' name ',' IDENTIFIER ',' IDENTIFIER ')' ';'
         | KW_MAKE_SEQ_PROPERTY '(' name ',' IDENTIFIER ',' IDENTIFIER ')' ';'
 {
 {
   CPPDeclaration *length_getter = $5->find_symbol(current_scope, global_scope, current_lexer);
   CPPDeclaration *length_getter = $5->find_symbol(current_scope, global_scope, current_lexer);
-  if (length_getter == (CPPDeclaration *)NULL || length_getter->get_subtype() != CPPDeclaration::ST_function_group) {
+  if (length_getter == nullptr || length_getter->get_subtype() != CPPDeclaration::ST_function_group) {
     yyerror("reference to non-existent or invalid length method: " + $5->get_fully_scoped_name(), @5);
     yyerror("reference to non-existent or invalid length method: " + $5->get_fully_scoped_name(), @5);
-    length_getter = NULL;
+    length_getter = nullptr;
   }
   }
 
 
   CPPDeclaration *getter = $7->find_symbol(current_scope, global_scope, current_lexer);
   CPPDeclaration *getter = $7->find_symbol(current_scope, global_scope, current_lexer);
-  if (getter == (CPPDeclaration *)NULL || getter->get_subtype() != CPPDeclaration::ST_function_group) {
+  if (getter == nullptr || getter->get_subtype() != CPPDeclaration::ST_function_group) {
     yyerror("Reference to non-existent or invalid getter: " + $7->get_fully_scoped_name(), @7);
     yyerror("Reference to non-existent or invalid getter: " + $7->get_fully_scoped_name(), @7);
+    getter = nullptr;
   }
   }
 
 
-  CPPMakeProperty *make_property = new CPPMakeProperty($3, getter->as_function_group(), NULL, current_scope, @1.file);
-  make_property->_length_function = length_getter->as_function_group();
-  current_scope->add_declaration(make_property, global_scope, current_lexer, @1);
+  if (getter != nullptr && length_getter != nullptr) {
+    CPPMakeProperty *make_property = new CPPMakeProperty($3, CPPMakeProperty::T_sequence, current_scope, @1.file);
+    make_property->_get_function = getter->as_function_group();
+    make_property->_length_function = length_getter->as_function_group();
+    current_scope->add_declaration(make_property, global_scope, current_lexer, @1);
+  }
 }
 }
         | KW_MAKE_SEQ_PROPERTY '(' name ',' IDENTIFIER ',' IDENTIFIER ',' IDENTIFIER ')' ';'
         | KW_MAKE_SEQ_PROPERTY '(' name ',' IDENTIFIER ',' IDENTIFIER ',' IDENTIFIER ')' ';'
 {
 {
   CPPDeclaration *length_getter = $5->find_symbol(current_scope, global_scope, current_lexer);
   CPPDeclaration *length_getter = $5->find_symbol(current_scope, global_scope, current_lexer);
-  if (length_getter == (CPPDeclaration *)NULL || length_getter->get_subtype() != CPPDeclaration::ST_function_group) {
+  if (length_getter == nullptr || length_getter->get_subtype() != CPPDeclaration::ST_function_group) {
     yyerror("reference to non-existent or invalid length method: " + $5->get_fully_scoped_name(), @5);
     yyerror("reference to non-existent or invalid length method: " + $5->get_fully_scoped_name(), @5);
-    length_getter = NULL;
+    length_getter = nullptr;
   }
   }
 
 
   CPPDeclaration *getter = $7->find_symbol(current_scope, global_scope, current_lexer);
   CPPDeclaration *getter = $7->find_symbol(current_scope, global_scope, current_lexer);
-  if (getter == (CPPDeclaration *)NULL || getter->get_subtype() != CPPDeclaration::ST_function_group) {
+  if (getter == nullptr || getter->get_subtype() != CPPDeclaration::ST_function_group) {
     yyerror("Reference to non-existent or invalid getter: " + $7->get_fully_scoped_name(), @7);
     yyerror("Reference to non-existent or invalid getter: " + $7->get_fully_scoped_name(), @7);
+    getter = nullptr;
+  }
 
 
-  } else {
-    CPPDeclaration *setter = $9->find_symbol(current_scope, global_scope, current_lexer);
-    CPPFunctionGroup *setter_func = NULL;
+  if (getter != nullptr && length_getter != nullptr) {
+    CPPMakeProperty *make_property = new CPPMakeProperty($3, CPPMakeProperty::T_sequence, current_scope, @1.file);
+    make_property->_get_function = getter->as_function_group();
+    make_property->_length_function = length_getter->as_function_group();
 
 
-    if (setter == (CPPDeclaration *)NULL || setter->get_subtype() != CPPDeclaration::ST_function_group) {
+    CPPDeclaration *setter = $9->find_symbol(current_scope, global_scope, current_lexer);
+    if (setter == nullptr || setter->get_subtype() != CPPDeclaration::ST_function_group) {
       yyerror("Reference to non-existent or invalid setter: " + $9->get_fully_scoped_name(), @9);
       yyerror("Reference to non-existent or invalid setter: " + $9->get_fully_scoped_name(), @9);
     } else {
     } else {
-      setter_func = setter->as_function_group();
+      make_property->_set_function = setter->as_function_group();
     }
     }
 
 
-    CPPMakeProperty *make_property = new CPPMakeProperty($3, getter->as_function_group(),
-                                                         setter_func, current_scope, @1.file);
-    make_property->_length_function = length_getter->as_function_group();
     current_scope->add_declaration(make_property, global_scope, current_lexer, @1);
     current_scope->add_declaration(make_property, global_scope, current_lexer, @1);
   }
   }
 }
 }
         | KW_MAKE_SEQ_PROPERTY '(' name ',' IDENTIFIER ',' IDENTIFIER ',' IDENTIFIER ',' IDENTIFIER ')' ';'
         | KW_MAKE_SEQ_PROPERTY '(' name ',' IDENTIFIER ',' IDENTIFIER ',' IDENTIFIER ',' IDENTIFIER ')' ';'
 {
 {
   CPPDeclaration *length_getter = $5->find_symbol(current_scope, global_scope, current_lexer);
   CPPDeclaration *length_getter = $5->find_symbol(current_scope, global_scope, current_lexer);
-  if (length_getter == (CPPDeclaration *)NULL || length_getter->get_subtype() != CPPDeclaration::ST_function_group) {
+  if (length_getter == nullptr || length_getter->get_subtype() != CPPDeclaration::ST_function_group) {
     yyerror("reference to non-existent or invalid length method: " + $5->get_fully_scoped_name(), @5);
     yyerror("reference to non-existent or invalid length method: " + $5->get_fully_scoped_name(), @5);
     length_getter = NULL;
     length_getter = NULL;
   }
   }
 
 
   CPPDeclaration *getter = $7->find_symbol(current_scope, global_scope, current_lexer);
   CPPDeclaration *getter = $7->find_symbol(current_scope, global_scope, current_lexer);
-  if (getter == (CPPDeclaration *)NULL || getter->get_subtype() != CPPDeclaration::ST_function_group) {
+  if (getter == nullptr || getter->get_subtype() != CPPDeclaration::ST_function_group) {
     yyerror("Reference to non-existent or invalid getter: " + $7->get_fully_scoped_name(), @7);
     yyerror("Reference to non-existent or invalid getter: " + $7->get_fully_scoped_name(), @7);
+    getter = nullptr;
+  }
 
 
-  } else {
-    CPPDeclaration *setter = $9->find_symbol(current_scope, global_scope, current_lexer);
-    CPPFunctionGroup *setter_func = NULL;
+  if (getter != nullptr && length_getter != nullptr) {
+    CPPMakeProperty *make_property = new CPPMakeProperty($3, CPPMakeProperty::T_sequence, current_scope, @1.file);
+    make_property->_get_function = getter->as_function_group();
+    make_property->_length_function = length_getter->as_function_group();
 
 
-    if (setter == (CPPDeclaration *)NULL || setter->get_subtype() != CPPDeclaration::ST_function_group) {
+    CPPDeclaration *setter = $9->find_symbol(current_scope, global_scope, current_lexer);
+    if (setter == nullptr || setter->get_subtype() != CPPDeclaration::ST_function_group) {
       yyerror("Reference to non-existent or invalid setter: " + $9->get_fully_scoped_name(), @9);
       yyerror("Reference to non-existent or invalid setter: " + $9->get_fully_scoped_name(), @9);
     } else {
     } else {
-      setter_func = setter->as_function_group();
+      make_property->_set_function = setter->as_function_group();
     }
     }
 
 
     CPPDeclaration *deleter = $11->find_symbol(current_scope, global_scope, current_lexer);
     CPPDeclaration *deleter = $11->find_symbol(current_scope, global_scope, current_lexer);
-    if (deleter == (CPPDeclaration *)NULL || deleter->get_subtype() != CPPDeclaration::ST_function_group) {
+    if (deleter == nullptr || deleter->get_subtype() != CPPDeclaration::ST_function_group) {
       yyerror("reference to non-existent or invalid delete method: " + $11->get_fully_scoped_name(), @11);
       yyerror("reference to non-existent or invalid delete method: " + $11->get_fully_scoped_name(), @11);
-      deleter = NULL;
-    }
-
-    CPPMakeProperty *make_property = new CPPMakeProperty($3, getter->as_function_group(),
-                                                         setter_func, current_scope, @1.file);
-    make_property->_length_function = length_getter->as_function_group();
-    if (deleter) {
+    } else {
       make_property->_del_function = deleter->as_function_group();
       make_property->_del_function = deleter->as_function_group();
     }
     }
+
     current_scope->add_declaration(make_property, global_scope, current_lexer, @1);
     current_scope->add_declaration(make_property, global_scope, current_lexer, @1);
   }
   }
 }
 }
-        | KW_MAKE_PROPERTY2 '(' name ',' IDENTIFIER ',' IDENTIFIER ')' ';'
+        | KW_MAKE_MAP_PROPERTY '(' name ',' IDENTIFIER ')' ';'
 {
 {
-  CPPDeclaration *hasser = $5->find_symbol(current_scope, global_scope, current_lexer);
-  if (hasser == (CPPDeclaration *)NULL || hasser->get_subtype() != CPPDeclaration::ST_function_group) {
-    yyerror("Reference to non-existent or invalid has-function: " + $5->get_fully_scoped_name(), @5);
-  }
+  CPPDeclaration *getter = $5->find_symbol(current_scope, global_scope, current_lexer);
+  if (getter == nullptr || getter->get_subtype() != CPPDeclaration::ST_function_group) {
+    yyerror("reference to non-existent or invalid item getter method: " + $5->get_fully_scoped_name(), @5);
 
 
+  } else {
+    CPPMakeProperty *make_property = new CPPMakeProperty($3, CPPMakeProperty::T_mapping, current_scope, @1.file);
+    make_property->_get_function = getter->as_function_group();
+    current_scope->add_declaration(make_property, global_scope, current_lexer, @1);
+  }
+}
+        | KW_MAKE_MAP_PROPERTY '(' name ',' IDENTIFIER ',' IDENTIFIER ')' ';'
+{
   CPPDeclaration *getter = $7->find_symbol(current_scope, global_scope, current_lexer);
   CPPDeclaration *getter = $7->find_symbol(current_scope, global_scope, current_lexer);
-  if (getter == (CPPDeclaration *)NULL || getter->get_subtype() != CPPDeclaration::ST_function_group) {
+  if (getter == nullptr || getter->get_subtype() != CPPDeclaration::ST_function_group) {
     yyerror("Reference to non-existent or invalid getter: " + $7->get_fully_scoped_name(), @7);
     yyerror("Reference to non-existent or invalid getter: " + $7->get_fully_scoped_name(), @7);
-  }
 
 
-  if (hasser && getter) {
+  } else {
     CPPMakeProperty *make_property;
     CPPMakeProperty *make_property;
-    make_property = new CPPMakeProperty($3,
-                                        hasser->as_function_group(),
-                                        getter->as_function_group(),
-                                        NULL, NULL,
-                                        current_scope, @1.file);
+    make_property = new CPPMakeProperty($3, CPPMakeProperty::T_mapping, current_scope, @1.file);
+    make_property->_get_function = getter->as_function_group();
+
+    CPPDeclaration *hasser = $5->find_symbol(current_scope, global_scope, current_lexer);
+    if (hasser == nullptr || hasser->get_subtype() != CPPDeclaration::ST_function_group) {
+      yyerror("reference to non-existent or invalid has/find method: " + $5->get_fully_scoped_name(), @5);
+    } else {
+      make_property->_has_function = hasser->as_function_group();
+    }
+
     current_scope->add_declaration(make_property, global_scope, current_lexer, @1);
     current_scope->add_declaration(make_property, global_scope, current_lexer, @1);
   }
   }
 }
 }
-        | KW_MAKE_PROPERTY2 '(' name ',' IDENTIFIER ',' IDENTIFIER ',' IDENTIFIER ',' IDENTIFIER ')' ';'
+        | KW_MAKE_MAP_PROPERTY '(' name ',' IDENTIFIER ',' IDENTIFIER ',' IDENTIFIER maybe_comma_identifier ')' ';'
 {
 {
-  CPPDeclaration *hasser = $5->find_symbol(current_scope, global_scope, current_lexer);
-  if (hasser == (CPPDeclaration *)NULL || hasser->get_subtype() != CPPDeclaration::ST_function_group) {
-    yyerror("Reference to non-existent or invalid has-function: " + $5->get_fully_scoped_name(), @5);
-  }
-
   CPPDeclaration *getter = $7->find_symbol(current_scope, global_scope, current_lexer);
   CPPDeclaration *getter = $7->find_symbol(current_scope, global_scope, current_lexer);
-  if (getter == (CPPDeclaration *)NULL || getter->get_subtype() != CPPDeclaration::ST_function_group) {
+  if (getter == nullptr || getter->get_subtype() != CPPDeclaration::ST_function_group) {
     yyerror("Reference to non-existent or invalid getter: " + $7->get_fully_scoped_name(), @7);
     yyerror("Reference to non-existent or invalid getter: " + $7->get_fully_scoped_name(), @7);
-  }
 
 
-  CPPDeclaration *setter = $9->find_symbol(current_scope, global_scope, current_lexer);
-  if (setter == (CPPDeclaration *)NULL || setter->get_subtype() != CPPDeclaration::ST_function_group) {
-    yyerror("Reference to non-existent or invalid setter: " + $9->get_fully_scoped_name(), @9);
+  } else {
+    CPPMakeProperty *make_property = new CPPMakeProperty($3, CPPMakeProperty::T_mapping, current_scope, @1.file);
+    make_property->_get_function = getter->as_function_group();
+
+    CPPDeclaration *hasser = $5->find_symbol(current_scope, global_scope, current_lexer);
+    if (hasser == nullptr || hasser->get_subtype() != CPPDeclaration::ST_function_group) {
+      yyerror("reference to non-existent or invalid has/find method: " + $5->get_fully_scoped_name(), @5);
+    } else {
+      make_property->_has_function = hasser->as_function_group();
+    }
+
+    CPPDeclaration *setter = $9->find_symbol(current_scope, global_scope, current_lexer);
+    if (setter == nullptr || setter->get_subtype() != CPPDeclaration::ST_function_group) {
+      yyerror("Reference to non-existent or invalid setter: " + $9->get_fully_scoped_name(), @9);
+    } else {
+      make_property->_set_function = setter->as_function_group();
+    }
+
+    if ($10 != nullptr) {
+      CPPDeclaration *deleter = $10->find_symbol(current_scope, global_scope, current_lexer);
+      if (deleter == nullptr || deleter->get_subtype() != CPPDeclaration::ST_function_group) {
+        yyerror("reference to non-existent or invalid delete method: " + $10->get_fully_scoped_name(), @10);
+      } else {
+        make_property->_del_function = deleter->as_function_group();
+      }
+    }
+
+    current_scope->add_declaration(make_property, global_scope, current_lexer, @1);
   }
   }
+}
+        | KW_MAKE_PROPERTY2 '(' name ',' IDENTIFIER ',' IDENTIFIER ')' ';'
+{
+  CPPDeclaration *getter = $7->find_symbol(current_scope, global_scope, current_lexer);
+  if (getter == nullptr || getter->get_subtype() != CPPDeclaration::ST_function_group) {
+    yyerror("Reference to non-existent or invalid getter: " + $7->get_fully_scoped_name(), @7);
+
+  } else {
+    CPPMakeProperty *make_property;
+    make_property = new CPPMakeProperty($3, CPPMakeProperty::T_normal,
+                                        current_scope, @1.file);
+    make_property->_get_function = getter->as_function_group();
+
+    CPPDeclaration *hasser = $5->find_symbol(current_scope, global_scope, current_lexer);
+    if (hasser == nullptr || hasser->get_subtype() != CPPDeclaration::ST_function_group) {
+      yyerror("reference to non-existent or invalid has/find method: " + $5->get_fully_scoped_name(), @5);
+    } else {
+      make_property->_has_function = hasser->as_function_group();
+    }
 
 
-  CPPDeclaration *clearer = $11->find_symbol(current_scope, global_scope, current_lexer);
-  if (clearer == (CPPDeclaration *)NULL || clearer->get_subtype() != CPPDeclaration::ST_function_group) {
-    yyerror("Reference to non-existent or invalid clear-function: " + $11->get_fully_scoped_name(), @11);
+    current_scope->add_declaration(make_property, global_scope, current_lexer, @1);
   }
   }
+}
+        | KW_MAKE_PROPERTY2 '(' name ',' IDENTIFIER ',' IDENTIFIER ',' IDENTIFIER ',' IDENTIFIER ')' ';'
+{
+  CPPDeclaration *getter = $7->find_symbol(current_scope, global_scope, current_lexer);
+  if (getter == nullptr || getter->get_subtype() != CPPDeclaration::ST_function_group) {
+    yyerror("Reference to non-existent or invalid getter: " + $7->get_fully_scoped_name(), @7);
 
 
-  if (hasser && getter && setter && clearer) {
+  } else {
     CPPMakeProperty *make_property;
     CPPMakeProperty *make_property;
-    make_property = new CPPMakeProperty($3,
-                                        hasser->as_function_group(),
-                                        getter->as_function_group(),
-                                        setter->as_function_group(),
-                                        clearer->as_function_group(),
+    make_property = new CPPMakeProperty($3, CPPMakeProperty::T_normal,
                                         current_scope, @1.file);
                                         current_scope, @1.file);
+    make_property->_get_function = getter->as_function_group();
+
+    CPPDeclaration *hasser = $5->find_symbol(current_scope, global_scope, current_lexer);
+    if (hasser == nullptr || hasser->get_subtype() != CPPDeclaration::ST_function_group) {
+      yyerror("reference to non-existent or invalid has/find method: " + $5->get_fully_scoped_name(), @5);
+    } else {
+      make_property->_has_function = hasser->as_function_group();
+    }
+
+    CPPDeclaration *setter = $7->find_symbol(current_scope, global_scope, current_lexer);
+    if (setter == nullptr || setter->get_subtype() != CPPDeclaration::ST_function_group) {
+      yyerror("reference to non-existent or invalid setter: " + $7->get_fully_scoped_name(), @7);
+    } else {
+      make_property->_set_function = setter->as_function_group();
+    }
+
+    CPPDeclaration *clearer = $11->find_symbol(current_scope, global_scope, current_lexer);
+    if (clearer == nullptr || clearer->get_subtype() != CPPDeclaration::ST_function_group) {
+      yyerror("reference to non-existent or invalid clear method: " + $11->get_fully_scoped_name(), @11);
+    } else {
+      make_property->_clear_function = clearer->as_function_group();
+    }
+
     current_scope->add_declaration(make_property, global_scope, current_lexer, @1);
     current_scope->add_declaration(make_property, global_scope, current_lexer, @1);
   }
   }
 }
 }
@@ -1723,6 +1786,18 @@ maybe_trailing_return_type:
         ;
         ;
 
 
 
 
+maybe_comma_identifier:
+        empty
+{
+  $$ = NULL;
+}
+        | ',' IDENTIFIER
+{
+  $$ = $2;
+}
+        ;
+
+
 function_parameter_list:
 function_parameter_list:
         empty
         empty
 {
 {

+ 8 - 28
dtool/src/cppparser/cppMakeProperty.cxx

@@ -18,37 +18,17 @@
  *
  *
  */
  */
 CPPMakeProperty::
 CPPMakeProperty::
-CPPMakeProperty(CPPIdentifier *ident,
-                CPPFunctionGroup *getter, CPPFunctionGroup *setter,
+CPPMakeProperty(CPPIdentifier *ident, Type type,
                 CPPScope *current_scope, const CPPFile &file) :
                 CPPScope *current_scope, const CPPFile &file) :
   CPPDeclaration(file),
   CPPDeclaration(file),
   _ident(ident),
   _ident(ident),
-  _length_function(NULL),
-  _has_function(NULL),
-  _get_function(getter),
-  _set_function(setter),
-  _clear_function(NULL),
-  _del_function(NULL)
-{
-  _ident->_native_scope = current_scope;
-}
-
-/**
- *
- */
-CPPMakeProperty::
-CPPMakeProperty(CPPIdentifier *ident,
-                CPPFunctionGroup *hasser, CPPFunctionGroup *getter,
-                CPPFunctionGroup *setter, CPPFunctionGroup *clearer,
-                CPPScope *current_scope, const CPPFile &file) :
-  CPPDeclaration(file),
-  _ident(ident),
-  _length_function(NULL),
-  _has_function(hasser),
-  _get_function(getter),
-  _set_function(setter),
-  _clear_function(clearer),
-  _del_function(NULL)
+  _type(type),
+  _length_function(nullptr),
+  _has_function(nullptr),
+  _get_function(nullptr),
+  _set_function(nullptr),
+  _clear_function(nullptr),
+  _del_function(nullptr)
 {
 {
   _ident->_native_scope = current_scope;
   _ident->_native_scope = current_scope;
 }
 }

+ 63 - 8
dtool/src/cppparser/cppMakeProperty.h

@@ -23,16 +23,72 @@
  * This is a MAKE_PROPERTY() declaration appearing within a class body.  It
  * This is a MAKE_PROPERTY() declaration appearing within a class body.  It
  * means to generate a property within Python, replacing (for instance)
  * means to generate a property within Python, replacing (for instance)
  * get_something()/set_something() with a synthetic 'something' attribute.
  * get_something()/set_something() with a synthetic 'something' attribute.
+ *
+ * This is an example of a simple property (MAKE_PROPERTY is defined as
+ * the built-in __make_property):
+ * @@code
+ *   Thing get_thing() const;
+ *   void set_thing(const Thing &);
+ *
+ *   MAKE_PROPERTY(thing, get_thing, set_thing);
+ * @@endcode
+ * The setter may be omitted to make the property read-only.
+ *
+ * There is also a secondary macro that allows the property to be set to a
+ * cleared state using separate clear functions.  In the scripting language,
+ * this would be represented by a "null" value, or an "optional" construct in
+ * languages that have no notion of a null value.
+ *
+ * @@code
+ *   bool has_thing() const;
+ *   Thing get_thing() const;
+ *   void set_thing(const Thing &);
+ *   void clear_thing();
+ *   MAKE_PROPERTY2(thing, has_thing, get_thing, set_thing, clear_thing);
+ * @@endcode
+ * As with MAKE_PROPERTY, both the setter and clearer can be omitted to create
+ * a read-only property.
+ *
+ * Thirdly, there is a variant called MAKE_SEQ_PROPERTY.  It takes a length
+ * function as argument and the getter and setter take an index as first
+ * argument:
+ * @@code
+ *   size_t get_num_things() const;
+ *   Thing &get_thing(size_t i) const;
+ *   void set_thing(size_t i, Thing value) const;
+ *   void remove_thing(size_t i) const;
+ *
+ *   MAKE_SEQ_PROPERTY(get_num_things, get_thing, set_thing, remove_thing);
+ * @@endcode
+ *
+ * Lastly, there is the possibility to have properties with key/value
+ * associations, often called a "map" or "dictionary" in scripting languages:
+ * @@code
+ *   bool has_thing(string key) const;
+ *   Thing &get_thing(string key) const;
+ *   void set_thing(string key, Thing value) const;
+ *   void clear_thing(string key) const;
+ *
+ *   MAKE_MAP_PROPERTY(things, has_thing, get_thing, set_thing, clear_thing);
+ * @@endcode
+ * You may also replace the "has" function with a "find" function that returns
+ * an index.  If the returned index is negative (or in the case of an unsigned
+ * integer, the maximum value), the item is assumed not to be present in the
+ * mapping.
+ *
+ * It is also possible to use both MAKE_SEQ_PROPERTY and MAKE_MAP_PROPERTY on
+ * the same property name.  This implies that this property has both a
+ * sequence and mapping interface.
  */
  */
 class CPPMakeProperty : public CPPDeclaration {
 class CPPMakeProperty : public CPPDeclaration {
 public:
 public:
-  CPPMakeProperty(CPPIdentifier *ident,
-                  CPPFunctionGroup *getter, CPPFunctionGroup *setter,
-                  CPPScope *current_scope, const CPPFile &file);
+  enum Type {
+    T_normal = 0x0,
+    T_sequence = 0x1,
+    T_mapping = 0x2,
+  };
 
 
-  CPPMakeProperty(CPPIdentifier *ident,
-                  CPPFunctionGroup *hasser, CPPFunctionGroup *getter,
-                  CPPFunctionGroup *setter, CPPFunctionGroup *clearer,
+  CPPMakeProperty(CPPIdentifier *ident, Type type,
                   CPPScope *current_scope, const CPPFile &file);
                   CPPScope *current_scope, const CPPFile &file);
 
 
   virtual string get_simple_name() const;
   virtual string get_simple_name() const;
@@ -46,8 +102,7 @@ public:
   virtual CPPMakeProperty *as_make_property();
   virtual CPPMakeProperty *as_make_property();
 
 
   CPPIdentifier *_ident;
   CPPIdentifier *_ident;
-  // If length_function is not NULL, this is actually a sequence property,
-  // and the other functions take an additional index argument.
+  Type _type;
   CPPFunctionGroup *_length_function;
   CPPFunctionGroup *_length_function;
   CPPFunctionGroup *_has_function;
   CPPFunctionGroup *_has_function;
   CPPFunctionGroup *_get_function;
   CPPFunctionGroup *_get_function;

+ 2 - 0
dtool/src/dtoolbase/dtoolbase.h

@@ -456,6 +456,7 @@ typedef struct _object PyObject;
 #define MAKE_PROPERTY2(property_name, ...) __make_property2(property_name, __VA_ARGS__)
 #define MAKE_PROPERTY2(property_name, ...) __make_property2(property_name, __VA_ARGS__)
 #define MAKE_SEQ(seq_name, num_name, element_name) __make_seq(seq_name, num_name, element_name)
 #define MAKE_SEQ(seq_name, num_name, element_name) __make_seq(seq_name, num_name, element_name)
 #define MAKE_SEQ_PROPERTY(property_name, ...) __make_seq_property(property_name, __VA_ARGS__)
 #define MAKE_SEQ_PROPERTY(property_name, ...) __make_seq_property(property_name, __VA_ARGS__)
+#define MAKE_MAP_PROPERTY(property_name, ...) __make_map_property(property_name, __VA_ARGS__)
 #define EXTENSION(x) __extension x
 #define EXTENSION(x) __extension x
 #define EXTEND __extension
 #define EXTEND __extension
 #else
 #else
@@ -466,6 +467,7 @@ typedef struct _object PyObject;
 #define MAKE_PROPERTY2(property_name, ...)
 #define MAKE_PROPERTY2(property_name, ...)
 #define MAKE_SEQ(seq_name, num_name, element_name)
 #define MAKE_SEQ(seq_name, num_name, element_name)
 #define MAKE_SEQ_PROPERTY(property_name, ...)
 #define MAKE_SEQ_PROPERTY(property_name, ...)
+#define MAKE_MAP_PROPERTY(property_name, ...)
 #define EXTENSION(x)
 #define EXTENSION(x)
 #define EXTEND
 #define EXTEND
 #endif
 #endif

+ 4 - 0
dtool/src/dtoolutil/executionEnvironment.h

@@ -51,6 +51,10 @@ PUBLISHED:
 
 
   static Filename get_cwd();
   static Filename get_cwd();
 
 
+PUBLISHED:
+  MAKE_MAP_PROPERTY(environment_variables, has_environment_variable,
+                    get_environment_variable, set_environment_variable);
+
   MAKE_SEQ_PROPERTY(args, get_num_args, get_arg);
   MAKE_SEQ_PROPERTY(args, get_num_args, get_arg);
   MAKE_PROPERTY(binary_name, get_binary_name, set_binary_name);
   MAKE_PROPERTY(binary_name, get_binary_name, set_binary_name);
   MAKE_PROPERTY(dtool_name, get_dtool_name, set_dtool_name);
   MAKE_PROPERTY(dtool_name, get_dtool_name, set_dtool_name);

+ 6 - 8
dtool/src/interrogate/interfaceMaker.cxx

@@ -75,8 +75,8 @@ InterfaceMaker::MakeSeq::
 MakeSeq(const string &name, const InterrogateMakeSeq &imake_seq) :
 MakeSeq(const string &name, const InterrogateMakeSeq &imake_seq) :
   _name(name),
   _name(name),
   _imake_seq(imake_seq),
   _imake_seq(imake_seq),
-  _length_getter(NULL),
-  _element_getter(NULL)
+  _length_getter(nullptr),
+  _element_getter(nullptr)
 {
 {
 }
 }
 
 
@@ -86,12 +86,10 @@ MakeSeq(const string &name, const InterrogateMakeSeq &imake_seq) :
 InterfaceMaker::Property::
 InterfaceMaker::Property::
 Property(const InterrogateElement &ielement) :
 Property(const InterrogateElement &ielement) :
   _ielement(ielement),
   _ielement(ielement),
-  _length_function(NULL),
-  _getter(NULL),
-  _setter(NULL),
-  _has_function(NULL),
-  _clear_function(NULL),
-  _deleter(NULL),
+  _length_function(nullptr),
+  _has_function(nullptr),
+  _clear_function(nullptr),
+  _deleter(nullptr),
   _has_this(false)
   _has_this(false)
 {
 {
 }
 }

+ 2 - 2
dtool/src/interrogate/interfaceMaker.h

@@ -126,9 +126,9 @@ public:
     Property(const InterrogateElement &ielement);
     Property(const InterrogateElement &ielement);
 
 
     const InterrogateElement &_ielement;
     const InterrogateElement &_ielement;
+    vector<FunctionRemap *> _getter_remaps;
+    vector<FunctionRemap *> _setter_remaps;
     Function *_length_function;
     Function *_length_function;
-    Function *_getter;
-    Function *_setter;
     Function *_has_function;
     Function *_has_function;
     Function *_clear_function;
     Function *_clear_function;
     Function *_deleter;
     Function *_deleter;

+ 380 - 140
dtool/src/interrogate/interfaceMakerPythonNative.cxx

@@ -2640,8 +2640,7 @@ write_module_class(ostream &out, Object *obj) {
     for (pit = obj->_properties.begin(); pit != obj->_properties.end(); ++pit) {
     for (pit = obj->_properties.begin(); pit != obj->_properties.end(); ++pit) {
       Property *property = (*pit);
       Property *property = (*pit);
       const InterrogateElement &ielem = property->_ielement;
       const InterrogateElement &ielem = property->_ielement;
-      if (!property->_has_this ||
-          property->_getter == NULL || !is_function_legal(property->_getter)) {
+      if (!property->_has_this || property->_getter_remaps.empty()) {
         continue;
         continue;
       }
       }
 
 
@@ -2652,8 +2651,7 @@ write_module_class(ostream &out, Object *obj) {
 
 
       string getter = "&Dtool_" + ClassName + "_" + ielem.get_name() + "_Getter";
       string getter = "&Dtool_" + ClassName + "_" + ielem.get_name() + "_Getter";
       string setter = "NULL";
       string setter = "NULL";
-      if (property->_length_function == NULL &&
-          property->_setter != NULL && is_function_legal(property->_setter)) {
+      if (!ielem.is_sequence() && !ielem.is_mapping() && !property->_setter_remaps.empty()) {
         setter = "&Dtool_" + ClassName + "_" + ielem.get_name() + "_Setter";
         setter = "&Dtool_" + ClassName + "_" + ielem.get_name() + "_Setter";
       }
       }
 
 
@@ -3190,8 +3188,7 @@ write_module_class(ostream &out, Object *obj) {
   for (pit = obj->_properties.begin(); pit != obj->_properties.end(); ++pit) {
   for (pit = obj->_properties.begin(); pit != obj->_properties.end(); ++pit) {
     Property *property = (*pit);
     Property *property = (*pit);
     const InterrogateElement &ielem = property->_ielement;
     const InterrogateElement &ielem = property->_ielement;
-    if (property->_has_this ||
-        property->_getter == NULL || !is_function_legal(property->_getter)) {
+    if (property->_has_this || property->_getter_remaps.empty()) {
       continue;
       continue;
     }
     }
 
 
@@ -3200,8 +3197,7 @@ write_module_class(ostream &out, Object *obj) {
 
 
     string getter = "&Dtool_" + ClassName + "_" + ielem.get_name() + "_Getter";
     string getter = "&Dtool_" + ClassName + "_" + ielem.get_name() + "_Getter";
     string setter = "NULL";
     string setter = "NULL";
-    if (property->_length_function == NULL &&
-        property->_setter != NULL && is_function_legal(property->_setter)) {
+    if (!ielem.is_sequence() && !ielem.is_mapping() && !property->_setter_remaps.empty()) {
       setter = "&Dtool_" + ClassName + "_" + ielem.get_name() + "_Setter";
       setter = "&Dtool_" + ClassName + "_" + ielem.get_name() + "_Setter";
     }
     }
 
 
@@ -5997,7 +5993,7 @@ write_function_instance(ostream &out, FunctionRemap *remap,
     // this for coercion constructors since they are called by other wrapper
     // this for coercion constructors since they are called by other wrapper
     // functions which already check this on their own.  Generated getters
     // functions which already check this on their own.  Generated getters
     // obviously can't raise asserts.
     // obviously can't raise asserts.
-    if (watch_asserts && (return_flags & RF_coerced) == 0 &&
+    if (watch_asserts && (return_flags & (RF_coerced | RF_raise_keyerror)) == 0 &&
         remap->_type != FunctionRemap::T_getter &&
         remap->_type != FunctionRemap::T_getter &&
         remap->_type != FunctionRemap::T_setter) {
         remap->_type != FunctionRemap::T_setter) {
       out << "#ifndef NDEBUG\n";
       out << "#ifndef NDEBUG\n";
@@ -6126,6 +6122,27 @@ write_function_instance(ostream &out, FunctionRemap *remap,
       indent(out, indent_level) << "manage = true;\n";
       indent(out, indent_level) << "manage = true;\n";
       indent(out, indent_level) << "return true;\n";
       indent(out, indent_level) << "return true;\n";
     }
     }
+
+  } else if (return_flags & RF_raise_keyerror) {
+    CPPType *orig_type = remap->_return_type->get_orig_type();
+
+    if (TypeManager::is_bool(orig_type) || TypeManager::is_pointer(orig_type)) {
+      indent(out, indent_level) << "if (!" << return_expr << ") {\n";
+    } else if (TypeManager::is_unsigned_integer(orig_type)) {
+      indent(out, indent_level) << "if ((int)" << return_expr << " == -1) {\n";
+    } else if (TypeManager::is_integer(orig_type)) {
+      indent(out, indent_level) << "if (" << return_expr << " < 0) {\n";
+    } else {
+      indent(out, indent_level) << "if (false) {\n";
+    }
+
+    if (args_type == AT_single_arg) {
+      indent(out, indent_level) << "  PyErr_SetObject(PyExc_KeyError, arg);\n";
+    } else {
+      indent(out, indent_level) << "  PyErr_SetObject(PyExc_KeyError, key);\n";
+    }
+    error_return(out, indent_level + 2, return_flags);
+    indent(out, indent_level) << "}\n";
   }
   }
 
 
   // Close the extra braces opened earlier.
   // Close the extra braces opened earlier.
@@ -6398,19 +6415,21 @@ write_make_seq(ostream &out, Object *obj, const std::string &ClassName,
  */
  */
 void InterfaceMakerPythonNative::
 void InterfaceMakerPythonNative::
 write_getset(ostream &out, Object *obj, Property *property) {
 write_getset(ostream &out, Object *obj, Property *property) {
+  // We keep around this empty vector for passing to get_call_str.
+  const vector_string pexprs;
 
 
   string ClassName = make_safe_name(obj->_itype.get_scoped_name());
   string ClassName = make_safe_name(obj->_itype.get_scoped_name());
   std::string cClassName = obj->_itype.get_true_name();
   std::string cClassName = obj->_itype.get_true_name();
 
 
   const InterrogateElement &ielem = property->_ielement;
   const InterrogateElement &ielem = property->_ielement;
 
 
-  if (property->_length_function != NULL) {
+  FunctionRemap *len_remap = nullptr;
+  if (property->_length_function != nullptr) {
     // This is actually a sequence.  Wrap this with a special class.
     // This is actually a sequence.  Wrap this with a special class.
-    FunctionRemap *len_remap = property->_length_function->_remaps.front();
-    vector_string pexprs;
+    len_remap = property->_length_function->_remaps.front();
 
 
     out << "/**\n"
     out << "/**\n"
-           " * sequence length function for property " << cClassName << "::" << ielem.get_name() << "\n"
+           " * sequence length function for property " << ielem.get_scoped_name() << "\n"
            " */\n"
            " */\n"
            "static Py_ssize_t Dtool_" + ClassName + "_" + ielem.get_name() + "_Len(PyObject *self) {\n";
            "static Py_ssize_t Dtool_" + ClassName + "_" + ielem.get_name() + "_Len(PyObject *self) {\n";
     if (property->_length_function->_has_this) {
     if (property->_length_function->_has_this) {
@@ -6424,72 +6443,72 @@ write_getset(ostream &out, Object *obj, Property *property) {
       out << "  return (Py_ssize_t)" << len_remap->get_call_str("", pexprs) << ";\n";
       out << "  return (Py_ssize_t)" << len_remap->get_call_str("", pexprs) << ";\n";
     }
     }
     out << "}\n\n";
     out << "}\n\n";
+  }
+
+  if (property->_getter_remaps.empty()) {
+    return;
+  }
+
+  if (ielem.is_sequence()) {
+    out <<
+      "/**\n"
+      " * sequence getter for property " << ielem.get_scoped_name() << "\n"
+      " */\n"
+      "static PyObject *Dtool_" + ClassName + "_" + ielem.get_name() + "_Sequence_Getitem(PyObject *self, Py_ssize_t index) {\n";
 
 
-    // Now write out the getitem helper function.
-    if (property->_getter != NULL) {
+    if (property->_has_this) {
       out <<
       out <<
-        "/**\n"
-        " * sequence getter for property " << cClassName << "::" << ielem.get_name() << "\n"
-        " */\n"
-        "static PyObject *Dtool_" + ClassName + "_" + ielem.get_name() + "_Getitem(PyObject *self, Py_ssize_t index) {\n";
-      if (property->_getter->_has_this ||
-          (property->_has_function && property->_has_function->_has_this)) {
-        out <<
-          "  " << cClassName << " *local_this = NULL;\n"
-          "  if (!Dtool_Call_ExtractThisPointer(self, Dtool_" << ClassName << ", (void **)&local_this)) {\n"
-          "    return NULL;\n"
-          "  }\n";
-      }
+        "  " << cClassName << " *local_this = NULL;\n"
+        "  if (!Dtool_Call_ExtractThisPointer(self, Dtool_" << ClassName << ", (void **)&local_this)) {\n"
+        "    return NULL;\n"
+        "  }\n";
+    }
 
 
-      // This is a getitem of a sequence type.  This means we *need* to raise
-      // IndexError if we're out of bounds.
-      out << "  if (index < 0 || index >= (Py_ssize_t)"
-          << len_remap->get_call_str("local_this", pexprs) << ") {\n";
-      out << "    PyErr_SetString(PyExc_IndexError, \"" << ClassName << "." << ielem.get_name() << "[] index out of range\");\n";
-      out << "    return NULL;\n";
-      out << "  }\n";
+    // This is a getitem of a sequence type.  This means we *need* to raise
+    // IndexError if we're out of bounds.
+    out << "  if (index < 0 || index >= (Py_ssize_t)"
+        << len_remap->get_call_str("local_this", pexprs) << ") {\n";
+    out << "    PyErr_SetString(PyExc_IndexError, \"" << ClassName << "." << ielem.get_name() << "[] index out of range\");\n";
+    out << "    return NULL;\n";
+    out << "  }\n";
 
 
-      if (property->_has_function != NULL) {
-        if (property->_has_function->_has_this) {
-          out << "  if (!local_this->" << property->_has_function->_ifunc.get_name() << "(index)) {\n";
-        } else {
-          out << "  if (!" << cClassName << "::" << property->_has_function->_ifunc.get_name() << "(index)) {\n";
-        }
-        out << "    Py_INCREF(Py_None);\n"
-            << "    return Py_None;\n"
-            << "  }\n";
-      }
+    /*if (property->_has_function != NULL) {
+      out << "  if (!local_this->" << property->_has_function->_ifunc.get_name() << "(index)) {\n"
+          << "    Py_INCREF(Py_None);\n"
+          << "    return Py_None;\n"
+          << "  }\n";
+    }*/
 
 
-      std::set<FunctionRemap*> remaps;
+    std::set<FunctionRemap*> remaps;
 
 
-      // Extract only the getters that take one argument.
-      Function::Remaps::iterator it;
-      for (it = property->_getter->_remaps.begin();
-           it != property->_getter->_remaps.end();
-           ++it) {
-        FunctionRemap *remap = *it;
-        int min_num_args = remap->get_min_num_args();
-        int max_num_args = remap->get_max_num_args();
-        if (min_num_args <= 1 && max_num_args >= 1) {
-          remaps.insert(remap);
-        }
+    // Extract only the getters that take one integral argument.
+    Function::Remaps::iterator it;
+    for (it = property->_getter_remaps.begin();
+          it != property->_getter_remaps.end();
+          ++it) {
+      FunctionRemap *remap = *it;
+      int min_num_args = remap->get_min_num_args();
+      int max_num_args = remap->get_max_num_args();
+      if (min_num_args <= 1 && max_num_args >= 1 &&
+          TypeManager::is_integer(remap->_parameters[(size_t)remap->_has_this]._remap->get_new_type())) {
+        remaps.insert(remap);
       }
       }
+    }
 
 
-      string expected_params;
-      write_function_forset(out, remaps, 1, 1, expected_params, 2, true, true,
-                            AT_no_args, RF_pyobject | RF_err_null, false, true, "index");
+    string expected_params;
+    write_function_forset(out, 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"
-             "  }\n"
-             "}\n\n";
-    }
+    out << "  if (!_PyErr_OCCURRED()) {\n";
+    out << "    return Dtool_Raise_BadArgumentsError(\n";
+    output_quoted(out, 6, expected_params);
+    out << ");\n"
+            "  }\n"
+            "}\n\n";
 
 
     // Write out a setitem if this is not a read-only property.
     // Write out a setitem if this is not a read-only property.
-    if (property->_setter != NULL) {
-      out << "static int Dtool_" + ClassName + "_" + ielem.get_name() + "_Setitem(PyObject *self, Py_ssize_t index, PyObject *arg) {\n";
+    if (!property->_setter_remaps.empty()) {
+      out << "static int Dtool_" + ClassName + "_" + ielem.get_name() + "_Sequence_Setitem(PyObject *self, Py_ssize_t index, PyObject *arg) {\n";
       if (property->_has_this) {
       if (property->_has_this) {
         out << "  " << cClassName  << " *local_this = NULL;\n";
         out << "  " << cClassName  << " *local_this = NULL;\n";
         out << "  if (!Dtool_Call_ExtractThisPointer_NonConst(self, Dtool_" << ClassName << ", (void **)&local_this, \""
         out << "  if (!Dtool_Call_ExtractThisPointer_NonConst(self, Dtool_" << ClassName << ", (void **)&local_this, \""
@@ -6527,13 +6546,14 @@ write_getset(ostream &out, Object *obj, Property *property) {
 
 
       // Extract only the setters that take two arguments.
       // Extract only the setters that take two arguments.
       Function::Remaps::iterator it;
       Function::Remaps::iterator it;
-      for (it = property->_setter->_remaps.begin();
-           it != property->_setter->_remaps.end();
+      for (it = property->_setter_remaps.begin();
+           it != property->_setter_remaps.end();
            ++it) {
            ++it) {
         FunctionRemap *remap = *it;
         FunctionRemap *remap = *it;
         int min_num_args = remap->get_min_num_args();
         int min_num_args = remap->get_min_num_args();
         int max_num_args = remap->get_max_num_args();
         int max_num_args = remap->get_max_num_args();
-        if (min_num_args <= 2 && max_num_args >= 2) {
+        if (min_num_args <= 2 && max_num_args >= 2 &&
+            TypeManager::is_integer(remap->_parameters[1]._remap->get_new_type())) {
           remaps.insert(remap);
           remaps.insert(remap);
         }
         }
       }
       }
@@ -6551,8 +6571,192 @@ write_getset(ostream &out, Object *obj, Property *property) {
       out << "  return -1;\n";
       out << "  return -1;\n";
       out << "}\n\n";
       out << "}\n\n";
     }
     }
+  }
+
+
+  // Write the getitem functions.
+  if (ielem.is_mapping()) {
+    out <<
+      "/**\n"
+      " * mapping getitem for property " << ielem.get_scoped_name() << "\n"
+      " */\n"
+      "static PyObject *Dtool_" + ClassName + "_" + ielem.get_name() + "_Mapping_Getitem(PyObject *self, PyObject *arg) {\n";
+
+    // Before we do the has_function: if this is also a sequence, then we have
+    // to also handle the case here that we were passed an index.
+    if (ielem.is_sequence()) {
+      out <<
+        "#if PY_MAJOR_VERSION >= 3\n"
+        "  if (PyLong_CheckExact(arg)) {\n"
+        "#else\n"
+        "  if (PyLong_CheckExact(arg) || PyInt_CheckExact(arg)) {\n"
+        "#endif\n"
+        "    return Dtool_" << ClassName << "_" << ielem.get_name() << "_Sequence_Getitem(self, PyLongOrInt_AsSize_t(arg));\n"
+        "  }\n\n";
+    }
+
+    if (property->_has_this) {
+      out <<
+        "  " << cClassName << " *local_this = NULL;\n"
+        "  if (!Dtool_Call_ExtractThisPointer(self, Dtool_" << ClassName << ", (void **)&local_this)) {\n"
+        "    return NULL;\n"
+        "  }\n";
+    }
+
+    if (property->_has_function != NULL) {
+      std::set<FunctionRemap*> remaps;
+      remaps.insert(property->_has_function->_remaps.begin(),
+                    property->_has_function->_remaps.end());
+
+      out << "  {\n";
+      string expected_params;
+      write_function_forset(out, remaps, 1, 1, expected_params, 4, true, true,
+                            AT_single_arg, RF_raise_keyerror | RF_err_null, false, true);
+      out << "  }\n";
+    }
+
+    std::set<FunctionRemap*> remaps;
+    // Extract only the getters that take one argument.  Fish out the ones
+    // already taken by the sequence getter.
+    Function::Remaps::iterator it;
+    for (it = property->_getter_remaps.begin();
+          it != property->_getter_remaps.end();
+          ++it) {
+      FunctionRemap *remap = *it;
+      int min_num_args = remap->get_min_num_args();
+      int max_num_args = remap->get_max_num_args();
+      if (min_num_args <= 1 && max_num_args >= 1 &&
+          (!ielem.is_sequence() || !TypeManager::is_integer(remap->_parameters[(size_t)remap->_has_this]._remap->get_new_type()))) {
+        remaps.insert(remap);
+      }
+    }
+
+    string expected_params;
+    write_function_forset(out, remaps, 1, 1, expected_params, 2, true, true,
+                          AT_single_arg, RF_pyobject | RF_err_null, false, true);
+
+    out << "  if (!_PyErr_OCCURRED()) {\n";
+    out << "    return Dtool_Raise_BadArgumentsError(\n";
+    output_quoted(out, 6, expected_params);
+    out << ");\n"
+            "  }\n"
+            "  return NULL;\n"
+            "}\n\n";
+
+    // Write out a setitem if this is not a read-only property.
+    if (!property->_setter_remaps.empty()) {
+      out <<
+        "/**\n"
+        " * mapping setitem for property " << ielem.get_scoped_name() << "\n"
+        " */\n"
+        "static int Dtool_" + ClassName + "_" + ielem.get_name() + "_Mapping_Setitem(PyObject *self, PyObject *key, PyObject *value) {\n";
+
+      if (property->_has_this) {
+        out <<
+          "  " << cClassName  << " *local_this = NULL;\n"
+          "  if (!Dtool_Call_ExtractThisPointer_NonConst(self, Dtool_" << ClassName << ", (void **)&local_this, \""
+            << classNameFromCppName(cClassName, false) << "." << ielem.get_name() << "\")) {\n"
+          "    return -1;\n"
+          "  }\n\n";
+      }
+
+      out << "  if (value == (PyObject *)NULL) {\n";
+      if (property->_deleter != NULL) {
+        out << "    PyObject *arg = key;\n";
+        std::set<FunctionRemap*> remaps;
+        remaps.insert(property->_deleter->_remaps.begin(),
+                      property->_deleter->_remaps.end());
+
+        string expected_params;
+        write_function_forset(out, remaps, 1, 1,
+                              expected_params, 4, true, true, AT_single_arg,
+                              RF_int, false, false);
+        out << "    return -1;\n";
+      } else {
+        out << "    Dtool_Raise_TypeError(\"can't delete " << ielem.get_name() << "[] attribute\");\n"
+               "    return -1;\n";
+      }
+      out << "  }\n";
+
+      if (property->_clear_function != NULL) {
+        out << "  if (value == Py_None) {\n"
+            << "    local_this->" << property->_clear_function->_ifunc.get_name() << "(key);\n"
+            << "    return 0;\n"
+            << "  }\n";
+      }
+
+      std::set<FunctionRemap*> remaps;
+      remaps.insert(property->_setter_remaps.begin(),
+                    property->_setter_remaps.end());
+
+      // We have to create an args tuple only to unpack it alter, ugh.
+      out << "  PyObject *args = PyTuple_New(2);\n"
+          << "  PyTuple_SET_ITEM(args, 0, key);\n"
+          << "  PyTuple_SET_ITEM(args, 1, value);\n"
+          << "  Py_INCREF(key);\n"
+          << "  Py_INCREF(value);\n";
+
+      string expected_params;
+      write_function_forset(out, remaps, 2, 2,
+                            expected_params, 2, true, true, AT_varargs,
+                            RF_int | RF_decref_args, false, false);
+
+      out << "  if (!_PyErr_OCCURRED()) {\n";
+      out << "    Dtool_Raise_BadArgumentsError(\n";
+      output_quoted(out, 6, expected_params);
+      out << ");\n";
+      out << "  }\n";
+      out << "  Py_DECREF(args);\n";
+      out << "  return -1;\n";
+      out << "}\n\n";
+    }
+  }
+
+  // Now write the actual getter wrapper.  It will be a different wrapper
+  // depending on whether it's a mapping, sequence, or both.
+  if (ielem.is_mapping() && ielem.is_sequence()) {
+    out << "static PyObject *Dtool_" + ClassName + "_" + ielem.get_name() + "_Getter(PyObject *self, void *) {\n";
+    if (property->_has_this) {
+      out << "  nassertr(self != NULL, NULL);\n"
+             "  Py_INCREF(self);\n";
+    } else {
+      out << "  Py_XINCREF(self);\n";
+    }
+    out << "  Dtool_SeqMapWrapper *wrap = PyObject_New(Dtool_SeqMapWrapper, &Dtool_SeqMapWrapper_Type);\n"
+           "  wrap->_seq._base._self = self;\n"
+           "  wrap->_seq._len_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Len;\n"
+           "  wrap->_seq._getitem_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Sequence_Getitem;\n"
+           "  wrap->_map_getitem_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Mapping_Getitem;\n";
+    if (!property->_setter_remaps.empty()) {
+      out << "  wrap->_seq._setitem_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Sequence_Setitem;\n";
+      out << "  wrap->_map_setitem_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Mapping_Setitem;\n";
+    } else {
+      out << "  wrap->_seq._setitem_func = NULL;\n";
+      out << "  wrap->_map_setitem_func = NULL;\n";
+    }
+    out << "  return (PyObject *)wrap;\n"
+            "}\n\n";
+
+  } else if (ielem.is_mapping()) {
+    out << "static PyObject *Dtool_" + ClassName + "_" + ielem.get_name() + "_Getter(PyObject *self, void *) {\n";
+    if (property->_has_this) {
+      out << "  nassertr(self != NULL, NULL);\n"
+             "  Py_INCREF(self);\n";
+    } else {
+      out << "  Py_XINCREF(self);\n";
+    }
+    out << "  Dtool_MappingWrapper *wrap = PyObject_New(Dtool_MappingWrapper, &Dtool_MappingWrapper_Type);\n"
+           "  wrap->_base._self = self;\n"
+           "  wrap->_getitem_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Mapping_Getitem;\n";
+    if (!property->_setter_remaps.empty()) {
+      out << "  wrap->_setitem_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Mapping_Setitem;\n";
+    } else {
+      out << "  wrap->_setitem_func = NULL;\n";
+    }
+    out << "  return (PyObject *)wrap;\n"
+           "}\n\n";
 
 
-    // Now write the getter, which returns a special wrapper object.
+  } else if (ielem.is_sequence()) {
     out << "static PyObject *Dtool_" + ClassName + "_" + ielem.get_name() + "_Getter(PyObject *self, void *) {\n";
     out << "static PyObject *Dtool_" + ClassName + "_" + ielem.get_name() + "_Getter(PyObject *self, void *) {\n";
     if (property->_has_this) {
     if (property->_has_this) {
       out << "  nassertr(self != NULL, NULL);\n"
       out << "  nassertr(self != NULL, NULL);\n"
@@ -6561,21 +6765,21 @@ write_getset(ostream &out, Object *obj, Property *property) {
       out << "  Py_XINCREF(self);\n";
       out << "  Py_XINCREF(self);\n";
     }
     }
     out << "  Dtool_SequenceWrapper *wrap = PyObject_New(Dtool_SequenceWrapper, &Dtool_SequenceWrapper_Type);\n"
     out << "  Dtool_SequenceWrapper *wrap = PyObject_New(Dtool_SequenceWrapper, &Dtool_SequenceWrapper_Type);\n"
-           "  wrap->_base = self;\n"
+           "  wrap->_base._self = self;\n"
            "  wrap->_len_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Len;\n"
            "  wrap->_len_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Len;\n"
-           "  wrap->_getitem_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Getitem;\n";
-    if (property->_setter != NULL) {
-      out << "  wrap->_setitem_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Setitem;\n";
+           "  wrap->_getitem_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Sequence_Getitem;\n";
+    if (!property->_setter_remaps.empty()) {
+      out << "  wrap->_setitem_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Sequence_Setitem;\n";
     } else {
     } else {
       out << "  wrap->_setitem_func = NULL;\n";
       out << "  wrap->_setitem_func = NULL;\n";
     }
     }
     out << "  return (PyObject *)wrap;\n"
     out << "  return (PyObject *)wrap;\n"
-           "}\n\n";
+            "}\n\n";
 
 
-  } else if (property->_getter != NULL) {
+  } else {
     // Write out a regular, unwrapped getter.
     // Write out a regular, unwrapped getter.
     out << "static PyObject *Dtool_" + ClassName + "_" + ielem.get_name() + "_Getter(PyObject *self, void *) {\n";
     out << "static PyObject *Dtool_" + ClassName + "_" + ielem.get_name() + "_Getter(PyObject *self, void *) {\n";
-    FunctionRemap *remap = property->_getter->_remaps.front();
+    FunctionRemap *remap = property->_getter_remaps.front();
 
 
     if (remap->_has_this) {
     if (remap->_has_this) {
       if (remap->_const_method) {
       if (remap->_const_method) {
@@ -6611,7 +6815,7 @@ write_getset(ostream &out, Object *obj, Property *property) {
     out << "}\n\n";
     out << "}\n\n";
 
 
     // Write out a setter if this is not a read-only property.
     // Write out a setter if this is not a read-only property.
-    if (property->_setter != NULL) {
+    if (!property->_setter_remaps.empty()) {
       out << "static int Dtool_" + ClassName + "_" + ielem.get_name() + "_Setter(PyObject *self, PyObject *arg, void *) {\n";
       out << "static int Dtool_" + ClassName + "_" + ielem.get_name() + "_Setter(PyObject *self, PyObject *arg, void *) {\n";
       if (remap->_has_this) {
       if (remap->_has_this) {
         out << "  " << cClassName  << " *local_this = NULL;\n";
         out << "  " << cClassName  << " *local_this = NULL;\n";
@@ -6630,7 +6834,7 @@ write_getset(ostream &out, Object *obj, Property *property) {
             << "    return 0;\n";
             << "    return 0;\n";
       } else {
       } else {
         out << "    Dtool_Raise_TypeError(\"can't delete " << ielem.get_name() << " attribute\");\n"
         out << "    Dtool_Raise_TypeError(\"can't delete " << ielem.get_name() << " attribute\");\n"
-               "    return -1;\n";
+                "    return -1;\n";
       }
       }
       out << "  }\n";
       out << "  }\n";
 
 
@@ -6649,9 +6853,9 @@ write_getset(ostream &out, Object *obj, Property *property) {
 
 
       // Extract only the setters that take one argument.
       // Extract only the setters that take one argument.
       Function::Remaps::iterator it;
       Function::Remaps::iterator it;
-      for (it = property->_setter->_remaps.begin();
-           it != property->_setter->_remaps.end();
-           ++it) {
+      for (it = property->_setter_remaps.begin();
+            it != property->_setter_remaps.end();
+            ++it) {
         FunctionRemap *remap = *it;
         FunctionRemap *remap = *it;
         int min_num_args = remap->get_min_num_args();
         int min_num_args = remap->get_min_num_args();
         int max_num_args = remap->get_max_num_args();
         int max_num_args = remap->get_max_num_args();
@@ -6763,62 +6967,8 @@ record_object(TypeIndex type_index) {
     ElementIndex element_index = itype.get_element(ei);
     ElementIndex element_index = itype.get_element(ei);
     const InterrogateElement &ielement = idb->get_element(element_index);
     const InterrogateElement &ielement = idb->get_element(element_index);
 
 
-    Property *property = new Property(ielement);
-
-    if (ielement.has_setter()) {
-      FunctionIndex func_index = ielement.get_setter();
-      Function *setter = record_function(itype, func_index);
-      if (is_function_legal(setter)) {
-        property->_setter = setter;
-        property->_has_this |= setter->_has_this;
-      }
-    }
-
-    if (ielement.has_getter()) {
-      FunctionIndex func_index = ielement.get_getter();
-      Function *getter = record_function(itype, func_index);
-      if (is_function_legal(getter)) {
-        property->_getter = getter;
-        property->_has_this |= getter->_has_this;
-      }
-    }
-
-    if (ielement.has_has_function()) {
-      FunctionIndex func_index = ielement.get_has_function();
-      Function *has_function = record_function(itype, func_index);
-      if (is_function_legal(has_function)) {
-        property->_has_function = has_function;
-        property->_has_this |= has_function->_has_this;
-      }
-    }
-
-    if (ielement.has_clear_function()) {
-      FunctionIndex func_index = ielement.get_clear_function();
-      Function *clear_function = record_function(itype, func_index);
-      if (is_function_legal(clear_function)) {
-        property->_clear_function = clear_function;
-        property->_has_this |= clear_function->_has_this;
-      }
-    }
-
-    if (ielement.has_del_function()) {
-      FunctionIndex func_index = ielement.get_del_function();
-      Function *del_function = record_function(itype, func_index);
-      if (is_function_legal(del_function)) {
-        property->_deleter = del_function;
-        property->_has_this |= del_function->_has_this;
-      }
-    }
-
-    if (ielement.is_sequence()) {
-      FunctionIndex func_index = ielement.get_length_function();
-      property->_length_function = record_function(itype, func_index);
-      if (property->_length_function != nullptr) {
-        property->_has_this |= property->_length_function->_has_this;
-      }
-    }
-
-    if (property->_getter != NULL) {
+    Property *property = record_property(itype, itype.get_element(ei));
+    if (property != nullptr) {
       object->_properties.push_back(property);
       object->_properties.push_back(property);
     } else {
     } else {
       // No use exporting a property without a getter.
       // No use exporting a property without a getter.
@@ -6850,6 +7000,96 @@ record_object(TypeIndex type_index) {
   }
   }
   return object;
   return object;
 }
 }
+
+/**
+ *
+ */
+InterfaceMaker::Property *InterfaceMakerPythonNative::
+record_property(const InterrogateType &itype, ElementIndex element_index) {
+  InterrogateDatabase *idb = InterrogateDatabase::get_ptr();
+  const InterrogateElement &ielement = idb->get_element(element_index);
+  if (!ielement.has_getter()) {
+    // A property needs at the very least a getter.
+    return nullptr;
+  }
+
+  Property *property;
+  {
+    FunctionIndex func_index = ielement.get_getter();
+    if (func_index != 0) {
+      const InterrogateFunction &ifunc = idb->get_function(func_index);
+      property = new Property(ielement);
+
+      InterrogateFunction::Instances::const_iterator ii;
+      for (ii = ifunc._instances->begin(); ii != ifunc._instances->end(); ++ii) {
+        CPPInstance *cppfunc = (*ii).second;
+        FunctionRemap *remap =
+          make_function_remap(itype, ifunc, cppfunc, 0);
+
+        if (remap != nullptr && is_remap_legal(remap)) {
+          property->_getter_remaps.push_back(remap);
+          property->_has_this |= remap->_has_this;
+        }
+      }
+    } else {
+      return nullptr;
+    }
+  }
+
+  if (ielement.has_setter()) {
+    FunctionIndex func_index = ielement.get_setter();
+    if (func_index != 0) {
+      const InterrogateFunction &ifunc = idb->get_function(func_index);
+
+      InterrogateFunction::Instances::const_iterator ii;
+      for (ii = ifunc._instances->begin(); ii != ifunc._instances->end(); ++ii) {
+        CPPInstance *cppfunc = (*ii).second;
+        FunctionRemap *remap =
+          make_function_remap(itype, ifunc, cppfunc, 0);
+
+        if (remap != nullptr && is_remap_legal(remap)) {
+          property->_setter_remaps.push_back(remap);
+          property->_has_this |= remap->_has_this;
+        }
+      }
+    }
+  }
+
+  if (ielement.has_has_function()) {
+    FunctionIndex func_index = ielement.get_has_function();
+    Function *has_function = record_function(itype, func_index);
+    if (is_function_legal(has_function)) {
+      property->_has_function = has_function;
+      property->_has_this |= has_function->_has_this;
+    }
+  }
+
+  if (ielement.has_clear_function()) {
+    FunctionIndex func_index = ielement.get_clear_function();
+    Function *clear_function = record_function(itype, func_index);
+    if (is_function_legal(clear_function)) {
+      property->_clear_function = clear_function;
+      property->_has_this |= clear_function->_has_this;
+    }
+  }
+
+  if (ielement.has_del_function()) {
+    FunctionIndex func_index = ielement.get_del_function();
+    Function *del_function = record_function(itype, func_index);
+    if (is_function_legal(del_function)) {
+      property->_deleter = del_function;
+      property->_has_this |= del_function->_has_this;
+    }
+  }
+
+  if (ielement.is_sequence()) {
+    FunctionIndex func_index = ielement.get_length_function();
+    property->_length_function = record_function(itype, func_index);
+  }
+
+  return property;
+}
+
 /**
 /**
  * Walks through the set of functions in the database and generates wrappers
  * Walks through the set of functions in the database and generates wrappers
  * for each function, storing these in the database.  No actual code should be
  * for each function, storing these in the database.  No actual code should be

+ 4 - 0
dtool/src/interrogate/interfaceMakerPythonNative.h

@@ -47,6 +47,7 @@ public:
   virtual bool separate_overloading();
   virtual bool separate_overloading();
 
 
   virtual Object *record_object(TypeIndex type_index);
   virtual Object *record_object(TypeIndex type_index);
+  Property *record_property(const InterrogateType &itype, ElementIndex element_index);
 
 
 protected:
 protected:
   virtual string get_wrapper_prefix();
   virtual string get_wrapper_prefix();
@@ -111,6 +112,9 @@ private:
 
 
     // Decref temporary args object before returning.
     // Decref temporary args object before returning.
     RF_decref_args = 0x1000,
     RF_decref_args = 0x1000,
+
+    // This raises a KeyError on falsey (or -1) return value.
+    RF_raise_keyerror = 0x4000,
   };
   };
 
 
   class SlottedFunctionDef {
   class SlottedFunctionDef {

+ 96 - 41
dtool/src/interrogate/interrogateBuilder.cxx

@@ -1798,34 +1798,46 @@ get_make_property(CPPMakeProperty *make_property, CPPStructType *struct_type, CP
   }
   }
 
 
   string property_name = make_property->get_local_name(&parser);
   string property_name = make_property->get_local_name(&parser);
+  InterrogateDatabase *idb = InterrogateDatabase::get_ptr();
 
 
   // First, check to see if it's already there.
   // First, check to see if it's already there.
+  ElementIndex index = 0;
   PropertiesByName::const_iterator tni =
   PropertiesByName::const_iterator tni =
     _properties_by_name.find(property_name);
     _properties_by_name.find(property_name);
   if (tni != _properties_by_name.end()) {
   if (tni != _properties_by_name.end()) {
-    ElementIndex index = (*tni).second;
-    return index;
+    index = (*tni).second;
+    const InterrogateElement &ielem = idb->get_element(index);
+    if (ielem._make_property == make_property) {
+      // This is the same property.
+      return index;
+    }
+
+    // It is possible to have property definitions with the same name, but
+    // they cannot define conflicting interfaces.
+    if ((ielem.is_sequence() || ielem.is_mapping()) !=
+        (make_property->_type != CPPMakeProperty::T_normal)) {
+      cerr << "Conflicting property definitions for " << property_name << "!\n";
+      return index;
+    }
   }
   }
 
 
   // If we have a length function (ie. this is a sequence property), we should
   // If we have a length function (ie. this is a sequence property), we should
   // find the function that will give us the length.
   // find the function that will give us the length.
   FunctionIndex length_function = 0;
   FunctionIndex length_function = 0;
-  bool is_seq = false;
-
-  CPPFunctionGroup::Instances::const_iterator fi;
-  CPPFunctionGroup *fgroup = make_property->_length_function;
-  if (fgroup != NULL) {
-    is_seq = true;
-
-    for (fi = fgroup->_instances.begin(); fi != fgroup->_instances.end(); ++fi) {
-      CPPInstance *function = (*fi);
-      CPPFunctionType *ftype =
-        function->_type->as_function_type();
-      if (ftype != NULL) {
-        length_function = get_function(function, "", struct_type,
-                                       struct_type->get_scope(), 0);
-        if (length_function != 0) {
-          break;
+  if (make_property->_type & CPPMakeProperty::T_sequence) {
+    CPPFunctionGroup::Instances::const_iterator fi;
+    CPPFunctionGroup *fgroup = make_property->_length_function;
+    if (fgroup != NULL) {
+      for (fi = fgroup->_instances.begin(); fi != fgroup->_instances.end(); ++fi) {
+        CPPInstance *function = (*fi);
+        CPPFunctionType *ftype =
+          function->_type->as_function_type();
+        if (ftype != NULL) {
+          length_function = get_function(function, "", struct_type,
+                                         struct_type->get_scope(), 0);
+          if (length_function != 0) {
+            break;
+          }
         }
         }
       }
       }
     }
     }
@@ -1840,7 +1852,10 @@ get_make_property(CPPMakeProperty *make_property, CPPStructType *struct_type, CP
   CPPInstance *getter = NULL;
   CPPInstance *getter = NULL;
   CPPType *return_type = NULL;
   CPPType *return_type = NULL;
 
 
-  fgroup = make_property->_get_function;
+  // How many arguments we expect the getter to have.
+  size_t num_args = (size_t)(make_property->_type != CPPMakeProperty::T_normal);
+
+  CPPFunctionGroup *fgroup = make_property->_get_function;
   if (fgroup != NULL) {
   if (fgroup != NULL) {
     CPPFunctionGroup::Instances::const_iterator fi;
     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) {
@@ -1852,9 +1867,13 @@ get_make_property(CPPMakeProperty *make_property, CPPStructType *struct_type, CP
 
 
       const CPPParameterList::Parameters &params = ftype->_parameters->_parameters;
       const CPPParameterList::Parameters &params = ftype->_parameters->_parameters;
 
 
-      size_t expected_num_args = (size_t)is_seq;
+      size_t expected_num_args = 0;
       size_t index_arg = 0;
       size_t index_arg = 0;
 
 
+      if (make_property->_type != CPPMakeProperty::T_normal) {
+        ++expected_num_args;
+      }
+
       if (!params.empty() && params[0]->get_simple_name() == "self" &&
       if (!params.empty() && params[0]->get_simple_name() == "self" &&
           TypeManager::is_pointer_to_PyObject(params[0]->_type)) {
           TypeManager::is_pointer_to_PyObject(params[0]->_type)) {
         // Taking a PyObject *self argument.
         // Taking a PyObject *self argument.
@@ -1867,7 +1886,8 @@ get_make_property(CPPMakeProperty *make_property, CPPStructType *struct_type, CP
           (params.size() > expected_num_args &&
           (params.size() > expected_num_args &&
            params[expected_num_args]->_initializer != NULL)) {
            params[expected_num_args]->_initializer != NULL)) {
         // If this is a sequence getter, it must take an index argument.
         // If this is a sequence getter, it must take an index argument.
-        if (is_seq && !TypeManager::is_integer(params[index_arg]->_type)) {
+        if (make_property->_type == CPPMakeProperty::T_sequence &&
+            !TypeManager::is_integer(params[index_arg]->_type)) {
           continue;
           continue;
         }
         }
 
 
@@ -1899,13 +1919,14 @@ get_make_property(CPPMakeProperty *make_property, CPPStructType *struct_type, CP
       CPPInstance *function = (*fi);
       CPPInstance *function = (*fi);
       CPPFunctionType *ftype =
       CPPFunctionType *ftype =
         function->_type->as_function_type();
         function->_type->as_function_type();
-      if (ftype != NULL && TypeManager::is_bool(ftype->_return_type)) {
+      if (ftype != nullptr && (TypeManager::is_integer(ftype->_return_type) ||
+                               TypeManager::is_pointer(ftype->_return_type))) {
         hasser = function;
         hasser = function;
         break;
         break;
       }
       }
     }
     }
 
 
-    if (hasser == NULL || return_type == NULL) {
+    if (hasser == nullptr) {
       cerr << "No instance of has-function '"
       cerr << "No instance of has-function '"
            << fgroup->_name << "' is suitable!\n";
            << fgroup->_name << "' is suitable!\n";
       return 0;
       return 0;
@@ -1921,44 +1942,77 @@ get_make_property(CPPMakeProperty *make_property, CPPStructType *struct_type, CP
     for (fi = fgroup->_instances.begin(); fi != fgroup->_instances.end(); ++fi) {
     for (fi = fgroup->_instances.begin(); fi != fgroup->_instances.end(); ++fi) {
       CPPInstance *function = (*fi);
       CPPInstance *function = (*fi);
       CPPFunctionType *ftype = function->_type->as_function_type();
       CPPFunctionType *ftype = function->_type->as_function_type();
-      if (ftype != NULL && ftype->_parameters->_parameters.size() == (size_t)is_seq) {
+      if (ftype != NULL && ftype->_parameters->_parameters.size() == num_args) {
         deleter = function;
         deleter = function;
         break;
         break;
       }
       }
     }
     }
 
 
-    if (deleter == NULL || return_type == NULL) {
+    if (deleter == nullptr) {
       cerr << "No instance of delete-function '"
       cerr << "No instance of delete-function '"
            << fgroup->_name << "' is suitable!\n";
            << fgroup->_name << "' is suitable!\n";
       return 0;
       return 0;
     }
     }
   }
   }
 
 
-  InterrogateDatabase *idb = InterrogateDatabase::get_ptr();
-  // It isn't here, so we'll have to define it.
-  ElementIndex index = idb->get_next_index();
-  _properties_by_name[property_name] = index;
+  if (index == 0) {
+    // It isn't here, so we'll have to define it.
+    index = idb->get_next_index();
+    _properties_by_name[property_name] = index;
+
+    InterrogateElement iproperty;
+    iproperty._name = make_property->get_simple_name();
+    iproperty._scoped_name = descope(make_property->get_local_name(&parser));
+    idb->add_element(index, iproperty);
+  }
 
 
-  InterrogateElement iproperty;
-  iproperty._name = make_property->get_simple_name();
-  iproperty._scoped_name = descope(make_property->get_local_name(&parser));
+  InterrogateElement &iproperty = idb->update_element(index);
 
 
   if (return_type != NULL) {
   if (return_type != NULL) {
-    iproperty._type = get_type(TypeManager::unwrap_reference(return_type), false);
+    TypeIndex return_index = get_type(TypeManager::unwrap_reference(return_type), false);
+    if (iproperty._type != 0 && iproperty._type != return_index) {
+      cerr << "Property " << property_name << " has inconsistent element type!\n";
+    }
   } else {
   } else {
     iproperty._type = 0;
     iproperty._type = 0;
   }
   }
 
 
-  if (length_function != 0) {
+  if (make_property->_type & CPPMakeProperty::T_sequence) {
     iproperty._flags |= InterrogateElement::F_sequence;
     iproperty._flags |= InterrogateElement::F_sequence;
     iproperty._length_function = length_function;
     iproperty._length_function = length_function;
   }
   }
 
 
-  if (getter != NULL) {
-    iproperty._flags |= InterrogateElement::F_has_getter;
-    iproperty._getter = get_function(getter, "", struct_type,
-                                     struct_type->get_scope(), 0);
-    nassertr(iproperty._getter, 0);
+  if (make_property->_type & CPPMakeProperty::T_mapping) {
+    iproperty._flags |= InterrogateElement::F_mapping;
+  }
+
+  if (make_property->_type == CPPMakeProperty::T_normal) {
+    if (getter != NULL) {
+      iproperty._flags |= InterrogateElement::F_has_getter;
+      iproperty._getter = get_function(getter, "", struct_type,
+                                      struct_type->get_scope(), 0);
+      nassertr(iproperty._getter, 0);
+    }
+  } else {
+    // We could have a mixed sequence/mapping property, so synthesize a
+    // getitem function.  We don't really care what's in here; we just use
+    // this to store the remaps.
+    if (!iproperty.has_getter()) {
+      iproperty._flags |= InterrogateElement::F_has_getter;
+      iproperty._getter = InterrogateDatabase::get_ptr()->get_next_index();
+      InterrogateFunction *ifunction = new InterrogateFunction;
+      ifunction->_instances = new InterrogateFunction::Instances;
+      InterrogateDatabase::get_ptr()->add_function(iproperty._getter, ifunction);
+    }
+
+    // Add our getter to the generated getitem function.
+    string signature = TypeManager::get_function_signature(getter);
+    InterrogateFunction &ifunction =
+      InterrogateDatabase::get_ptr()->update_function(iproperty._getter);
+    if (ifunction._instances == nullptr) {
+      ifunction._instances = new InterrogateFunction::Instances;
+    }
+    ifunction._instances->insert(InterrogateFunction::Instances::value_type(signature, getter));
   }
   }
 
 
   if (hasser != NULL) {
   if (hasser != NULL) {
@@ -2011,7 +2065,6 @@ get_make_property(CPPMakeProperty *make_property, CPPStructType *struct_type, CP
     }
     }
   }
   }
 
 
-  idb->add_element(index, iproperty);
   return index;
   return index;
 }
 }
 
 
@@ -2621,7 +2674,9 @@ define_struct_type(InterrogateType &itype, CPPStructType *cpptype,
 
 
     } else if ((*di)->get_subtype() == CPPDeclaration::ST_make_property) {
     } else if ((*di)->get_subtype() == CPPDeclaration::ST_make_property) {
       ElementIndex element_index = get_make_property((*di)->as_make_property(), cpptype, scope);
       ElementIndex element_index = get_make_property((*di)->as_make_property(), cpptype, scope);
-      itype._elements.push_back(element_index);
+      if (find(itype._elements.begin(), itype._elements.end(), element_index) == itype._elements.end()) {
+        itype._elements.push_back(element_index);
+      }
 
 
     } else if ((*di)->get_subtype() == CPPDeclaration::ST_make_seq) {
     } else if ((*di)->get_subtype() == CPPDeclaration::ST_make_seq) {
       MakeSeqIndex make_seq_index = get_make_seq((*di)->as_make_seq(), cpptype);
       MakeSeqIndex make_seq_index = get_make_seq((*di)->as_make_seq(), cpptype);

+ 10 - 0
dtool/src/interrogatedb/interrogateElement.I

@@ -26,6 +26,7 @@ InterrogateElement(InterrogateModuleDef *def) :
   _clear_function = 0;
   _clear_function = 0;
   _del_function = 0;
   _del_function = 0;
   _length_function = 0;
   _length_function = 0;
+  _make_property = nullptr;
 }
 }
 
 
 /**
 /**
@@ -52,6 +53,7 @@ operator = (const InterrogateElement &copy) {
   _clear_function = copy._clear_function;
   _clear_function = copy._clear_function;
   _del_function = copy._del_function;
   _del_function = copy._del_function;
   _length_function = copy._length_function;
   _length_function = copy._length_function;
+  _make_property = copy._make_property;
 }
 }
 
 
 /**
 /**
@@ -199,6 +201,14 @@ get_length_function() const {
   return _length_function;
   return _length_function;
 }
 }
 
 
+/**
+ *
+ */
+INLINE bool InterrogateElement::
+is_mapping() const {
+  return (_flags & F_mapping) != 0;
+}
+
 
 
 INLINE ostream &
 INLINE ostream &
 operator << (ostream &out, const InterrogateElement &element) {
 operator << (ostream &out, const InterrogateElement &element) {

+ 4 - 0
dtool/src/interrogatedb/interrogateElement.h

@@ -19,6 +19,7 @@
 #include "interrogateComponent.h"
 #include "interrogateComponent.h"
 
 
 class IndexRemapper;
 class IndexRemapper;
+class CPPMakeProperty;
 
 
 /**
 /**
  * An internal representation of a data element, like a data member or a
  * An internal representation of a data element, like a data member or a
@@ -51,6 +52,7 @@ public:
   INLINE FunctionIndex get_del_function() const;
   INLINE FunctionIndex get_del_function() const;
   INLINE bool is_sequence() const;
   INLINE bool is_sequence() const;
   INLINE FunctionIndex get_length_function() const;
   INLINE FunctionIndex get_length_function() const;
+  INLINE bool is_mapping() const;
 
 
   void output(ostream &out) const;
   void output(ostream &out) const;
   void input(istream &in);
   void input(istream &in);
@@ -80,6 +82,8 @@ private:
   FunctionIndex _clear_function;
   FunctionIndex _clear_function;
   FunctionIndex _del_function;
   FunctionIndex _del_function;
 
 
+  CPPMakeProperty *_make_property;
+
   friend class InterrogateBuilder;
   friend class InterrogateBuilder;
 };
 };
 
 

+ 205 - 13
dtool/src/interrogatedb/py_panda.cxx

@@ -614,6 +614,14 @@ PyObject *Dtool_PyModuleInitHelper(LibraryDef *defs[], const char *modulename) {
       return Dtool_Raise_TypeError("PyType_Ready(Dtool_SequenceWrapper)");
       return Dtool_Raise_TypeError("PyType_Ready(Dtool_SequenceWrapper)");
     }
     }
 
 
+    if (PyType_Ready(&Dtool_MappingWrapper_Type) < 0) {
+      return Dtool_Raise_TypeError("PyType_Ready(Dtool_MappingWrapper)");
+    }
+
+    if (PyType_Ready(&Dtool_SeqMapWrapper_Type) < 0) {
+      return Dtool_Raise_TypeError("PyType_Ready(Dtool_SeqMapWrapper)");
+    }
+
     if (PyType_Ready(&Dtool_StaticProperty_Type) < 0) {
     if (PyType_Ready(&Dtool_StaticProperty_Type) < 0) {
       return Dtool_Raise_TypeError("PyType_Ready(Dtool_StaticProperty_Type)");
       return Dtool_Raise_TypeError("PyType_Ready(Dtool_StaticProperty_Type)");
     }
     }
@@ -1028,34 +1036,79 @@ bool Dtool_ExtractOptionalArg(PyObject **result, PyObject *args, PyObject *kwds)
 }
 }
 
 
 /**
 /**
- * This class is returned from properties that require a settable interface,
- * ie. something.children[i] = 3.
+ * These classes are returned from properties that require a subscript
+ * interface, ie. something.children[i] = 3.
  */
  */
-static void Dtool_SequenceWrapper_dealloc(PyObject *self) {
-  Dtool_SequenceWrapper *wrap = (Dtool_SequenceWrapper *)self;
+static void Dtool_WrapperBase_dealloc(PyObject *self) {
+  Dtool_WrapperBase *wrap = (Dtool_WrapperBase *)self;
   nassertv(wrap);
   nassertv(wrap);
-  Py_XDECREF(wrap->_base);
+  Py_XDECREF(wrap->_self);
 }
 }
 
 
 static Py_ssize_t Dtool_SequenceWrapper_length(PyObject *self) {
 static Py_ssize_t Dtool_SequenceWrapper_length(PyObject *self) {
   Dtool_SequenceWrapper *wrap = (Dtool_SequenceWrapper *)self;
   Dtool_SequenceWrapper *wrap = (Dtool_SequenceWrapper *)self;
   nassertr(wrap, -1);
   nassertr(wrap, -1);
-  nassertr(wrap->_len_func, -1);
-  return wrap->_len_func(wrap->_base);
+  if (wrap->_len_func != nullptr) {
+    nassertr(wrap->_len_func, -1);
+    return wrap->_len_func(wrap->_base._self);
+  } else {
+    Dtool_Raise_TypeError("property does not support len()");
+    return -1;
+  }
 }
 }
 
 
 static PyObject *Dtool_SequenceWrapper_getitem(PyObject *self, Py_ssize_t index) {
 static PyObject *Dtool_SequenceWrapper_getitem(PyObject *self, Py_ssize_t index) {
   Dtool_SequenceWrapper *wrap = (Dtool_SequenceWrapper *)self;
   Dtool_SequenceWrapper *wrap = (Dtool_SequenceWrapper *)self;
-  nassertr(wrap, NULL);
-  nassertr(wrap->_getitem_func, NULL);
-  return wrap->_getitem_func(wrap->_base, index);
+  nassertr(wrap, nullptr);
+  nassertr(wrap->_getitem_func, nullptr);
+  return wrap->_getitem_func(wrap->_base._self, index);
 }
 }
 
 
 static int Dtool_SequenceWrapper_setitem(PyObject *self, Py_ssize_t index, PyObject *value) {
 static int Dtool_SequenceWrapper_setitem(PyObject *self, Py_ssize_t index, PyObject *value) {
   Dtool_SequenceWrapper *wrap = (Dtool_SequenceWrapper *)self;
   Dtool_SequenceWrapper *wrap = (Dtool_SequenceWrapper *)self;
   nassertr(wrap, -1);
   nassertr(wrap, -1);
-  nassertr(wrap->_setitem_func, -1);
-  return wrap->_setitem_func(wrap->_base, index, value);
+  if (wrap->_setitem_func != nullptr) {
+    return wrap->_setitem_func(wrap->_base._self, index, value);
+  } else {
+    Dtool_Raise_TypeError("property does not support item assignment");
+    return -1;
+  }
+}
+
+static PyObject *Dtool_MappingWrapper_getitem(PyObject *self, PyObject *key) {
+  Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)self;
+  nassertr(wrap, nullptr);
+  nassertr(wrap->_getitem_func, nullptr);
+  return wrap->_getitem_func(wrap->_base._self, key);
+}
+
+static int Dtool_MappingWrapper_setitem(PyObject *self, PyObject *key, PyObject *value) {
+  Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)self;
+  nassertr(wrap, -1);
+  if (wrap->_setitem_func != nullptr) {
+    return wrap->_setitem_func(wrap->_base._self, key, value);
+  } else {
+    Dtool_Raise_TypeError("property does not support item assignment");
+    return -1;
+  }
+}
+
+static PyObject *Dtool_SeqMapWrapper_getitem(PyObject *self, PyObject *key) {
+  Dtool_SeqMapWrapper *wrap = (Dtool_SeqMapWrapper *)self;
+  nassertr(wrap, nullptr);
+  nassertr(wrap->_map_getitem_func, nullptr);
+  return wrap->_map_getitem_func(wrap->_seq._base._self, key);
+}
+
+static int Dtool_SeqMapWrapper_setitem(PyObject *self, PyObject *key, PyObject *value) {
+  Dtool_SeqMapWrapper *wrap = (Dtool_SeqMapWrapper *)self;
+  nassertr(wrap, -1);
+  if (wrap->_map_setitem_func != nullptr) {
+    return wrap->_map_setitem_func(wrap->_seq._base._self, key, value);
+  } else {
+    Dtool_Raise_TypeError("property does not support item assignment");
+    return -1;
+  }
 }
 }
 
 
 static PySequenceMethods Dtool_SequenceWrapper_SequenceMethods = {
 static PySequenceMethods Dtool_SequenceWrapper_SequenceMethods = {
@@ -1071,12 +1124,27 @@ static PySequenceMethods Dtool_SequenceWrapper_SequenceMethods = {
   0, // sq_inplace_repeat
   0, // sq_inplace_repeat
 };
 };
 
 
+static PyMappingMethods Dtool_MappingWrapper_MappingMethods = {
+  0, // mp_length
+  Dtool_MappingWrapper_getitem,
+  Dtool_MappingWrapper_setitem,
+};
+
+static PyMappingMethods Dtool_SeqMapWrapper_MappingMethods = {
+  Dtool_SequenceWrapper_length,
+  Dtool_SeqMapWrapper_getitem,
+  Dtool_SeqMapWrapper_setitem,
+};
+
+/**
+ * This variant defines only a sequence interface.
+ */
 PyTypeObject Dtool_SequenceWrapper_Type = {
 PyTypeObject Dtool_SequenceWrapper_Type = {
   PyVarObject_HEAD_INIT(NULL, 0)
   PyVarObject_HEAD_INIT(NULL, 0)
   "sequence wrapper",
   "sequence wrapper",
   sizeof(Dtool_SequenceWrapper),
   sizeof(Dtool_SequenceWrapper),
   0, // tp_itemsize
   0, // tp_itemsize
-  Dtool_SequenceWrapper_dealloc,
+  Dtool_WrapperBase_dealloc,
   0, // tp_print
   0, // tp_print
   0, // tp_getattr
   0, // tp_getattr
   0, // tp_setattr
   0, // tp_setattr
@@ -1262,4 +1330,128 @@ PyTypeObject Dtool_StaticProperty_Type = {
 #endif
 #endif
 };
 };
 
 
+/**
+ * This variant defines only a mapping interface.
+ */
+PyTypeObject Dtool_MappingWrapper_Type = {
+  PyVarObject_HEAD_INIT(NULL, 0)
+  "mapping wrapper",
+  sizeof(Dtool_MappingWrapper),
+  0, // tp_itemsize
+  Dtool_WrapperBase_dealloc,
+  0, // tp_print
+  0, // tp_getattr
+  0, // tp_setattr
+#if PY_MAJOR_VERSION >= 3
+  0, // tp_reserved
+#else
+  0, // tp_compare
+#endif
+  0, // tp_repr
+  0, // tp_as_number
+  0, // tp_as_sequence
+  &Dtool_MappingWrapper_MappingMethods,
+  0, // tp_hash
+  0, // tp_call
+  0, // tp_str
+  PyObject_GenericGetAttr,
+  PyObject_GenericSetAttr,
+  0, // tp_as_buffer
+  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES,
+  0, // tp_doc
+  0, // tp_traverse
+  0, // tp_clear
+  0, // tp_richcompare
+  0, // tp_weaklistoffset
+  0, // tp_iter
+  0, // tp_iternext
+  0, // tp_methods
+  0, // tp_members
+  0, // tp_getset
+  0, // tp_base
+  0, // tp_dict
+  0, // tp_descr_get
+  0, // tp_descr_set
+  0, // tp_dictoffset
+  0, // tp_init
+  PyType_GenericAlloc,
+  0, // tp_new
+  PyObject_Del,
+  0, // tp_is_gc
+  0, // tp_bases
+  0, // tp_mro
+  0, // tp_cache
+  0, // tp_subclasses
+  0, // tp_weaklist
+  0, // tp_del
+#if PY_VERSION_HEX >= 0x02060000
+  0, // tp_version_tag
+#endif
+#if PY_VERSION_HEX >= 0x03040000
+  0, // tp_finalize
+#endif
+};
+
+/**
+ * This variant defines both a sequence and mapping interface.
+ */
+PyTypeObject Dtool_SeqMapWrapper_Type = {
+  PyVarObject_HEAD_INIT(NULL, 0)
+  "sequence/mapping wrapper",
+  sizeof(Dtool_SeqMapWrapper),
+  0, // tp_itemsize
+  Dtool_WrapperBase_dealloc,
+  0, // tp_print
+  0, // tp_getattr
+  0, // tp_setattr
+#if PY_MAJOR_VERSION >= 3
+  0, // tp_reserved
+#else
+  0, // tp_compare
+#endif
+  0, // tp_repr
+  0, // tp_as_number
+  &Dtool_SequenceWrapper_SequenceMethods,
+  &Dtool_SeqMapWrapper_MappingMethods,
+  0, // tp_hash
+  0, // tp_call
+  0, // tp_str
+  PyObject_GenericGetAttr,
+  PyObject_GenericSetAttr,
+  0, // tp_as_buffer
+  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES,
+  0, // tp_doc
+  0, // tp_traverse
+  0, // tp_clear
+  0, // tp_richcompare
+  0, // tp_weaklistoffset
+  0, // tp_iter
+  0, // tp_iternext
+  0, // tp_methods
+  0, // tp_members
+  0, // tp_getset
+  0, // tp_base
+  0, // tp_dict
+  0, // tp_descr_get
+  0, // tp_descr_set
+  0, // tp_dictoffset
+  0, // tp_init
+  PyType_GenericAlloc,
+  0, // tp_new
+  PyObject_Del,
+  0, // tp_is_gc
+  0, // tp_bases
+  0, // tp_mro
+  0, // tp_cache
+  0, // tp_subclasses
+  0, // tp_weaklist
+  0, // tp_del
+#if PY_VERSION_HEX >= 0x02060000
+  0, // tp_version_tag
+#endif
+#if PY_VERSION_HEX >= 0x03040000
+  0, // tp_finalize
+#endif
+};
+
 #endif  // HAVE_PYTHON
 #endif  // HAVE_PYTHON

+ 25 - 4
dtool/src/interrogatedb/py_panda.h

@@ -339,6 +339,9 @@ EXPCL_INTERROGATEDB PyObject *_Dtool_Raise_BadArgumentsError();
 #define Dtool_Raise_BadArgumentsError(x) Dtool_Raise_TypeError("Arguments must match:\n" x)
 #define Dtool_Raise_BadArgumentsError(x) Dtool_Raise_TypeError("Arguments must match:\n" x)
 #endif
 #endif
 
 
+// These functions are similar to Dtool_WrapValue, except that they also
+// contain code for checking assertions and exceptions when compiling with
+// NDEBUG mode on.
 EXPCL_INTERROGATEDB PyObject *_Dtool_Return_None();
 EXPCL_INTERROGATEDB PyObject *_Dtool_Return_None();
 EXPCL_INTERROGATEDB PyObject *Dtool_Return_Bool(bool value);
 EXPCL_INTERROGATEDB PyObject *Dtool_Return_Bool(bool value);
 EXPCL_INTERROGATEDB PyObject *_Dtool_Return(PyObject *value);
 EXPCL_INTERROGATEDB PyObject *_Dtool_Return(PyObject *value);
@@ -465,18 +468,36 @@ EXPCL_INTERROGATEDB PyObject *
 map_deepcopy_to_copy(PyObject *self, PyObject *args);
 map_deepcopy_to_copy(PyObject *self, PyObject *args);
 
 
 /**
 /**
- * This class is returned from properties that require a settable interface,
- * ie. something.children[i] = 3.
+ * These classes are returned from properties that require a subscript
+ * interface, ie. something.children[i] = 3.
  */
  */
+struct Dtool_WrapperBase {
+  PyObject_HEAD;
+  PyObject *_self;
+};
+
 struct Dtool_SequenceWrapper {
 struct Dtool_SequenceWrapper {
-  PyObject_HEAD
-  PyObject *_base;
+  Dtool_WrapperBase _base;
   lenfunc _len_func;
   lenfunc _len_func;
   ssizeargfunc _getitem_func;
   ssizeargfunc _getitem_func;
   ssizeobjargproc _setitem_func;
   ssizeobjargproc _setitem_func;
 };
 };
 
 
+struct Dtool_MappingWrapper {
+  Dtool_WrapperBase _base;
+  binaryfunc _getitem_func;
+  objobjargproc _setitem_func;
+};
+
+struct Dtool_SeqMapWrapper {
+  Dtool_SequenceWrapper _seq;
+  binaryfunc _map_getitem_func;
+  objobjargproc _map_setitem_func;
+};
+
 EXPCL_INTERROGATEDB extern PyTypeObject Dtool_SequenceWrapper_Type;
 EXPCL_INTERROGATEDB extern PyTypeObject Dtool_SequenceWrapper_Type;
+EXPCL_INTERROGATEDB extern PyTypeObject Dtool_MappingWrapper_Type;
+EXPCL_INTERROGATEDB extern PyTypeObject Dtool_SeqMapWrapper_Type;
 EXPCL_INTERROGATEDB extern PyTypeObject Dtool_StaticProperty_Type;
 EXPCL_INTERROGATEDB extern PyTypeObject Dtool_StaticProperty_Type;
 
 
 EXPCL_INTERROGATEDB PyObject *Dtool_NewStaticProperty(PyTypeObject *obj, const PyGetSetDef *getset);
 EXPCL_INTERROGATEDB PyObject *Dtool_NewStaticProperty(PyTypeObject *obj, const PyGetSetDef *getset);

+ 3 - 2
panda/src/chan/animGroup.h

@@ -44,12 +44,13 @@ PUBLISHED:
   AnimGroup *get_child(int n) const;
   AnimGroup *get_child(int n) const;
   MAKE_SEQ(get_children, get_num_children, get_child);
   MAKE_SEQ(get_children, get_num_children, get_child);
 
 
-  MAKE_SEQ_PROPERTY(children, get_num_children, get_child);
-
   AnimGroup *get_child_named(const string &name) const;
   AnimGroup *get_child_named(const string &name) const;
   AnimGroup *find_child(const string &name) const;
   AnimGroup *find_child(const string &name) const;
   void sort_descendants();
   void sort_descendants();
 
 
+  MAKE_SEQ_PROPERTY(children, get_num_children, get_child);
+  MAKE_MAP_PROPERTY(children, get_child_named, get_child_named);
+
 public:
 public:
   virtual TypeHandle get_value_type() const;
   virtual TypeHandle get_value_type() const;
 
 

+ 3 - 1
panda/src/chan/partGroup.h

@@ -70,12 +70,14 @@ PUBLISHED:
   int get_num_children() const;
   int get_num_children() const;
   PartGroup *get_child(int n) const;
   PartGroup *get_child(int n) const;
   MAKE_SEQ(get_children, get_num_children, get_child);
   MAKE_SEQ(get_children, get_num_children, get_child);
-  MAKE_SEQ_PROPERTY(children, get_num_children, get_child);
 
 
   PartGroup *get_child_named(const string &name) const;
   PartGroup *get_child_named(const string &name) const;
   PartGroup *find_child(const string &name) const;
   PartGroup *find_child(const string &name) const;
   void sort_descendants();
   void sort_descendants();
 
 
+  MAKE_SEQ_PROPERTY(children, get_num_children, get_child);
+  MAKE_MAP_PROPERTY(children, get_child_named, get_child_named);
+
   bool apply_freeze(const TransformState *transform);
   bool apply_freeze(const TransformState *transform);
   virtual bool apply_freeze_matrix(const LVecBase3 &pos, const LVecBase3 &hpr, const LVecBase3 &scale);
   virtual bool apply_freeze_matrix(const LVecBase3 &pos, const LVecBase3 &hpr, const LVecBase3 &scale);
   virtual bool apply_freeze_scalar(PN_stdfloat value);
   virtual bool apply_freeze_scalar(PN_stdfloat value);

+ 3 - 0
panda/src/gobj/geomVertexFormat.h

@@ -125,6 +125,9 @@ PUBLISHED:
   MAKE_SEQ_PROPERTY(points, get_num_points, get_point);
   MAKE_SEQ_PROPERTY(points, get_num_points, get_point);
   MAKE_SEQ_PROPERTY(vectors, get_num_vectors, get_vector);
   MAKE_SEQ_PROPERTY(vectors, get_num_vectors, get_vector);
 
 
+  // We also define this as a mapping interface, for lookups by name.
+  MAKE_MAP_PROPERTY(columns, has_column, get_column);
+
   void output(ostream &out) const;
   void output(ostream &out) const;
   void write(ostream &out, int indent_level = 0) const;
   void write(ostream &out, int indent_level = 0) const;
   void write_with_data(ostream &out, int indent_level,
   void write_with_data(ostream &out, int indent_level,

+ 2 - 0
panda/src/gobj/texture.h

@@ -539,6 +539,8 @@ PUBLISHED:
   void set_aux_data(const string &key, TypedReferenceCount *aux_data);
   void set_aux_data(const string &key, TypedReferenceCount *aux_data);
   void clear_aux_data(const string &key);
   void clear_aux_data(const string &key);
   TypedReferenceCount *get_aux_data(const string &key) const;
   TypedReferenceCount *get_aux_data(const string &key) const;
+  MAKE_MAP_PROPERTY(aux_data, get_aux_data, get_aux_data,
+                    set_aux_data, clear_aux_data);
 
 
   INLINE static void set_textures_power_2(AutoTextureScale scale);
   INLINE static void set_textures_power_2(AutoTextureScale scale);
   INLINE static AutoTextureScale get_textures_power_2();
   INLINE static AutoTextureScale get_textures_power_2();

+ 11 - 0
panda/src/gobj/transformBlend.I

@@ -142,6 +142,17 @@ get_weight(size_t n) const {
   return _entries[n]._weight;
   return _entries[n]._weight;
 }
 }
 
 
+/**
+ * Removes the nth transform stored in the blend object.
+ */
+INLINE void TransformBlend::
+remove_transform(size_t n) {
+  nassertv(n < _entries.size());
+  _entries.erase(_entries.begin() + n);
+  Thread *current_thread = Thread::get_current_thread();
+  clear_result(current_thread);
+}
+
 /**
 /**
  * Replaces the nth transform stored in the blend object.
  * Replaces the nth transform stored in the blend object.
  */
  */

+ 6 - 0
panda/src/gobj/transformBlend.h

@@ -62,9 +62,15 @@ PUBLISHED:
   INLINE const VertexTransform *get_transform(size_t n) const;
   INLINE const VertexTransform *get_transform(size_t n) const;
   MAKE_SEQ(get_transforms, get_num_transforms, get_transform);
   MAKE_SEQ(get_transforms, get_num_transforms, get_transform);
   INLINE PN_stdfloat get_weight(size_t n) const;
   INLINE PN_stdfloat get_weight(size_t n) const;
+  INLINE void remove_transform(size_t n);
   INLINE void set_transform(size_t n, const VertexTransform *transform);
   INLINE void set_transform(size_t n, const VertexTransform *transform);
   INLINE void set_weight(size_t n, PN_stdfloat weight);
   INLINE void set_weight(size_t n, PN_stdfloat weight);
 
 
+  MAKE_SEQ_PROPERTY(transforms, get_num_transforms, get_transform,
+                    set_transform, remove_transform);
+  MAKE_SEQ_PROPERTY(weights, get_num_transforms, get_weight, set_weight);
+  MAKE_MAP_PROPERTY(weights, has_transform, get_weight);
+
   INLINE void update_blend(Thread *current_thread) const;
   INLINE void update_blend(Thread *current_thread) const;
 
 
   INLINE void get_blend(LMatrix4 &result, Thread *current_thread) const;
   INLINE void get_blend(LMatrix4 &result, Thread *current_thread) const;

+ 4 - 0
panda/src/pgraph/camera.h

@@ -91,12 +91,16 @@ PUBLISHED:
   void clear_tag_states();
   void clear_tag_states();
   bool has_tag_state(const string &tag_state) const;
   bool has_tag_state(const string &tag_state) const;
   CPT(RenderState) get_tag_state(const string &tag_state) const;
   CPT(RenderState) get_tag_state(const string &tag_state) const;
+  MAKE_MAP_PROPERTY(tag_states, has_tag_state, get_tag_state,
+                    set_tag_state, clear_tag_state);
 
 
   void set_aux_scene_data(const NodePath &node_path, AuxSceneData *data);
   void set_aux_scene_data(const NodePath &node_path, AuxSceneData *data);
   bool clear_aux_scene_data(const NodePath &node_path);
   bool clear_aux_scene_data(const NodePath &node_path);
   AuxSceneData *get_aux_scene_data(const NodePath &node_path) const;
   AuxSceneData *get_aux_scene_data(const NodePath &node_path) const;
   void list_aux_scene_data(ostream &out) const;
   void list_aux_scene_data(ostream &out) const;
   int cleanup_aux_scene_data(Thread *current_thread = Thread::get_current_thread());
   int cleanup_aux_scene_data(Thread *current_thread = Thread::get_current_thread());
+  MAKE_MAP_PROPERTY(aux_scene_data, get_aux_scene_data, get_aux_scene_data,
+                    set_aux_scene_data, clear_aux_scene_data);
 
 
 private:
 private:
   void add_display_region(DisplayRegion *display_region);
   void add_display_region(DisplayRegion *display_region);

+ 3 - 0
panda/src/pgraph/nodePath.h

@@ -913,6 +913,9 @@ PUBLISHED:
   INLINE bool has_net_tag(const string &key) const;
   INLINE bool has_net_tag(const string &key) const;
   NodePath find_net_tag(const string &key) const;
   NodePath find_net_tag(const string &key) const;
 
 
+  MAKE_MAP_PROPERTY(tags, has_tag, get_tag, set_tag, clear_tag);
+  MAKE_MAP_PROPERTY(net_tags, has_net_tag, get_net_tag);
+
   EXTENSION(INLINE PyObject *get_tag_keys() const);
   EXTENSION(INLINE PyObject *get_tag_keys() const);
 
 
   EXTENSION(PyObject *get_python_tags());
   EXTENSION(PyObject *get_python_tags());

+ 2 - 0
panda/src/pgraph/renderState.h

@@ -109,6 +109,8 @@ PUBLISHED:
   INLINE int get_override(TypeHandle type) const;
   INLINE int get_override(TypeHandle type) const;
   INLINE int get_override(int slot) const;
   INLINE int get_override(int slot) const;
 
 
+  MAKE_MAP_PROPERTY(attribs, has_attrib, get_attrib);
+
   INLINE CPT(RenderState) get_unique() const;
   INLINE CPT(RenderState) get_unique() const;
 
 
   virtual bool unref() const;
   virtual bool unref() const;

+ 8 - 0
panda/src/pgraph/shaderInput.I

@@ -417,6 +417,14 @@ ShaderInput(CPT_InternalName name, const LVecBase2i &vec, int priority) :
 {
 {
 }
 }
 
 
+/**
+ *
+ */
+INLINE ShaderInput::
+operator bool () const {
+  return _type != M_invalid;
+}
+
 /**
 /**
  *
  *
  */
  */

+ 1 - 0
panda/src/pgraph/shaderInput.h

@@ -104,6 +104,7 @@ PUBLISHED:
     M_buffer,
     M_buffer,
   };
   };
 
 
+  INLINE operator bool() const;
   INLINE bool operator == (const ShaderInput &other) const;
   INLINE bool operator == (const ShaderInput &other) const;
   INLINE bool operator != (const ShaderInput &other) const;
   INLINE bool operator != (const ShaderInput &other) const;
   INLINE bool operator < (const ShaderInput &other) const;
   INLINE bool operator < (const ShaderInput &other) const;

+ 6 - 0
panda/src/pgraph/textureAttrib.h

@@ -64,12 +64,18 @@ PUBLISHED:
 
 
   int find_on_stage(const TextureStage *stage) const;
   int find_on_stage(const TextureStage *stage) const;
 
 
+  MAKE_SEQ_PROPERTY(on_stages, get_num_on_stages, get_on_stage);
+  MAKE_MAP_PROPERTY(on_stages, find_on_stage, get_on_stage);
+
   INLINE int get_num_off_stages() const;
   INLINE int get_num_off_stages() const;
   INLINE TextureStage *get_off_stage(int n) const;
   INLINE TextureStage *get_off_stage(int n) const;
   MAKE_SEQ(get_off_stages, get_num_off_stages, get_off_stage);
   MAKE_SEQ(get_off_stages, get_num_off_stages, get_off_stage);
   INLINE bool has_off_stage(TextureStage *stage) const;
   INLINE bool has_off_stage(TextureStage *stage) const;
   INLINE bool has_all_off() const;
   INLINE bool has_all_off() const;
 
 
+  MAKE_SEQ_PROPERTY(off_stages, get_num_off_stages, get_off_stage);
+  MAKE_MAP_PROPERTY(off_stages, has_off_stage, get_off_stage);
+
   INLINE bool is_identity() const;
   INLINE bool is_identity() const;
 
 
   CPT(RenderAttrib) add_on_stage(TextureStage *stage, Texture *tex, int override = 0) const;
   CPT(RenderAttrib) add_on_stage(TextureStage *stage, Texture *tex, int override = 0) const;

Some files were not shown because too many files changed in this diff