瀏覽代碼

Merge branch 'master' into vulkan

rdb 8 年之前
父節點
當前提交
9feb68f0aa
共有 100 個文件被更改,包括 4883 次插入1777 次删除
  1. 10 1
      .gitignore
  2. 18 3
      .travis.yml
  3. 34 1
      README.md
  4. 1 1
      contrib/src/ai/aiCharacter.h
  5. 1 1
      contrib/src/ai/aiNode.h
  6. 1 1
      contrib/src/ai/flock.h
  7. 0 10
      direct/metalibs/direct/direct.cxx
  8. 95 1
      direct/src/actor/Actor.py
  9. 13 0
      direct/src/controls/InputState.py
  10. 1 1
      direct/src/dcparser/dcPacker.cxx
  11. 2 2
      direct/src/distributed/cConnectionRepository.h
  12. 21 0
      direct/src/filter/CommonFilters.py
  13. 12 0
      direct/src/filter/FilterManager.py
  14. 3 2
      direct/src/filter/filter-bloomx.sha
  15. 3 2
      direct/src/filter/filter-bloomy.sha
  16. 6 4
      direct/src/filter/filter-blurx.sha
  17. 6 4
      direct/src/filter/filter-blury.sha
  18. 1 1
      direct/src/interval/ActorInterval.py
  19. 3 1
      direct/src/interval/Interval.py
  20. 3 3
      direct/src/interval/cConstrainHprInterval.h
  21. 4 4
      direct/src/interval/cConstrainPosHprInterval.h
  22. 3 3
      direct/src/interval/cConstrainPosInterval.h
  23. 3 3
      direct/src/interval/cConstrainTransformInterval.h
  24. 2 2
      direct/src/interval/cLerpAnimEffectInterval.h
  25. 4 4
      direct/src/interval/cLerpNodePathInterval.h
  26. 1 1
      direct/src/interval/cMetaInterval.h
  27. 1 1
      direct/src/interval/hideInterval.h
  28. 1 1
      direct/src/interval/showInterval.h
  29. 1 1
      direct/src/interval/waitInterval.h
  30. 24 0
      direct/src/showbase/Audio3DManager.py
  31. 12 0
      direct/src/showbase/DirectObject.py
  32. 128 53
      direct/src/showbase/Loader.py
  33. 24 1
      direct/src/showbase/Messenger.py
  34. 42 4
      direct/src/showbase/MirrorDemo.py
  35. 53 45
      direct/src/showbase/ShowBase.py
  36. 2 2
      direct/src/showbase/Transitions.py
  37. 35 11
      direct/src/stdpy/thread.py
  38. 33 11
      direct/src/stdpy/threading.py
  39. 43 26
      direct/src/stdpy/threading2.py
  40. 13 1
      direct/src/task/Task.py
  41. 536 524
      dtool/src/cppparser/cppBison.cxx.prebuilt
  42. 92 90
      dtool/src/cppparser/cppBison.h.prebuilt
  43. 252 95
      dtool/src/cppparser/cppBison.yxx
  44. 8 0
      dtool/src/cppparser/cppExtensionType.cxx
  45. 1 0
      dtool/src/cppparser/cppExtensionType.h
  46. 2 0
      dtool/src/cppparser/cppFunctionType.h
  47. 19 8
      dtool/src/cppparser/cppInstance.cxx
  48. 14 28
      dtool/src/cppparser/cppMakeProperty.cxx
  49. 65 8
      dtool/src/cppparser/cppMakeProperty.h
  50. 8 0
      dtool/src/cppparser/cppPointerType.cxx
  51. 1 0
      dtool/src/cppparser/cppPointerType.h
  52. 1 0
      dtool/src/cppparser/cppPreprocessor.cxx
  53. 8 0
      dtool/src/cppparser/cppSimpleType.cxx
  54. 1 0
      dtool/src/cppparser/cppSimpleType.h
  55. 176 0
      dtool/src/cppparser/cppStructType.cxx
  56. 7 1
      dtool/src/cppparser/cppStructType.h
  57. 8 0
      dtool/src/cppparser/cppType.cxx
  58. 1 0
      dtool/src/cppparser/cppType.h
  59. 8 0
      dtool/src/cppparser/cppTypedefType.cxx
  60. 1 0
      dtool/src/cppparser/cppTypedefType.h
  61. 13 1
      dtool/src/dtoolbase/dtoolbase.h
  62. 13 16
      dtool/src/dtoolbase/dtoolbase_cc.h
  63. 14 17
      dtool/src/dtoolbase/memoryHook.cxx
  64. 1 3
      dtool/src/dtoolbase/memoryHook.h
  65. 0 2
      dtool/src/dtoolbase/typeRegistryNode.cxx
  66. 0 2
      dtool/src/dtoolbase/typeRegistryNode.h
  67. 16 0
      dtool/src/dtoolutil/executionEnvironment.cxx
  68. 9 0
      dtool/src/dtoolutil/executionEnvironment.h
  69. 11 4
      dtool/src/dtoolutil/filename.cxx
  70. 1 1
      dtool/src/dtoolutil/filename_ext.cxx
  71. 5 5
      dtool/src/dtoolutil/load_dso.cxx
  72. 3 3
      dtool/src/dtoolutil/pandaFileStream.h
  73. 15 0
      dtool/src/dtoolutil/pandaSystem.h
  74. 1 0
      dtool/src/dtoolutil/textEncoder.h
  75. 28 0
      dtool/src/interrogate/README.md
  76. 17 11
      dtool/src/interrogate/functionRemap.cxx
  77. 10 9
      dtool/src/interrogate/interfaceMaker.cxx
  78. 5 2
      dtool/src/interrogate/interfaceMaker.h
  79. 1 1
      dtool/src/interrogate/interfaceMakerPython.cxx
  80. 458 275
      dtool/src/interrogate/interfaceMakerPythonNative.cxx
  81. 4 0
      dtool/src/interrogate/interfaceMakerPythonNative.h
  82. 169 37
      dtool/src/interrogate/interrogateBuilder.cxx
  83. 0 52
      dtool/src/interrogate/typeManager.cxx
  84. 0 1
      dtool/src/interrogate/typeManager.h
  85. 1 1
      dtool/src/interrogatedb/interrogateDatabase.cxx
  86. 46 0
      dtool/src/interrogatedb/interrogateElement.I
  87. 8 1
      dtool/src/interrogatedb/interrogateElement.cxx
  88. 12 0
      dtool/src/interrogatedb/interrogateElement.h
  89. 8 0
      dtool/src/interrogatedb/interrogateType.I
  90. 2 0
      dtool/src/interrogatedb/interrogateType.h
  91. 2 4
      dtool/src/interrogatedb/p3interrogatedb_composite2.cxx
  92. 54 0
      dtool/src/interrogatedb/py_compat.cxx
  93. 175 0
      dtool/src/interrogatedb/py_compat.h
  94. 42 8
      dtool/src/interrogatedb/py_panda.I
  95. 73 207
      dtool/src/interrogatedb/py_panda.cxx
  96. 31 146
      dtool/src/interrogatedb/py_panda.h
  97. 1675 0
      dtool/src/interrogatedb/py_wrappers.cxx
  98. 78 0
      dtool/src/interrogatedb/py_wrappers.h
  99. 9 0
      dtool/src/parser-inc/iostream
  100. 1 1
      dtool/src/prc/configVariable.h

+ 10 - 1
.gitignore

@@ -8,6 +8,7 @@
 core
 core
 core.*
 core.*
 vgcore.*
 vgcore.*
+*.core
 
 
 # Editor files/directories
 # Editor files/directories
 *.save
 *.save
@@ -22,6 +23,9 @@ vgcore.*
 *.o
 *.o
 *.gch
 *.gch
 *.pch
 *.pch
+/+DESC
+/+MANIFEST
+/pkg-plist
 
 
 # Produced installer/executables
 # Produced installer/executables
 /*.exe
 /*.exe
@@ -31,6 +35,7 @@ vgcore.*
 /*.pkg
 /*.pkg
 /*.dmg
 /*.dmg
 /*.whl
 /*.whl
+/*.txz
 
 
 # CMake
 # CMake
 /build/
 /build/
@@ -47,6 +52,10 @@ Thumbs.db
 ehthumbs.db
 ehthumbs.db
 
 
 # Python
 # Python
-__pycache__
+__pycache__/
 *.pyc
 *.pyc
 *.pyo
 *.pyo
+
+# Test tool cache directories
+.tox/
+.cache/

+ 18 - 3
.travis.yml

@@ -2,15 +2,22 @@ language: cpp
 sudo: false
 sudo: false
 matrix:
 matrix:
   include:
   include:
-    - compiler: gcc
-      env: PYTHONV=python2.7 FLAGS=--optimize=4
     - compiler: clang
     - compiler: clang
       env: PYTHONV=python3 FLAGS=--installer
       env: PYTHONV=python3 FLAGS=--installer
     - compiler: clang
     - compiler: clang
       env: PYTHONV=python2.7 FLAGS=--override=STDFLOAT_DOUBLE=1
       env: PYTHONV=python2.7 FLAGS=--override=STDFLOAT_DOUBLE=1
+    - compiler: gcc
+      env: PYTHONV=python2.7 FLAGS=--optimize=4
+      before_install:
+        - export CC=gcc-4.7
+        - export CXX=g++-4.7
 addons:
 addons:
   apt:
   apt:
+    sources:
+    - ubuntu-toolchain-r-test
     packages:
     packages:
+    - gcc-4.7
+    - g++-4.7
     - bison
     - bison
     - flex
     - flex
     - libfreetype6-dev
     - libfreetype6-dev
@@ -27,8 +34,16 @@ addons:
     - nvidia-cg-toolkit
     - nvidia-cg-toolkit
     - python-dev
     - python-dev
     - python3-dev
     - python3-dev
+    - python-virtualenv
     - zlib1g-dev
     - zlib1g-dev
-script: $PYTHONV makepanda/makepanda.py --everything --git-commit $TRAVIS_COMMIT $FLAGS --threads 4 && LD_LIBRARY_PATH=built/lib PYTHONPATH=built $PYTHONV makepanda/test_imports.py
+    - fakeroot
+install:
+    - virtualenv --python=$PYTHONV venv && source venv/bin/activate
+    - $PYTHONV -m pip install pytest
+script:
+    - $PYTHONV makepanda/makepanda.py --everything --git-commit $TRAVIS_COMMIT $FLAGS --threads 4
+    - LD_LIBRARY_PATH=built/lib PYTHONPATH=built $PYTHONV makepanda/test_imports.py
+    - LD_LIBRARY_PATH=built/lib PYTHONPATH=built $PYTHONV -m pytest tests
 notifications:
 notifications:
   irc:
   irc:
     channels:
     channels:

+ 34 - 1
README.md

@@ -134,13 +134,46 @@ If the build was successful, makepanda will have generated a .dmg file in
 the source directory containing the installer.  Simply open it and run the
 the source directory containing the installer.  Simply open it and run the
 package file in order to install the SDK onto your system.
 package file in order to install the SDK onto your system.
 
 
+FreeBSD
+-------
+
+Building on FreeBSD is very similar to building on Linux.  You will need to
+install the requisite packages using the system package manager.  To install
+the recommended set of dependencies, you can use this command:
+
+```bash
+pkg install pkgconf png jpeg-turbo tiff freetype2 eigen squish openal opusfile libvorbis libX11 libGL ode bullet assimp openexr
+```
+
+You will also need to choose which version of Python you want to use.
+Install the appropriate package for it (such as `python2` or `python36`) and
+run the makepanda script with your chosen Python version:
+
+```bash
+python3.6 makepanda/makepanda.py --everything --installer --no-egl --no-gles --no-gles2
+```
+
+If successful, this will produce a .pkg file in the root of the source
+directory which you can install using `pkg install`.
+
+Running Tests
+=============
+
+Install [PyTest](https://docs.pytest.org/en/latest/getting-started.html#installation)
+and run the `pytest` command.  If you have not installed Panda3D, you will
+need to configure your enviroment by pointing the `PYTHONPATH` variable at
+the `built` directory.  On Linux, you will also need to point the
+`LD_LIBRARY_PATH` variable at the `built/lib` directory.
+
+As a convenience, you can alternatively pass the `--tests` option to makepanda.
+
 Reporting Issues
 Reporting Issues
 ================
 ================
 
 
 If you encounter any bugs when using Panda3D, please report them in the bug
 If you encounter any bugs when using Panda3D, please report them in the bug
 tracker.  This is hosted at:
 tracker.  This is hosted at:
 
 
-  https://bugs.launchpad.net/panda3d
+  https://github.com/panda3d/panda3d/issues
 
 
 Make sure to first use the search function to see if the bug has already been
 Make sure to first use the search function to see if the bug has already been
 reported.  When filling out a bug report, make sure that you include as much
 reported.  When filling out a bug report, make sure that you include as much

+ 1 - 1
contrib/src/ai/aiCharacter.h

@@ -62,7 +62,7 @@ PUBLISHED:
     // This function is used to enable or disable the guides for path finding.
     // This function is used to enable or disable the guides for path finding.
     void set_pf_guide(bool pf_guide);
     void set_pf_guide(bool pf_guide);
 
 
-    AICharacter(string model_name, NodePath model_np, double mass, double movt_force, double max_force);
+    explicit AICharacter(string model_name, NodePath model_np, double mass, double movt_force, double max_force);
     ~AICharacter();
     ~AICharacter();
 };
 };
 
 

+ 1 - 1
contrib/src/ai/aiNode.h

@@ -66,7 +66,7 @@ public:
   AINode *_next;
   AINode *_next;
 
 
 PUBLISHED:
 PUBLISHED:
-  AINode(int grid_x, int grid_y, LVecBase3 pos, float w, float l, float h);
+  explicit AINode(int grid_x, int grid_y, LVecBase3 pos, float w, float l, float h);
   ~AINode();
   ~AINode();
 
 
   bool contains(float x, float y);
   bool contains(float x, float y);

+ 1 - 1
contrib/src/ai/flock.h

@@ -44,7 +44,7 @@ public:
   AICharList _ai_char_list;
   AICharList _ai_char_list;
 
 
 PUBLISHED:
 PUBLISHED:
-  Flock(unsigned int flock_id, double vcone_angle, double vcone_radius, unsigned int separation_wt = 2,
+  explicit Flock(unsigned int flock_id, double vcone_angle, double vcone_radius, unsigned int separation_wt = 2,
     unsigned int cohesion_wt = 4, unsigned int alignment_wt = 1);
     unsigned int cohesion_wt = 4, unsigned int alignment_wt = 1);
   ~Flock();
   ~Flock();
 
 

+ 0 - 10
direct/metalibs/direct/direct.cxx

@@ -1,10 +0,0 @@
-/**
- * @file direct.cxx
- * @author drose
- * @date 2000-05-18
- */
-
-// This is a dummy file whose sole purpose is to give the compiler something
-// to compile when making libdirect.so in NO_DEFER mode, which generates an
-// empty library that itself links with all the other shared libraries that
-// make up libdirect.

+ 95 - 1
direct/src/actor/Actor.py

@@ -50,6 +50,10 @@ class Actor(DirectObject, NodePath):
         def __repr__(self):
         def __repr__(self):
             return 'Actor.PartDef(%s, %s)' % (repr(self.partBundleNP), repr(self.partModel))
             return 'Actor.PartDef(%s, %s)' % (repr(self.partBundleNP), repr(self.partModel))
 
 
+
+        #snake_case alias:
+        get_bundle = getBundle
+
     class AnimDef:
     class AnimDef:
 
 
         """Instances of this class are stored within the
         """Instances of this class are stored within the
@@ -72,6 +76,10 @@ class Actor(DirectObject, NodePath):
         def __repr__(self):
         def __repr__(self):
             return 'Actor.AnimDef(%s)' % (repr(self.filename))
             return 'Actor.AnimDef(%s)' % (repr(self.filename))
 
 
+
+        #snake_case alias:
+        make_copy = makeCopy
+
     class SubpartDef:
     class SubpartDef:
 
 
         """Instances of this class are stored within the SubpartDict
         """Instances of this class are stored within the SubpartDict
@@ -889,7 +897,7 @@ class Actor(DirectObject, NodePath):
         return ((toFrame+1)-fromFrame) / animControl.getFrameRate()
         return ((toFrame+1)-fromFrame) / animControl.getFrameRate()
 
 
     def getNumFrames(self, animName=None, partName=None):
     def getNumFrames(self, animName=None, partName=None):
-        lodName = next(iter(self.__animControlDict))
+        #lodName = next(iter(self.__animControlDict))
         controls = self.getAnimControls(animName, partName)
         controls = self.getAnimControls(animName, partName)
         if len(controls) == 0:
         if len(controls) == 0:
             return None
             return None
@@ -2549,3 +2557,89 @@ class Actor(DirectObject, NodePath):
         for partBundleDict in self.__partBundleDict.values():
         for partBundleDict in self.__partBundleDict.values():
             partDef = partBundleDict.get(subpartDef.truePartName)
             partDef = partBundleDict.get(subpartDef.truePartName)
             partDef.getBundle().setName(newBundleName)
             partDef.getBundle().setName(newBundleName)
+
+    #snake_case alias:
+    control_joint = controlJoint
+    set_lod_animation = setLODAnimation
+    get_anim_control_dict = getAnimControlDict
+    get_actor_info = getActorInfo
+    clear_lod_animation = clearLODAnimation
+    reset_lod = resetLOD
+    fix_bounds = fixBounds
+    get_anim_filename = getAnimFilename
+    get_subparts_complete = getSubpartsComplete
+    verify_subparts_complete = verifySubpartsComplete
+    get_play_rate = getPlayRate
+    clear_python_data = clearPythonData
+    load_anims = loadAnims
+    set_subparts_complete = setSubpartsComplete
+    draw_in_front = drawInFront
+    get_lod_node = getLODNode
+    hide_part = hidePart
+    get_joint_transform_state = getJointTransformState
+    set_control_effect = setControlEffect
+    get_anim_controls = getAnimControls
+    release_joint = releaseJoint
+    print_anim_blends = printAnimBlends
+    get_lod = getLOD
+    disable_blend = disableBlend
+    show_part = showPart
+    get_joint_transform = getJointTransform
+    face_away_from_viewer = faceAwayFromViewer
+    set_lod = setLOD
+    osd_anim_blends = osdAnimBlends
+    get_current_frame = getCurrentFrame
+    set_play_rate = setPlayRate
+    bind_all_anims = bindAllAnims
+    unload_anims = unloadAnims
+    remove_part = removePart
+    use_lod = useLOD
+    get_anim_blends = getAnimBlends
+    get_lod_index = getLODIndex
+    get_num_frames = getNumFrames
+    post_flatten = postFlatten
+    get_lod_names = getLODNames
+    list_joints = listJoints
+    make_subpart = makeSubpart
+    get_anim_control = getAnimControl
+    get_part_bundle = getPartBundle
+    get_part_bundle_dict = getPartBundleDict
+    get_duration = getDuration
+    has_lod = hasLOD
+    print_lod = printLOD
+    fix_bounds_old = fixBounds_old
+    get_anim_names = getAnimNames
+    get_part_bundles = getPartBundles
+    anim_panel = animPanel
+    stop_joint = stopJoint
+    actor_interval = actorInterval
+    hide_all_bounds = hideAllBounds
+    show_all_bounds = showAllBounds
+    init_anims_on_all_lods = initAnimsOnAllLODs
+    get_part = getPart
+    add_lod = addLOD
+    show_all_parts = showAllParts
+    get_joints = getJoints
+    get_overlapping_joints = getOverlappingJoints
+    enable_blend = enableBlend
+    face_towards_viewer = faceTowardsViewer
+    bind_anim = bindAnim
+    set_blend = setBlend
+    get_frame_time = getFrameTime
+    remove_node = removeNode
+    wait_pending = waitPending
+    expose_joint = exposeJoint
+    set_lod_node = setLODNode
+    get_frame_rate = getFrameRate
+    get_current_anim = getCurrentAnim
+    get_part_names = getPartNames
+    freeze_joint = freezeJoint
+    set_center = setCenter
+    rename_part_bundles = renamePartBundles
+    get_geom_node = getGeomNode
+    set_geom_node = setGeomNode
+    load_model = loadModel
+    copy_actor = copyActor
+    get_base_frame_rate = getBaseFrameRate
+    remove_anim_control_dict = removeAnimControlDict
+    load_anims_on_all_lods = loadAnimsOnAllLODs

+ 13 - 0
direct/src/controls/InputState.py

@@ -21,6 +21,9 @@ class InputStateToken:
     def __hash__(self):
     def __hash__(self):
         return self._hash
         return self._hash
 
 
+    #snake_case alias:
+    is_valid = isValid
+
 class InputStateWatchToken(InputStateToken, DirectObject.DirectObject):
 class InputStateWatchToken(InputStateToken, DirectObject.DirectObject):
     def release(self):
     def release(self):
         self._inputState._ignore(self)
         self._inputState._ignore(self)
@@ -39,6 +42,9 @@ class InputStateTokenGroup:
             token.release()
             token.release()
         self._tokens = []
         self._tokens = []
 
 
+    #snake_case alias:
+    add_token = addToken
+
 class InputState(DirectObject.DirectObject):
 class InputState(DirectObject.DirectObject):
     """
     """
     InputState is for tracking the on/off state of some events.
     InputState is for tracking the on/off state of some events.
@@ -235,3 +241,10 @@ class InputState(DirectObject.DirectObject):
         """for debugging"""
         """for debugging"""
         return self.notify.debug(
         return self.notify.debug(
             "%s (%s) %s"%(id(self), len(self._state), message))
             "%s (%s) %s"%(id(self), len(self._state), message))
+
+    #snake_case alias:
+    watch_with_modifiers = watchWithModifiers
+    is_set = isSet
+    get_event_name = getEventName
+    debug_print = debugPrint
+    release_inputs = releaseInputs

+ 1 - 1
direct/src/dcparser/dcPacker.cxx

@@ -708,7 +708,7 @@ pack_object(PyObject *object) {
     pack_int64(PyLong_AsLongLong(object));
     pack_int64(PyLong_AsLongLong(object));
 #if PY_MAJOR_VERSION >= 3
 #if PY_MAJOR_VERSION >= 3
   } else if (PyUnicode_Check(object)) {
   } else if (PyUnicode_Check(object)) {
-    char *buffer;
+    const char *buffer;
     Py_ssize_t length;
     Py_ssize_t length;
     buffer = PyUnicode_AsUTF8AndSize(object, &length);
     buffer = PyUnicode_AsUTF8AndSize(object, &length);
     if (buffer) {
     if (buffer) {

+ 2 - 2
direct/src/distributed/cConnectionRepository.h

@@ -55,8 +55,8 @@ class SocketStream;
  */
  */
 class EXPCL_DIRECT CConnectionRepository {
 class EXPCL_DIRECT CConnectionRepository {
 PUBLISHED:
 PUBLISHED:
-  CConnectionRepository(bool has_owner_view = false,
-                        bool threaded_net = false);
+  explicit CConnectionRepository(bool has_owner_view = false,
+                                 bool threaded_net = false);
   ~CConnectionRepository();
   ~CConnectionRepository();
 
 
 /*
 /*

+ 21 - 0
direct/src/filter/CommonFilters.py

@@ -536,3 +536,24 @@ class CommonFilters:
             del self.configuration["GammaAdjust"]
             del self.configuration["GammaAdjust"]
             return self.reconfigure((old_gamma != 1.0), "GammaAdjust")
             return self.reconfigure((old_gamma != 1.0), "GammaAdjust")
         return True
         return True
+
+    #snake_case alias:
+    del_cartoon_ink = delCartoonInk
+    set_half_pixel_shift = setHalfPixelShift
+    del_half_pixel_shift = delHalfPixelShift
+    set_inverted = setInverted
+    del_inverted = delInverted
+    del_view_glow = delViewGlow
+    set_volumetric_lighting = setVolumetricLighting
+    del_gamma_adjust = delGammaAdjust
+    set_bloom = setBloom
+    set_view_glow = setViewGlow
+    set_ambient_occlusion = setAmbientOcclusion
+    set_cartoon_ink = setCartoonInk
+    del_bloom = delBloom
+    del_ambient_occlusion = delAmbientOcclusion
+    load_shader = loadShader
+    set_blur_sharpen = setBlurSharpen
+    del_blur_sharpen = delBlurSharpen
+    del_volumetric_lighting = delVolumetricLighting
+    set_gamma_adjust = setGammaAdjust

+ 12 - 0
direct/src/filter/FilterManager.py

@@ -349,3 +349,15 @@ class FilterManager(DirectObject):
         self.nextsort = self.win.getSort() - 1000
         self.nextsort = self.win.getSort() - 1000
         self.basex = 0
         self.basex = 0
         self.basey = 0
         self.basey = 0
+
+    #snake_case alias:
+    is_fullscreen = isFullscreen
+    resize_buffers = resizeBuffers
+    set_stacked_clears = setStackedClears
+    render_scene_into = renderSceneInto
+    get_scaled_size = getScaledSize
+    render_quad_into = renderQuadInto
+    get_clears = getClears
+    set_clears = setClears
+    create_buffer = createBuffer
+    window_event = windowEvent

+ 3 - 2
direct/src/filter/filter-bloomx.sha

@@ -12,8 +12,9 @@ void vshader(float4 vtx_position : POSITION,
   l_position=mul(mat_modelproj, vtx_position);
   l_position=mul(mat_modelproj, vtx_position);
   float2 c=(vtx_position.xz * texpad_src.xy) + texpad_src.xy;
   float2 c=(vtx_position.xz * texpad_src.xy) + texpad_src.xy;
   float offset = texpix_src.x;
   float offset = texpix_src.x;
-  l_texcoord0 = float4(c.x-offset* -4, c.x-offset* -3, c.x-offset* -2, c.y);
-  l_texcoord1 = float4(c.x-offset* -1, c.x-offset*  0, c.x-offset*  1, c.y);
+  float pad = texpad_src.x * 2;
+  l_texcoord0 = float4(min(c.x-offset* -4, pad), min(c.x-offset* -3, pad), min(c.x-offset* -2, pad), c.y);
+  l_texcoord1 = float4(min(c.x-offset* -1, pad), c.x-offset*  0, c.x-offset*  1, c.y);
   l_texcoord2 = float4(c.x-offset*  2, c.x-offset*  3, c.x-offset*  4, c.y);
   l_texcoord2 = float4(c.x-offset*  2, c.x-offset*  3, c.x-offset*  4, c.y);
 }
 }
 
 

+ 3 - 2
direct/src/filter/filter-bloomy.sha

@@ -12,8 +12,9 @@ void vshader(float4 vtx_position : POSITION,
   l_position=mul(mat_modelproj, vtx_position);
   l_position=mul(mat_modelproj, vtx_position);
   float2 c=(vtx_position.xz * texpad_src.xy) + texpad_src.xy;
   float2 c=(vtx_position.xz * texpad_src.xy) + texpad_src.xy;
   float offset = texpix_src.y;
   float offset = texpix_src.y;
-  l_texcoord0 = float4(c.y-offset* -4, c.y-offset* -3, c.y-offset* -2, c.x);
-  l_texcoord1 = float4(c.y-offset* -1, c.y-offset*  0, c.y-offset*  1, c.x);
+  float pad = texpad_src.y * 2;
+  l_texcoord0 = float4(min(c.y-offset* -4, pad), min(c.y-offset* -3, pad), min(c.y-offset* -2, pad), c.x);
+  l_texcoord1 = float4(min(c.y-offset* -1, pad), c.y-offset*  0, c.y-offset*  1, c.x);
   l_texcoord2 = float4(c.y-offset*  2, c.y-offset*  3, c.y-offset*  4, c.x);
   l_texcoord2 = float4(c.y-offset*  2, c.y-offset*  3, c.y-offset*  4, c.x);
 }
 }
 
 

+ 6 - 4
direct/src/filter/filter-blurx.sha

@@ -2,7 +2,7 @@
 //
 //
 //Cg profile arbvp1 arbfp1
 //Cg profile arbvp1 arbfp1
 
 
-void vshader(float4 vtx_position : POSITION, 
+void vshader(float4 vtx_position : POSITION,
              float2 vtx_texcoord0 : TEXCOORD0,
              float2 vtx_texcoord0 : TEXCOORD0,
              out float4 l_position : POSITION,
              out float4 l_position : POSITION,
       	     out float2 l_texcoord0 : TEXCOORD0,
       	     out float2 l_texcoord0 : TEXCOORD0,
@@ -17,16 +17,18 @@ void vshader(float4 vtx_position : POSITION,
 void fshader(float2 l_texcoord0 : TEXCOORD0,
 void fshader(float2 l_texcoord0 : TEXCOORD0,
              out float4 o_color : COLOR,
              out float4 o_color : COLOR,
              uniform float2 texpix_src,
              uniform float2 texpix_src,
+             uniform float4 texpad_src,
              uniform sampler2D k_src : TEXUNIT0)
              uniform sampler2D k_src : TEXUNIT0)
 {
 {
+  float pad = texpad_src.x * 2;
   float3 offset = float3(1.0*texpix_src.x, 2.0*texpix_src.x, 3.0*texpix_src.x);
   float3 offset = float3(1.0*texpix_src.x, 2.0*texpix_src.x, 3.0*texpix_src.x);
   o_color  = tex2D(k_src, l_texcoord0);
   o_color  = tex2D(k_src, l_texcoord0);
   o_color += tex2D(k_src, float2(l_texcoord0.x - offset.z, l_texcoord0.y));
   o_color += tex2D(k_src, float2(l_texcoord0.x - offset.z, l_texcoord0.y));
   o_color += tex2D(k_src, float2(l_texcoord0.x - offset.y, l_texcoord0.y));
   o_color += tex2D(k_src, float2(l_texcoord0.x - offset.y, l_texcoord0.y));
   o_color += tex2D(k_src, float2(l_texcoord0.x - offset.x, l_texcoord0.y));
   o_color += tex2D(k_src, float2(l_texcoord0.x - offset.x, l_texcoord0.y));
-  o_color += tex2D(k_src, float2(l_texcoord0.x + offset.x, l_texcoord0.y));
-  o_color += tex2D(k_src, float2(l_texcoord0.x + offset.y, l_texcoord0.y));
-  o_color += tex2D(k_src, float2(l_texcoord0.x + offset.z, l_texcoord0.y));
+  o_color += tex2D(k_src, float2(min(l_texcoord0.x + offset.x, pad), l_texcoord0.y));
+  o_color += tex2D(k_src, float2(min(l_texcoord0.x + offset.y, pad), l_texcoord0.y));
+  o_color += tex2D(k_src, float2(min(l_texcoord0.x + offset.z, pad), l_texcoord0.y));
   o_color /= 7;
   o_color /= 7;
   o_color.w = 1;
   o_color.w = 1;
 }
 }

+ 6 - 4
direct/src/filter/filter-blury.sha

@@ -2,7 +2,7 @@
 //
 //
 //Cg profile arbvp1 arbfp1
 //Cg profile arbvp1 arbfp1
 
 
-void vshader(float4 vtx_position : POSITION, 
+void vshader(float4 vtx_position : POSITION,
              float2 vtx_texcoord0 : TEXCOORD0,
              float2 vtx_texcoord0 : TEXCOORD0,
              out float4 l_position : POSITION,
              out float4 l_position : POSITION,
       	     out float2 l_texcoord0 : TEXCOORD0,
       	     out float2 l_texcoord0 : TEXCOORD0,
@@ -17,16 +17,18 @@ void vshader(float4 vtx_position : POSITION,
 void fshader(float2 l_texcoord0 : TEXCOORD0,
 void fshader(float2 l_texcoord0 : TEXCOORD0,
              out float4 o_color : COLOR,
              out float4 o_color : COLOR,
              uniform float2 texpix_src,
              uniform float2 texpix_src,
+             uniform float4 texpad_src,
              uniform sampler2D k_src : TEXUNIT0)
              uniform sampler2D k_src : TEXUNIT0)
 {
 {
+  float pad = texpad_src.y * 2;
   float3 offset = float3(1.0*texpix_src.y, 2.0*texpix_src.y, 3.0*texpix_src.y);
   float3 offset = float3(1.0*texpix_src.y, 2.0*texpix_src.y, 3.0*texpix_src.y);
   o_color  = tex2D(k_src, l_texcoord0);
   o_color  = tex2D(k_src, l_texcoord0);
   o_color += tex2D(k_src, float2(l_texcoord0.x, l_texcoord0.y - offset.z));
   o_color += tex2D(k_src, float2(l_texcoord0.x, l_texcoord0.y - offset.z));
   o_color += tex2D(k_src, float2(l_texcoord0.x, l_texcoord0.y - offset.y));
   o_color += tex2D(k_src, float2(l_texcoord0.x, l_texcoord0.y - offset.y));
   o_color += tex2D(k_src, float2(l_texcoord0.x, l_texcoord0.y - offset.x));
   o_color += tex2D(k_src, float2(l_texcoord0.x, l_texcoord0.y - offset.x));
-  o_color += tex2D(k_src, float2(l_texcoord0.x, l_texcoord0.y + offset.x));
-  o_color += tex2D(k_src, float2(l_texcoord0.x, l_texcoord0.y + offset.y));
-  o_color += tex2D(k_src, float2(l_texcoord0.x, l_texcoord0.y + offset.z));
+  o_color += tex2D(k_src, float2(l_texcoord0.x, min(l_texcoord0.y + offset.x, pad)));
+  o_color += tex2D(k_src, float2(l_texcoord0.x, min(l_texcoord0.y + offset.y, pad)));
+  o_color += tex2D(k_src, float2(l_texcoord0.x, min(l_texcoord0.y + offset.z, pad)));
   o_color /= 7;
   o_color /= 7;
   o_color.w = 1;
   o_color.w = 1;
 }
 }

+ 1 - 1
direct/src/interval/ActorInterval.py

@@ -83,7 +83,7 @@ class ActorInterval(Interval.Interval):
                 if startTime == None:
                 if startTime == None:
                     startTime = float(self.startFrame) / float(self.frameRate)
                     startTime = float(self.startFrame) / float(self.frameRate)
                 endTime = startTime + duration
                 endTime = startTime + duration
-                self.endFrame = duration * self.frameRate
+                self.endFrame = endTime * self.frameRate
             else:
             else:
                 # No end frame specified.  Choose the maximum of all
                 # No end frame specified.  Choose the maximum of all
                 # of the controls' numbers of frames.
                 # of the controls' numbers of frames.

+ 3 - 1
direct/src/interval/Interval.py

@@ -116,8 +116,9 @@ class Interval(DirectObject):
         return self.currT
         return self.currT
 
 
     def start(self, startT = 0.0, endT = -1.0, playRate = 1.0):
     def start(self, startT = 0.0, endT = -1.0, playRate = 1.0):
+        """ Starts the interval.  Returns an awaitable. """
         self.setupPlay(startT, endT, playRate, 0)
         self.setupPlay(startT, endT, playRate, 0)
-        self.__spawnTask()
+        return self.__spawnTask()
 
 
     def loop(self, startT = 0.0, endT = -1.0, playRate = 1.0):
     def loop(self, startT = 0.0, endT = -1.0, playRate = 1.0):
         self.setupPlay(startT, endT, playRate, 1)
         self.setupPlay(startT, endT, playRate, 1)
@@ -427,6 +428,7 @@ class Interval(DirectObject):
         task = Task(self.__playTask)
         task = Task(self.__playTask)
         task.interval = self
         task.interval = self
         taskMgr.add(task, taskName)
         taskMgr.add(task, taskName)
+        return task
 
 
     def __removeTask(self):
     def __removeTask(self):
         # Kill old task(s), including those from a similarly-named but
         # Kill old task(s), including those from a similarly-named but

+ 3 - 3
direct/src/interval/cConstrainHprInterval.h

@@ -26,9 +26,9 @@
  */
  */
 class EXPCL_DIRECT CConstrainHprInterval : public CConstraintInterval {
 class EXPCL_DIRECT CConstrainHprInterval : public CConstraintInterval {
 PUBLISHED:
 PUBLISHED:
-  CConstrainHprInterval(const string &name, double duration,
-                        const NodePath &node, const NodePath &target,
-                        bool wrt, const LVecBase3 hprOffset=LVector3::zero());
+  explicit CConstrainHprInterval(const string &name, double duration,
+                                 const NodePath &node, const NodePath &target,
+                                 bool wrt, const LVecBase3 hprOffset=LVector3::zero());
 
 
   INLINE const NodePath &get_node() const;
   INLINE const NodePath &get_node() const;
   INLINE const NodePath &get_target() const;
   INLINE const NodePath &get_target() const;

+ 4 - 4
direct/src/interval/cConstrainPosHprInterval.h

@@ -26,10 +26,10 @@
  */
  */
 class EXPCL_DIRECT CConstrainPosHprInterval : public CConstraintInterval {
 class EXPCL_DIRECT CConstrainPosHprInterval : public CConstraintInterval {
 PUBLISHED:
 PUBLISHED:
-  CConstrainPosHprInterval(const string &name, double duration,
-                           const NodePath &node, const NodePath &target,
-                           bool wrt, const LVecBase3 posOffset=LVector3::zero(),
-                           const LVecBase3 hprOffset=LVector3::zero());
+  explicit CConstrainPosHprInterval(const string &name, double duration,
+                                    const NodePath &node, const NodePath &target,
+                                    bool wrt, const LVecBase3 posOffset=LVector3::zero(),
+                                    const LVecBase3 hprOffset=LVector3::zero());
 
 
   INLINE const NodePath &get_node() const;
   INLINE const NodePath &get_node() const;
   INLINE const NodePath &get_target() const;
   INLINE const NodePath &get_target() const;

+ 3 - 3
direct/src/interval/cConstrainPosInterval.h

@@ -25,9 +25,9 @@
  */
  */
 class EXPCL_DIRECT CConstrainPosInterval : public CConstraintInterval {
 class EXPCL_DIRECT CConstrainPosInterval : public CConstraintInterval {
 PUBLISHED:
 PUBLISHED:
-  CConstrainPosInterval(const string &name, double duration,
-                        const NodePath &node, const NodePath &target,
-                        bool wrt, const LVecBase3 posOffset=LVector3::zero());
+  explicit CConstrainPosInterval(const string &name, double duration,
+                                 const NodePath &node, const NodePath &target,
+                                 bool wrt, const LVecBase3 posOffset=LVector3::zero());
 
 
   INLINE const NodePath &get_node() const;
   INLINE const NodePath &get_node() const;
   INLINE const NodePath &get_target() const;
   INLINE const NodePath &get_target() const;

+ 3 - 3
direct/src/interval/cConstrainTransformInterval.h

@@ -24,9 +24,9 @@
  */
  */
 class EXPCL_DIRECT CConstrainTransformInterval : public CConstraintInterval {
 class EXPCL_DIRECT CConstrainTransformInterval : public CConstraintInterval {
 PUBLISHED:
 PUBLISHED:
-  CConstrainTransformInterval(const string &name, double duration,
-                              const NodePath &node, const NodePath &target,
-                              bool wrt);
+  explicit CConstrainTransformInterval(const string &name, double duration,
+                                       const NodePath &node,
+                                       const NodePath &target, bool wrt);
 
 
   INLINE const NodePath &get_node() const;
   INLINE const NodePath &get_node() const;
   INLINE const NodePath &get_target() const;
   INLINE const NodePath &get_target() const;

+ 2 - 2
direct/src/interval/cLerpAnimEffectInterval.h

@@ -31,8 +31,8 @@
  */
  */
 class EXPCL_DIRECT CLerpAnimEffectInterval : public CLerpInterval {
 class EXPCL_DIRECT CLerpAnimEffectInterval : public CLerpInterval {
 PUBLISHED:
 PUBLISHED:
-  INLINE CLerpAnimEffectInterval(const string &name, double duration,
-                                 BlendType blend_type);
+  INLINE explicit CLerpAnimEffectInterval(const string &name, double duration,
+                                          BlendType blend_type);
 
 
   INLINE void add_control(AnimControl *control, const string &name,
   INLINE void add_control(AnimControl *control, const string &name,
                           float begin_effect, float end_effect);
                           float begin_effect, float end_effect);

+ 4 - 4
direct/src/interval/cLerpNodePathInterval.h

@@ -25,10 +25,10 @@
  */
  */
 class EXPCL_DIRECT CLerpNodePathInterval : public CLerpInterval {
 class EXPCL_DIRECT CLerpNodePathInterval : public CLerpInterval {
 PUBLISHED:
 PUBLISHED:
-  CLerpNodePathInterval(const string &name, double duration,
-                        BlendType blend_type, bool bake_in_start,
-                        bool fluid,
-                        const NodePath &node, const NodePath &other);
+  explicit CLerpNodePathInterval(const string &name, double duration,
+                                 BlendType blend_type, bool bake_in_start,
+                                 bool fluid,
+                                 const NodePath &node, const NodePath &other);
 
 
   INLINE const NodePath &get_node() const;
   INLINE const NodePath &get_node() const;
   INLINE const NodePath &get_other() const;
   INLINE const NodePath &get_other() const;

+ 1 - 1
direct/src/interval/cMetaInterval.h

@@ -31,7 +31,7 @@
  */
  */
 class EXPCL_DIRECT CMetaInterval : public CInterval {
 class EXPCL_DIRECT CMetaInterval : public CInterval {
 PUBLISHED:
 PUBLISHED:
-  CMetaInterval(const string &name);
+  explicit CMetaInterval(const string &name);
   virtual ~CMetaInterval();
   virtual ~CMetaInterval();
 
 
   enum RelativeStart {
   enum RelativeStart {

+ 1 - 1
direct/src/interval/hideInterval.h

@@ -23,7 +23,7 @@
  */
  */
 class EXPCL_DIRECT HideInterval : public CInterval {
 class EXPCL_DIRECT HideInterval : public CInterval {
 PUBLISHED:
 PUBLISHED:
-  HideInterval(const NodePath &node, const string &name = string());
+  explicit HideInterval(const NodePath &node, const string &name = string());
 
 
   virtual void priv_instant();
   virtual void priv_instant();
   virtual void priv_reverse_instant();
   virtual void priv_reverse_instant();

+ 1 - 1
direct/src/interval/showInterval.h

@@ -23,7 +23,7 @@
  */
  */
 class EXPCL_DIRECT ShowInterval : public CInterval {
 class EXPCL_DIRECT ShowInterval : public CInterval {
 PUBLISHED:
 PUBLISHED:
-  ShowInterval(const NodePath &node, const string &name = string());
+  explicit ShowInterval(const NodePath &node, const string &name = string());
 
 
   virtual void priv_instant();
   virtual void priv_instant();
   virtual void priv_reverse_instant();
   virtual void priv_reverse_instant();

+ 1 - 1
direct/src/interval/waitInterval.h

@@ -23,7 +23,7 @@
  */
  */
 class EXPCL_DIRECT WaitInterval : public CInterval {
 class EXPCL_DIRECT WaitInterval : public CInterval {
 PUBLISHED:
 PUBLISHED:
-  INLINE WaitInterval(double duration);
+  INLINE explicit WaitInterval(double duration);
 
 
   virtual void priv_step(double t);
   virtual void priv_step(double t);
 
 

+ 24 - 0
direct/src/showbase/Audio3DManager.py

@@ -289,3 +289,27 @@ class Audio3DManager:
             for sound in self.sound_dict[object]:
             for sound in self.sound_dict[object]:
                 self.detachSound(sound)
                 self.detachSound(sound)
 
 
+    #snake_case alias:
+    get_doppler_factor = getDopplerFactor
+    set_listener_velocity_auto = setListenerVelocityAuto
+    attach_listener = attachListener
+    set_distance_factor = setDistanceFactor
+    attach_sound_to_object = attachSoundToObject
+    get_drop_off_factor = getDropOffFactor
+    set_doppler_factor = setDopplerFactor
+    get_sounds_on_object = getSoundsOnObject
+    set_sound_velocity_auto = setSoundVelocityAuto
+    get_sound_max_distance = getSoundMaxDistance
+    load_sfx = loadSfx
+    get_distance_factor = getDistanceFactor
+    set_listener_velocity = setListenerVelocity
+    set_sound_max_distance = setSoundMaxDistance
+    get_sound_velocity = getSoundVelocity
+    get_listener_velocity = getListenerVelocity
+    set_sound_velocity = setSoundVelocity
+    set_sound_min_distance = setSoundMinDistance
+    get_sound_min_distance = getSoundMinDistance
+    detach_listener = detachListener
+    set_drop_off_factor = setDropOffFactor
+    detach_sound = detachSound
+

+ 12 - 0
direct/src/showbase/DirectObject.py

@@ -101,3 +101,15 @@ class DirectObject:
             func = choice(getRepository()._crashOnProactiveLeakDetect,
             func = choice(getRepository()._crashOnProactiveLeakDetect,
                           self.notify.error, self.notify.warning)
                           self.notify.error, self.notify.warning)
             func('destroyed %s instance is still %s%s' % (self.__class__.__name__, estr, tstr))
             func('destroyed %s instance is still %s%s' % (self.__class__.__name__, estr, tstr))
+
+    #snake_case alias:
+    add_task = addTask
+    do_method_later = doMethodLater
+    detect_leaks = detectLeaks
+    accept_once = acceptOnce
+    ignore_all = ignoreAll
+    get_all_accepting = getAllAccepting
+    is_ignoring = isIgnoring
+    remove_all_tasks = removeAllTasks
+    remove_task = removeTask
+    is_accepting = isAccepting

+ 128 - 53
direct/src/showbase/Loader.py

@@ -21,31 +21,105 @@ class Loader(DirectObject):
     loaderIndex = 0
     loaderIndex = 0
 
 
     class Callback:
     class Callback:
-        def __init__(self, numObjects, gotList, callback, extraArgs):
+        """Returned by loadModel when used asynchronously.  This class is
+        modelled after Future, and can be awaited."""
+
+        # This indicates that this class behaves like a Future.
+        _asyncio_future_blocking = False
+
+        def __init__(self, loader, numObjects, gotList, callback, extraArgs):
+            self._loader = loader
             self.objects = [None] * numObjects
             self.objects = [None] * numObjects
             self.gotList = gotList
             self.gotList = gotList
             self.callback = callback
             self.callback = callback
             self.extraArgs = extraArgs
             self.extraArgs = extraArgs
-            self.numRemaining = numObjects
-            self.cancelled = False
             self.requests = set()
             self.requests = set()
+            self.requestList = []
 
 
         def gotObject(self, index, object):
         def gotObject(self, index, object):
             self.objects[index] = object
             self.objects[index] = object
-            self.numRemaining -= 1
 
 
-            if self.numRemaining == 0:
-                if self.gotList:
-                    self.callback(self.objects, *self.extraArgs)
-                else:
-                    self.callback(*(self.objects + self.extraArgs))
+            if not self.requests:
+                self._loader = None
+                if self.callback:
+                    if self.gotList:
+                        self.callback(self.objects, *self.extraArgs)
+                    else:
+                        self.callback(*(self.objects + self.extraArgs))
+
+        def cancel(self):
+            "Cancels the request.  Callback won't be called."
+            if self._loader:
+                for request in self.requests:
+                    self._loader.loader.remove(request)
+                    del self._loader._requests[request]
+                self._loader = None
+                self.requests = None
+                self.requestList = None
+
+        def cancelled(self):
+            "Returns true if the request was cancelled."
+            return self.requestList is None
+
+        def done(self):
+            "Returns true if all the requests were finished or cancelled."
+            return not self.requests
+
+        def result(self):
+            "Returns the results, suspending the thread to wait if necessary."
+            for r in list(self.requests):
+                r.wait()
+            if self.gotList:
+                return self.objects
+            else:
+                return self.objects[0]
+
+        def exception(self):
+            assert self.done() and not self.cancelled()
+            return None
+
+        def __await__(self):
+            """ Returns a generator that raises StopIteration when the loading
+            is complete.  This allows this class to be used with 'await'."""
+            if self.requests:
+                self._asyncio_future_blocking = True
+                yield self
+
+            # This should be a simple return, but older versions of Python
+            # don't allow return statements with arguments.
+            result = self.result()
+            exc = StopIteration(result)
+            exc.value = result
+            raise exc
+
+        def __aiter__(self):
+            """ This allows using `async for` to iterate asynchronously over
+            the results of this class.  It does guarantee to return the
+            results in order, though, even though they may not be loaded in
+            that order. """
+            requestList = self.requestList
+            assert requestList is not None, "Request was cancelled."
+
+            class AsyncIter:
+                index = 0
+                def __anext__(self):
+                    if self.index < len(requestList):
+                        i = self.index
+                        self.index = i + 1
+                        return requestList[i]
+                    else:
+                        raise StopAsyncIteration
+
+            iter = AsyncIter()
+            iter.objects = self.objects
+            return iter
 
 
     # special methods
     # special methods
     def __init__(self, base):
     def __init__(self, base):
         self.base = base
         self.base = base
         self.loader = PandaLoader.getGlobalPtr()
         self.loader = PandaLoader.getGlobalPtr()
 
 
-        self.__requests = {}
+        self._requests = {}
 
 
         self.hook = "async_loader_%s" % (Loader.loaderIndex)
         self.hook = "async_loader_%s" % (Loader.loaderIndex)
         Loader.loaderIndex += 1
         Loader.loaderIndex += 1
@@ -60,7 +134,8 @@ class Loader(DirectObject):
     # model loading funcs
     # model loading funcs
     def loadModel(self, modelPath, loaderOptions = None, noCache = None,
     def loadModel(self, modelPath, loaderOptions = None, noCache = None,
                   allowInstance = False, okMissing = None,
                   allowInstance = False, okMissing = None,
-                  callback = None, extraArgs = [], priority = None):
+                  callback = None, extraArgs = [], priority = None,
+                  blocking = None):
         """
         """
         Attempts to load a model or models from one or more relative
         Attempts to load a model or models from one or more relative
         pathnames.  If the input modelPath is a string (a single model
         pathnames.  If the input modelPath is a string (a single model
@@ -97,10 +172,10 @@ class Loader(DirectObject):
         If callback is not None, then the model load will be performed
         If callback is not None, then the model load will be performed
         asynchronously.  In this case, loadModel() will initiate a
         asynchronously.  In this case, loadModel() will initiate a
         background load and return immediately.  The return value will
         background load and return immediately.  The return value will
-        be an object that may later be passed to
-        loader.cancelRequest() to cancel the asynchronous request.  At
-        some later point, when the requested model(s) have finished
-        loading, the callback function will be invoked with the n
+        be an object that can be used to check the status, cancel the
+        request, or use it in an `await` expression.  Unless callback
+        is the special value True, when the requested model(s) have
+        finished loading, it will be invoked with the n
         loaded models passed as its parameter list.  It is possible
         loaded models passed as its parameter list.  It is possible
         that the callback will be invoked immediately, even before
         that the callback will be invoked immediately, even before
         loadModel() returns.  If you use callback, you may also
         loadModel() returns.  If you use callback, you may also
@@ -152,7 +227,10 @@ class Loader(DirectObject):
             modelList = modelPath
             modelList = modelPath
             gotList = True
             gotList = True
 
 
-        if callback is None:
+        if blocking is None:
+            blocking = callback is None
+
+        if blocking:
             # We got no callback, so it's a synchronous load.
             # We got no callback, so it's a synchronous load.
 
 
             result = []
             result = []
@@ -180,7 +258,7 @@ class Loader(DirectObject):
             # requested models have been loaded, we'll invoke the
             # requested models have been loaded, we'll invoke the
             # callback (passing it the models on the parameter list).
             # callback (passing it the models on the parameter list).
 
 
-            cb = Loader.Callback(len(modelList), gotList, callback, extraArgs)
+            cb = Loader.Callback(self, len(modelList), gotList, callback, extraArgs)
             i = 0
             i = 0
             for modelPath in modelList:
             for modelPath in modelList:
                 request = self.loader.makeAsyncRequest(Filename(modelPath), loaderOptions)
                 request = self.loader.makeAsyncRequest(Filename(modelPath), loaderOptions)
@@ -189,26 +267,26 @@ class Loader(DirectObject):
                 request.setDoneEvent(self.hook)
                 request.setDoneEvent(self.hook)
                 self.loader.loadAsync(request)
                 self.loader.loadAsync(request)
                 cb.requests.add(request)
                 cb.requests.add(request)
-                self.__requests[request] = (cb, i)
+                cb.requestList.append(request)
+                self._requests[request] = (cb, i)
                 i += 1
                 i += 1
             return cb
             return cb
 
 
     def cancelRequest(self, cb):
     def cancelRequest(self, cb):
         """Cancels an aysynchronous loading or flatten request issued
         """Cancels an aysynchronous loading or flatten request issued
         earlier.  The callback associated with the request will not be
         earlier.  The callback associated with the request will not be
-        called after cancelRequest() has been performed. """
+        called after cancelRequest() has been performed.
+
+        This is now deprecated: call cb.cancel() instead. """
 
 
-        if not cb.cancelled:
-            cb.cancelled = True
-            for request in cb.requests:
-                self.loader.remove(request)
-                del self.__requests[request]
-            cb.requests = None
+        cb.cancel()
 
 
     def isRequestPending(self, cb):
     def isRequestPending(self, cb):
         """ Returns true if an asynchronous loading or flatten request
         """ Returns true if an asynchronous loading or flatten request
         issued earlier is still pending, or false if it has completed or
         issued earlier is still pending, or false if it has completed or
-        been cancelled. """
+        been cancelled.
+
+        This is now deprecated: call cb.done() instead. """
 
 
         return bool(cb.requests)
         return bool(cb.requests)
 
 
@@ -290,7 +368,8 @@ class Loader(DirectObject):
         ModelPool.releaseModel(modelNode)
         ModelPool.releaseModel(modelNode)
 
 
     def saveModel(self, modelPath, node, loaderOptions = None,
     def saveModel(self, modelPath, node, loaderOptions = None,
-                  callback = None, extraArgs = [], priority = None):
+                  callback = None, extraArgs = [], priority = None,
+                  blocking = None):
         """ Saves the model (a NodePath or PandaNode) to the indicated
         """ Saves the model (a NodePath or PandaNode) to the indicated
         filename path.  Returns true on success, false on failure.  If
         filename path.  Returns true on success, false on failure.  If
         a callback is used, the model is saved asynchronously, and the
         a callback is used, the model is saved asynchronously, and the
@@ -325,7 +404,10 @@ class Loader(DirectObject):
         # From here on, we deal with a list of (filename, node) pairs.
         # From here on, we deal with a list of (filename, node) pairs.
         modelList = list(zip(modelList, nodeList))
         modelList = list(zip(modelList, nodeList))
 
 
-        if callback is None:
+        if blocking is None:
+            blocking = callback is None
+
+        if blocking:
             # We got no callback, so it's a synchronous save.
             # We got no callback, so it's a synchronous save.
 
 
             result = []
             result = []
@@ -344,7 +426,7 @@ class Loader(DirectObject):
             # requested models have been saved, we'll invoke the
             # requested models have been saved, we'll invoke the
             # callback (passing it the models on the parameter list).
             # callback (passing it the models on the parameter list).
 
 
-            cb = Loader.Callback(len(modelList), gotList, callback, extraArgs)
+            cb = Loader.Callback(self, len(modelList), gotList, callback, extraArgs)
             i = 0
             i = 0
             for modelPath, node in modelList:
             for modelPath, node in modelList:
                 request = self.loader.makeAsyncSaveRequest(Filename(modelPath), loaderOptions, node)
                 request = self.loader.makeAsyncSaveRequest(Filename(modelPath), loaderOptions, node)
@@ -353,7 +435,8 @@ class Loader(DirectObject):
                 request.setDoneEvent(self.hook)
                 request.setDoneEvent(self.hook)
                 self.loader.saveAsync(request)
                 self.loader.saveAsync(request)
                 cb.requests.add(request)
                 cb.requests.add(request)
-                self.__requests[request] = (cb, i)
+                cb.requestList.append(request)
+                self._requests[request] = (cb, i)
                 i += 1
                 i += 1
             return cb
             return cb
 
 
@@ -880,13 +963,14 @@ class Loader(DirectObject):
             # requested sounds have been loaded, we'll invoke the
             # requested sounds have been loaded, we'll invoke the
             # callback (passing it the sounds on the parameter list).
             # callback (passing it the sounds on the parameter list).
 
 
-            cb = Loader.Callback(len(soundList), gotList, callback, extraArgs)
+            cb = Loader.Callback(self, len(soundList), gotList, callback, extraArgs)
             for i, soundPath in enumerate(soundList):
             for i, soundPath in enumerate(soundList):
                 request = AudioLoadRequest(manager, soundPath, positional)
                 request = AudioLoadRequest(manager, soundPath, positional)
                 request.setDoneEvent(self.hook)
                 request.setDoneEvent(self.hook)
                 self.loader.loadAsync(request)
                 self.loader.loadAsync(request)
                 cb.requests.add(request)
                 cb.requests.add(request)
-                self.__requests[request] = (cb, i)
+                cb.requestList.append(request)
+                self._requests[request] = (cb, i)
             return cb
             return cb
 
 
     def unloadSfx(self, sfx):
     def unloadSfx(self, sfx):
@@ -944,14 +1028,15 @@ class Loader(DirectObject):
             callback = self.__asyncFlattenDone
             callback = self.__asyncFlattenDone
             gotList = True
             gotList = True
 
 
-        cb = Loader.Callback(len(modelList), gotList, callback, extraArgs)
+        cb = Loader.Callback(self, len(modelList), gotList, callback, extraArgs)
         i = 0
         i = 0
         for model in modelList:
         for model in modelList:
             request = ModelFlattenRequest(model.node())
             request = ModelFlattenRequest(model.node())
             request.setDoneEvent(self.hook)
             request.setDoneEvent(self.hook)
             self.loader.loadAsync(request)
             self.loader.loadAsync(request)
             cb.requests.add(request)
             cb.requests.add(request)
-            self.__requests[request] = (cb, i)
+            cb.requestList.append(request)
+            self._requests[request] = (cb, i)
             i += 1
             i += 1
         return cb
         return cb
 
 
@@ -980,36 +1065,26 @@ class Loader(DirectObject):
         of loaded objects, and call the appropriate callback when it's
         of loaded objects, and call the appropriate callback when it's
         time."""
         time."""
 
 
-        if request not in self.__requests:
+        if request not in self._requests:
             return
             return
 
 
-        cb, i = self.__requests[request]
-        if cb.cancelled:
+        cb, i = self._requests[request]
+        if cb.cancelled() or request.cancelled():
             # Shouldn't be here.
             # Shouldn't be here.
-            del self.__requests[request]
+            del self._requests[request]
             return
             return
 
 
         cb.requests.discard(request)
         cb.requests.discard(request)
         if not cb.requests:
         if not cb.requests:
-            del self.__requests[request]
-
-        object = None
-        if hasattr(request, "getModel"):
-            node = request.getModel()
-            if node is not None:
-                object = NodePath(node)
-
-        elif hasattr(request, "getSound"):
-            object = request.getSound()
+            del self._requests[request]
 
 
-        elif hasattr(request, "getSuccess"):
-            object = request.getSuccess()
+        result = request.result()
+        if isinstance(result, PandaNode):
+            result = NodePath(result)
 
 
-        cb.gotObject(i, object)
+        cb.gotObject(i, result)
 
 
     load_model = loadModel
     load_model = loadModel
-    cancel_request = cancelRequest
-    is_request_pending = isRequestPending
     unload_model = unloadModel
     unload_model = unloadModel
     save_model = saveModel
     save_model = saveModel
     load_font = loadFont
     load_font = loadFont

+ 24 - 1
direct/src/showbase/Messenger.py

@@ -109,6 +109,12 @@ class Messenger:
             if record[0] <= 0:
             if record[0] <= 0:
                 del self._id2object[id]
                 del self._id2object[id]
 
 
+    def future(self, event):
+        """ Returns a future that is triggered by the given event name.  This
+        will function only once. """
+
+        return eventMgr.eventHandler.get_future(event)
+
     def accept(self, event, object, method, extraArgs=[], persistent=1):
     def accept(self, event, object, method, extraArgs=[], persistent=1):
         """ accept(self, string, DirectObject, Function, List, Boolean)
         """ accept(self, string, DirectObject, Function, List, Boolean)
 
 
@@ -409,10 +415,14 @@ class Messenger:
                 # Release the lock temporarily while we call the method.
                 # Release the lock temporarily while we call the method.
                 self.lock.release()
                 self.lock.release()
                 try:
                 try:
-                    method (*(extraArgs + sentArgs))
+                    result = method (*(extraArgs + sentArgs))
                 finally:
                 finally:
                     self.lock.acquire()
                     self.lock.acquire()
 
 
+                if hasattr(result, 'cr_await'):
+                    # It's a coroutine, so schedule it with the task manager.
+                    taskMgr.add(result)
+
     def clear(self):
     def clear(self):
         """
         """
         Start fresh with a clear dict
         Start fresh with a clear dict
@@ -635,3 +645,16 @@ class Messenger:
         str = str + '='*50 + '\n'
         str = str + '='*50 + '\n'
         return str
         return str
 
 
+    #snake_case alias:
+    get_events = getEvents
+    is_ignoring = isIgnoring
+    who_accepts = whoAccepts
+    find_all = findAll
+    replace_method = replaceMethod
+    ignore_all = ignoreAll
+    is_accepting = isAccepting
+    is_empty = isEmpty
+    detailed_repr = detailedRepr
+    get_all_accepting = getAllAccepting
+    toggle_verbose = toggleVerbose
+

+ 42 - 4
direct/src/showbase/MirrorDemo.py

@@ -24,7 +24,8 @@ __all__ = ['setupMirror', 'showFrustum']
 from panda3d.core import *
 from panda3d.core import *
 from direct.task import Task
 from direct.task import Task
 
 
-def setupMirror(name, width, height, rootCamera = None):
+def setupMirror(name, width, height, rootCamera = None,
+                bufferSize = 256, clearColor = None):
     # The return value is a NodePath that contains a rectangle that
     # The return value is a NodePath that contains a rectangle that
     # reflects render.  You can reparent, reposition, and rotate it
     # reflects render.  You can reparent, reposition, and rotate it
     # anywhere you like.
     # anywhere you like.
@@ -49,9 +50,12 @@ def setupMirror(name, width, height, rootCamera = None):
     # Now create an offscreen buffer for rendering the mirror's point
     # Now create an offscreen buffer for rendering the mirror's point
     # of view.  The parameters here control the resolution of the
     # of view.  The parameters here control the resolution of the
     # texture.
     # texture.
-    buffer = base.win.makeTextureBuffer(name, 256, 256)
-    #buffer.setClearColor(base.win.getClearColor())
-    buffer.setClearColor(VBase4(0, 0, 1, 1))
+    buffer = base.win.makeTextureBuffer(name, bufferSize, bufferSize)
+    if clearColor is None:
+        buffer.setClearColor(base.win.getClearColor())
+        #buffer.setClearColor(VBase4(0, 0, 1, 1))
+    else:
+        buffer.setClearColor(clearColor)
 
 
     # Set up a display region on this buffer, and create a camera.
     # Set up a display region on this buffer, and create a camera.
     dr = buffer.makeDisplayRegion()
     dr = buffer.makeDisplayRegion()
@@ -85,6 +89,10 @@ def setupMirror(name, width, height, rootCamera = None):
         # Set the camera to the mirror-image position of the main camera.
         # Set the camera to the mirror-image position of the main camera.
         cameraNP.setMat(rootCamera.getMat(planeNP) * plane.getReflectionMat())
         cameraNP.setMat(rootCamera.getMat(planeNP) * plane.getReflectionMat())
 
 
+        # Set the cameras roll to the roll of the mirror. Otherwise
+        # mirrored objects will be moved unexpectedly
+        cameraNP.setR(planeNP.getR()-180)
+
         # And reset the frustum to exactly frame the mirror's corners.
         # And reset the frustum to exactly frame the mirror's corners.
         # This is a minor detail, but it helps to provide a realistic
         # This is a minor detail, but it helps to provide a realistic
         # reflection and keep the subject centered.
         # reflection and keep the subject centered.
@@ -92,6 +100,18 @@ def setupMirror(name, width, height, rootCamera = None):
         ur = cameraNP.getRelativePoint(card, Point3(width / 2.0, 0, height / 2.0))
         ur = cameraNP.getRelativePoint(card, Point3(width / 2.0, 0, height / 2.0))
         ll = cameraNP.getRelativePoint(card, Point3(-width / 2.0, 0, -height / 2.0))
         ll = cameraNP.getRelativePoint(card, Point3(-width / 2.0, 0, -height / 2.0))
         lr = cameraNP.getRelativePoint(card, Point3(width / 2.0, 0, -height / 2.0))
         lr = cameraNP.getRelativePoint(card, Point3(width / 2.0, 0, -height / 2.0))
+
+        # get the distance from the mirrors camera to the mirror plane
+        camvec = planeNP.getPos() - cameraNP.getPos()
+        camdist = camvec.length()
+
+        # set the discance on the mirrors corners so it will keep correct
+        # sizes of the mirrored objects
+        ul.setY(camdist)
+        ur.setY(camdist)
+        ll.setY(camdist)
+        lr.setY(camdist)
+
         lens.setFrustumFromCorners(ul, ur, ll, lr, Lens.FCCameraPlane | Lens.FCOffAxis | Lens.FCAspectRatio)
         lens.setFrustumFromCorners(ul, ur, ll, lr, Lens.FCCameraPlane | Lens.FCOffAxis | Lens.FCAspectRatio)
 
 
         return Task.cont
         return Task.cont
@@ -116,3 +136,21 @@ def showFrustum(np):
     geomNode.addGeom(lens.makeGeometry())
     geomNode.addGeom(lens.makeGeometry())
     cameraNP.attachNewNode(geomNode)
     cameraNP.attachNewNode(geomNode)
 
 
+if __name__ == "__main__":
+    from direct.showbase.ShowBase import ShowBase
+    base = ShowBase()
+
+    panda = loader.loadModel("panda")
+    panda.setH(180)
+    panda.setPos(0, 10, -2.5)
+    panda.setScale(0.5)
+    panda.reparentTo(render)
+
+    myMirror = setupMirror("mirror", 10, 10, bufferSize=1024, clearColor=(0, 0, 1, 1))
+    myMirror.setPos(0, 15, 2.5)
+    myMirror.setH(180)
+
+    # Uncomment this to show the frustum of the camera in the mirror
+    #showFrustum(render)
+
+    base.run()

+ 53 - 45
direct/src/showbase/ShowBase.py

@@ -968,7 +968,9 @@ class ShowBase(DirectObject.DirectObject):
             if isinstance(self.win, GraphicsWindow):
             if isinstance(self.win, GraphicsWindow):
                 self.setupMouse(self.win)
                 self.setupMouse(self.win)
             self.makeCamera2d(self.win)
             self.makeCamera2d(self.win)
-            self.makeCamera2dp(self.win)
+
+            if self.wantRender2dp:
+                self.makeCamera2dp(self.win)
 
 
             if oldLens != None:
             if oldLens != None:
                 # Restore the previous lens properties.
                 # Restore the previous lens properties.
@@ -1258,9 +1260,9 @@ class ShowBase(DirectObject.DirectObject):
         if win == None:
         if win == None:
             win = self.win
             win = self.win
 
 
-        if win != None and win.hasSize() and win.getSbsLeftYSize() != 0:
+        if win != None and win.getSideBySideStereo() and \
+                win.hasSize() and win.getSbsLeftYSize() != 0:
             aspectRatio = float(win.getSbsLeftXSize()) / float(win.getSbsLeftYSize())
             aspectRatio = float(win.getSbsLeftXSize()) / float(win.getSbsLeftYSize())
-
         else:
         else:
             if win == None or not hasattr(win, "getRequestedProperties"):
             if win == None or not hasattr(win, "getRequestedProperties"):
                 props = WindowProperties.getDefault()
                 props = WindowProperties.getDefault()
@@ -1295,8 +1297,7 @@ class ShowBase(DirectObject.DirectObject):
                 if not props.hasSize():
                 if not props.hasSize():
                     props = WindowProperties.getDefault()
                     props = WindowProperties.getDefault()
 
 
-            if props.hasSize():
-                return props.getXSize(), props.getYSize()
+            return props.getXSize(), props.getYSize()
 
 
     def makeCamera(self, win, sort = 0, scene = None,
     def makeCamera(self, win, sort = 0, scene = None,
                    displayRegion = (0, 1, 0, 1), stereo = None,
                    displayRegion = (0, 1, 0, 1), stereo = None,
@@ -1560,9 +1561,11 @@ class ShowBase(DirectObject.DirectObject):
 
 
         # Tell the gui system about our new mouse watcher.
         # Tell the gui system about our new mouse watcher.
         self.aspect2d.node().setMouseWatcher(mw.node())
         self.aspect2d.node().setMouseWatcher(mw.node())
-        self.aspect2dp.node().setMouseWatcher(mw.node())
         self.pixel2d.node().setMouseWatcher(mw.node())
         self.pixel2d.node().setMouseWatcher(mw.node())
-        self.pixel2dp.node().setMouseWatcher(mw.node())
+        if self.wantRender2dp:
+            self.aspect2dp.node().setMouseWatcher(mw.node())
+            self.pixel2dp.node().setMouseWatcher(mw.node())
+
         mw.node().addRegion(PGMouseWatcherBackground())
         mw.node().addRegion(PGMouseWatcherBackground())
 
 
         return self.buttonThrowers[0]
         return self.buttonThrowers[0]
@@ -2723,13 +2726,15 @@ class ShowBase(DirectObject.DirectObject):
             # changed and update the camera lenses and aspect2d parameters
             # changed and update the camera lenses and aspect2d parameters
             self.adjustWindowAspectRatio(self.getAspectRatio())
             self.adjustWindowAspectRatio(self.getAspectRatio())
 
 
-            # Temporary hasattr for old Pandas
-            if not hasattr(win, 'getSbsLeftXSize'):
-                self.pixel2d.setScale(2.0 / win.getXSize(), 1.0, 2.0 / win.getYSize())
-                self.pixel2dp.setScale(2.0 / win.getXSize(), 1.0, 2.0 / win.getYSize())
-            else:
+            if win.getSideBySideStereo() and win.hasSize() and win.getSbsLeftYSize() != 0:
                 self.pixel2d.setScale(2.0 / win.getSbsLeftXSize(), 1.0, 2.0 / win.getSbsLeftYSize())
                 self.pixel2d.setScale(2.0 / win.getSbsLeftXSize(), 1.0, 2.0 / win.getSbsLeftYSize())
                 self.pixel2dp.setScale(2.0 / win.getSbsLeftXSize(), 1.0, 2.0 / win.getSbsLeftYSize())
                 self.pixel2dp.setScale(2.0 / win.getSbsLeftXSize(), 1.0, 2.0 / win.getSbsLeftYSize())
+            else:
+                xsize, ysize = self.getSize()
+                if xsize > 0 and ysize > 0:
+                    self.pixel2d.setScale(2.0 / xsize, 1.0, 2.0 / ysize)
+                    if self.wantRender2dp:
+                        self.pixel2dp.setScale(2.0 / xsize, 1.0, 2.0 / ysize)
 
 
     def adjustWindowAspectRatio(self, aspectRatio):
     def adjustWindowAspectRatio(self, aspectRatio):
         """ This function is normally called internally by
         """ This function is normally called internally by
@@ -2753,11 +2758,12 @@ class ShowBase(DirectObject.DirectObject):
                 self.a2dLeft = -1
                 self.a2dLeft = -1
                 self.a2dRight = 1.0
                 self.a2dRight = 1.0
                 # Don't forget 2dp
                 # Don't forget 2dp
-                self.aspect2dp.setScale(1.0, aspectRatio, aspectRatio)
-                self.a2dpTop = 1.0 / aspectRatio
-                self.a2dpBottom = - 1.0 / aspectRatio
-                self.a2dpLeft = -1
-                self.a2dpRight = 1.0
+                if self.wantRender2dp:
+                    self.aspect2dp.setScale(1.0, aspectRatio, aspectRatio)
+                    self.a2dpTop = 1.0 / aspectRatio
+                    self.a2dpBottom = - 1.0 / aspectRatio
+                    self.a2dpLeft = -1
+                    self.a2dpRight = 1.0
 
 
             else:
             else:
                 # If the window is WIDE, lets expand the left and right
                 # If the window is WIDE, lets expand the left and right
@@ -2767,41 +2773,43 @@ class ShowBase(DirectObject.DirectObject):
                 self.a2dLeft = -aspectRatio
                 self.a2dLeft = -aspectRatio
                 self.a2dRight = aspectRatio
                 self.a2dRight = aspectRatio
                 # Don't forget 2dp
                 # Don't forget 2dp
-                self.aspect2dp.setScale(1.0 / aspectRatio, 1.0, 1.0)
-                self.a2dpTop = 1.0
-                self.a2dpBottom = -1.0
-                self.a2dpLeft = -aspectRatio
-                self.a2dpRight = aspectRatio
+                if self.wantRender2dp:
+                    self.aspect2dp.setScale(1.0 / aspectRatio, 1.0, 1.0)
+                    self.a2dpTop = 1.0
+                    self.a2dpBottom = -1.0
+                    self.a2dpLeft = -aspectRatio
+                    self.a2dpRight = aspectRatio
 
 
             # Reposition the aspect2d marker nodes
             # Reposition the aspect2d marker nodes
-            self.a2dTopCenter.setPos(0, self.a2dTop, self.a2dTop)
-            self.a2dBottomCenter.setPos(0, self.a2dBottom, self.a2dBottom)
+            self.a2dTopCenter.setPos(0, 0, self.a2dTop)
+            self.a2dTopCenterNs.setPos(0, 0, self.a2dTop)
+            self.a2dBottomCenter.setPos(0, 0, self.a2dBottom)
+            self.a2dBottomCenterNs.setPos(0, 0, self.a2dBottom)
             self.a2dLeftCenter.setPos(self.a2dLeft, 0, 0)
             self.a2dLeftCenter.setPos(self.a2dLeft, 0, 0)
-            self.a2dRightCenter.setPos(self.a2dRight, 0, 0)
-            self.a2dTopLeft.setPos(self.a2dLeft, self.a2dTop, self.a2dTop)
-            self.a2dTopRight.setPos(self.a2dRight, self.a2dTop, self.a2dTop)
-            self.a2dBottomLeft.setPos(self.a2dLeft, self.a2dBottom, self.a2dBottom)
-            self.a2dBottomRight.setPos(self.a2dRight, self.a2dBottom, self.a2dBottom)
-
-            # Reposition the aspect2d marker nodes
-            self.a2dTopCenterNs.setPos(0, self.a2dTop, self.a2dTop)
-            self.a2dBottomCenterNs.setPos(0, self.a2dBottom, self.a2dBottom)
             self.a2dLeftCenterNs.setPos(self.a2dLeft, 0, 0)
             self.a2dLeftCenterNs.setPos(self.a2dLeft, 0, 0)
+            self.a2dRightCenter.setPos(self.a2dRight, 0, 0)
             self.a2dRightCenterNs.setPos(self.a2dRight, 0, 0)
             self.a2dRightCenterNs.setPos(self.a2dRight, 0, 0)
-            self.a2dTopLeftNs.setPos(self.a2dLeft, self.a2dTop, self.a2dTop)
-            self.a2dTopRightNs.setPos(self.a2dRight, self.a2dTop, self.a2dTop)
-            self.a2dBottomLeftNs.setPos(self.a2dLeft, self.a2dBottom, self.a2dBottom)
-            self.a2dBottomRightNs.setPos(self.a2dRight, self.a2dBottom, self.a2dBottom)
+
+            self.a2dTopLeft.setPos(self.a2dLeft, 0, self.a2dTop)
+            self.a2dTopLeftNs.setPos(self.a2dLeft, 0, self.a2dTop)
+            self.a2dTopRight.setPos(self.a2dRight, 0, self.a2dTop)
+            self.a2dTopRightNs.setPos(self.a2dRight, 0, self.a2dTop)
+            self.a2dBottomLeft.setPos(self.a2dLeft, 0, self.a2dBottom)
+            self.a2dBottomLeftNs.setPos(self.a2dLeft, 0, self.a2dBottom)
+            self.a2dBottomRight.setPos(self.a2dRight, 0, self.a2dBottom)
+            self.a2dBottomRightNs.setPos(self.a2dRight, 0, self.a2dBottom)
 
 
             # Reposition the aspect2dp marker nodes
             # Reposition the aspect2dp marker nodes
-            self.a2dpTopCenter.setPos(0, self.a2dpTop, self.a2dpTop)
-            self.a2dpBottomCenter.setPos(0, self.a2dpBottom, self.a2dpBottom)
-            self.a2dpLeftCenter.setPos(self.a2dpLeft, 0, 0)
-            self.a2dpRightCenter.setPos(self.a2dpRight, 0, 0)
-            self.a2dpTopLeft.setPos(self.a2dpLeft, self.a2dpTop, self.a2dpTop)
-            self.a2dpTopRight.setPos(self.a2dpRight, self.a2dpTop, self.a2dpTop)
-            self.a2dpBottomLeft.setPos(self.a2dpLeft, self.a2dpBottom, self.a2dpBottom)
-            self.a2dpBottomRight.setPos(self.a2dpRight, self.a2dpBottom, self.a2dpBottom)
+            if self.wantRender2dp:
+                self.a2dpTopCenter.setPos(0, 0, self.a2dpTop)
+                self.a2dpBottomCenter.setPos(0, 0, self.a2dpBottom)
+                self.a2dpLeftCenter.setPos(self.a2dpLeft, 0, 0)
+                self.a2dpRightCenter.setPos(self.a2dpRight, 0, 0)
+
+                self.a2dpTopLeft.setPos(self.a2dpLeft, 0, self.a2dpTop)
+                self.a2dpTopRight.setPos(self.a2dpRight, 0, self.a2dpTop)
+                self.a2dpBottomLeft.setPos(self.a2dpLeft, 0, self.a2dpBottom)
+                self.a2dpBottomRight.setPos(self.a2dpRight, 0, self.a2dpBottom)
 
 
             # If anybody needs to update their GUI, put a callback on this event
             # If anybody needs to update their GUI, put a callback on this event
             messenger.send("aspectRatioChanged")
             messenger.send("aspectRatioChanged")

+ 2 - 2
direct/src/showbase/Transitions.py

@@ -347,7 +347,7 @@ class Transitions:
                 frameColor = (0, 0, 0, 1),
                 frameColor = (0, 0, 0, 1),
                 borderWidth = (0, 0),
                 borderWidth = (0, 0),
                 frameSize = (-1, 1, 0, 0.2),
                 frameSize = (-1, 1, 0, 0.2),
-                pos = (0, 0, 0.8),
+                pos = (0, 0, 1.0),
                 image = barImage,
                 image = barImage,
                 image_scale = (2.25,1,.5),
                 image_scale = (2.25,1,.5),
                 image_pos = (0,0,.1),
                 image_pos = (0,0,.1),
@@ -362,7 +362,7 @@ class Transitions:
                 frameColor = (0, 0, 0, 1),
                 frameColor = (0, 0, 0, 1),
                 borderWidth = (0, 0),
                 borderWidth = (0, 0),
                 frameSize = (-1, 1, 0, 0.2),
                 frameSize = (-1, 1, 0, 0.2),
-                pos = (0, 0, -1),
+                pos = (0, 0, -1.2),
                 image = barImage,
                 image = barImage,
                 image_scale = (2.25,1,.5),
                 image_scale = (2.25,1,.5),
                 image_pos = (0,0,.1),
                 image_pos = (0,0,.1),

+ 35 - 11
direct/src/stdpy/thread.py

@@ -11,18 +11,32 @@ __all__ = [
     'interrupt_main',
     'interrupt_main',
     'exit', 'allocate_lock', 'get_ident',
     'exit', 'allocate_lock', 'get_ident',
     'stack_size',
     'stack_size',
+    'force_yield', 'consider_yield',
     'forceYield', 'considerYield',
     'forceYield', 'considerYield',
+    'TIMEOUT_MAX'
     ]
     ]
 
 
 from panda3d import core
 from panda3d import core
+import sys
+
+if sys.platform == "win32":
+    TIMEOUT_MAX = float(0xffffffff // 1000)
+else:
+    TIMEOUT_MAX = float(0x7fffffffffffffff // 1000000000)
 
 
 # These methods are defined in Panda, and are particularly useful if
 # These methods are defined in Panda, and are particularly useful if
 # you may be running in Panda's SIMPLE_THREADS compilation mode.
 # you may be running in Panda's SIMPLE_THREADS compilation mode.
-forceYield = core.Thread.forceYield
-considerYield = core.Thread.considerYield
+force_yield = core.Thread.force_yield
+consider_yield = core.Thread.consider_yield
 
 
-class error(Exception):
-    pass
+forceYield = force_yield
+considerYield = consider_yield
+
+if sys.version_info >= (3, 3):
+    error = RuntimeError
+else:
+    class error(Exception):
+        pass
 
 
 class LockType:
 class LockType:
     """ Implements a mutex lock.  Instead of directly subclassing
     """ Implements a mutex lock.  Instead of directly subclassing
@@ -36,13 +50,18 @@ class LockType:
         self.__cvar = core.ConditionVar(self.__lock)
         self.__cvar = core.ConditionVar(self.__lock)
         self.__locked = False
         self.__locked = False
 
 
-    def acquire(self, waitflag = 1):
+    def acquire(self, waitflag = 1, timeout = -1):
         self.__lock.acquire()
         self.__lock.acquire()
         try:
         try:
             if self.__locked and not waitflag:
             if self.__locked and not waitflag:
                 return False
                 return False
-            while self.__locked:
-                self.__cvar.wait()
+
+            if timeout >= 0:
+                while self.__locked:
+                    self.__cvar.wait(timeout)
+            else:
+                while self.__locked:
+                    self.__cvar.wait()
 
 
             self.__locked = True
             self.__locked = True
             return True
             return True
@@ -202,12 +221,17 @@ def _get_thread_locals(thread, i):
 def _remove_thread_id(threadId):
 def _remove_thread_id(threadId):
     """ Removes the thread with the indicated ID from the thread list. """
     """ Removes the thread with the indicated ID from the thread list. """
 
 
+    # On interpreter shutdown, Python may set module globals to None.
+    if _threadsLock is None or _threads is None:
+        return
+
     _threadsLock.acquire()
     _threadsLock.acquire()
     try:
     try:
-        thread, locals, wrapper = _threads[threadId]
-        assert thread.getPythonIndex() == threadId
-        del _threads[threadId]
-        thread.setPythonIndex(-1)
+        if threadId in _threads:
+            thread, locals, wrapper = _threads[threadId]
+            assert thread.getPythonIndex() == threadId
+            del _threads[threadId]
+            thread.setPythonIndex(-1)
 
 
     finally:
     finally:
         _threadsLock.release()
         _threadsLock.release()

+ 33 - 11
direct/src/stdpy/threading.py

@@ -35,11 +35,15 @@ __all__ = [
     'Event',
     'Event',
     'Timer',
     'Timer',
     'local',
     'local',
-    'current_thread', 'currentThread',
-    'enumerate', 'active_count', 'activeCount',
+    'current_thread',
+    'main_thread',
+    'enumerate', 'active_count',
     'settrace', 'setprofile', 'stack_size',
     'settrace', 'setprofile', 'stack_size',
+    'TIMEOUT_MAX',
     ]
     ]
 
 
+TIMEOUT_MAX = _thread.TIMEOUT_MAX
+
 local = _thread._local
 local = _thread._local
 _newname = _thread._newname
 _newname = _thread._newname
 
 
@@ -98,7 +102,15 @@ class Thread(ThreadBase):
         self.__dict__['daemon'] = current.daemon
         self.__dict__['daemon'] = current.daemon
         self.__dict__['name'] = name
         self.__dict__['name'] = name
 
 
-        self.__thread = core.PythonThread(self.run, None, name, name)
+        def call_run():
+            # As soon as the thread is done, break the circular reference.
+            try:
+                self.run()
+            finally:
+                self.__thread = None
+                _thread._remove_thread_id(self.ident)
+
+        self.__thread = core.PythonThread(call_run, None, name, name)
         threadId = _thread._add_thread(self.__thread, weakref.proxy(self))
         threadId = _thread._add_thread(self.__thread, weakref.proxy(self))
         self.__dict__['ident'] = threadId
         self.__dict__['ident'] = threadId
 
 
@@ -109,16 +121,17 @@ class Thread(ThreadBase):
             _thread._remove_thread_id(self.ident)
             _thread._remove_thread_id(self.ident)
 
 
     def is_alive(self):
     def is_alive(self):
-        return self.__thread.isStarted()
+        thread = self.__thread
+        return thread is not None and thread.is_started()
 
 
-    def isAlive(self):
-        return self.__thread.isStarted()
+    isAlive = is_alive
 
 
     def start(self):
     def start(self):
-        if self.__thread.isStarted():
+        thread = self.__thread
+        if thread is None or thread.is_started():
             raise RuntimeError
             raise RuntimeError
 
 
-        if not self.__thread.start(core.TPNormal, True):
+        if not thread.start(core.TPNormal, True):
             raise RuntimeError
             raise RuntimeError
 
 
     def run(self):
     def run(self):
@@ -132,8 +145,12 @@ class Thread(ThreadBase):
     def join(self, timeout = None):
     def join(self, timeout = None):
         # We don't support a timed join here, sorry.
         # We don't support a timed join here, sorry.
         assert timeout is None
         assert timeout is None
-        self.__thread.join()
-        self.__thread = None
+        thread = self.__thread
+        if thread is not None:
+            thread.join()
+            # Clear the circular reference.
+            self.__thread = None
+            _thread._remove_thread_id(self.ident)
 
 
     def setName(self, name):
     def setName(self, name):
         self.__dict__['name'] = name
         self.__dict__['name'] = name
@@ -379,6 +396,10 @@ def current_thread():
     t = core.Thread.getCurrentThread()
     t = core.Thread.getCurrentThread()
     return _thread._get_thread_wrapper(t, _create_thread_wrapper)
     return _thread._get_thread_wrapper(t, _create_thread_wrapper)
 
 
+def main_thread():
+    t = core.Thread.getMainThread()
+    return _thread._get_thread_wrapper(t, _create_thread_wrapper)
+
 currentThread = current_thread
 currentThread = current_thread
 
 
 def enumerate():
 def enumerate():
@@ -386,7 +407,7 @@ def enumerate():
     _thread._threadsLock.acquire()
     _thread._threadsLock.acquire()
     try:
     try:
         for thread, locals, wrapper in list(_thread._threads.values()):
         for thread, locals, wrapper in list(_thread._threads.values()):
-            if wrapper and thread.isStarted():
+            if wrapper and wrapper.is_alive():
                 tlist.append(wrapper)
                 tlist.append(wrapper)
         return tlist
         return tlist
     finally:
     finally:
@@ -394,6 +415,7 @@ def enumerate():
 
 
 def active_count():
 def active_count():
     return len(enumerate())
     return len(enumerate())
+
 activeCount = active_count
 activeCount = active_count
 
 
 _settrace_func = None
 _settrace_func = None

+ 43 - 26
direct/src/stdpy/threading2.py

@@ -15,7 +15,7 @@ implementation. """
 
 
 import sys as _sys
 import sys as _sys
 
 
-from direct.stdpy import thread
+from direct.stdpy import thread as _thread
 from direct.stdpy.thread import stack_size, _newname, _local as local
 from direct.stdpy.thread import stack_size, _newname, _local as local
 from panda3d import core
 from panda3d import core
 _sleep = core.Thread.sleep
 _sleep = core.Thread.sleep
@@ -23,16 +23,19 @@ _sleep = core.Thread.sleep
 from time import time as _time
 from time import time as _time
 from traceback import format_exc as _format_exc
 from traceback import format_exc as _format_exc
 
 
-# Rename some stuff so "from threading import *" is safe
-__all__ = ['activeCount', 'Condition', 'currentThread', 'enumerate', 'Event',
-           'Lock', 'RLock', 'Semaphore', 'BoundedSemaphore', 'Thread',
-           'Timer', 'setprofile', 'settrace', 'local', 'stack_size']
+__all__ = ['get_ident', 'active_count', 'Condition', 'current_thread',
+           'enumerate', 'main_thread', 'TIMEOUT_MAX',
+           'Event', 'Lock', 'RLock', 'Semaphore', 'BoundedSemaphore', 'Thread',
+           'Timer', 'ThreadError',
+           'setprofile', 'settrace', 'local', 'stack_size']
 
 
-_start_new_thread = thread.start_new_thread
-_allocate_lock = thread.allocate_lock
-_get_ident = thread.get_ident
-ThreadError = thread.error
-del thread
+# Rename some stuff so "from threading import *" is safe
+_start_new_thread = _thread.start_new_thread
+_allocate_lock = _thread.allocate_lock
+get_ident = _thread.get_ident
+ThreadError = _thread.error
+TIMEOUT_MAX = _thread.TIMEOUT_MAX
+del _thread
 
 
 
 
 # Debug support (adapted from ihooks.py).
 # Debug support (adapted from ihooks.py).
@@ -446,7 +449,7 @@ class Thread(_Verbose):
         try:
         try:
             self.__started = True
             self.__started = True
             _active_limbo_lock.acquire()
             _active_limbo_lock.acquire()
-            _active[_get_ident()] = self
+            _active[get_ident()] = self
             del _limbo[self]
             del _limbo[self]
             _active_limbo_lock.release()
             _active_limbo_lock.release()
             if __debug__:
             if __debug__:
@@ -537,7 +540,7 @@ class Thread(_Verbose):
         _active_limbo_lock.acquire()
         _active_limbo_lock.acquire()
         try:
         try:
             try:
             try:
-                del _active[_get_ident()]
+                del _active[get_ident()]
             except KeyError:
             except KeyError:
                 if 'dummy_threading' not in _sys.modules:
                 if 'dummy_threading' not in _sys.modules:
                     raise
                     raise
@@ -581,10 +584,12 @@ class Thread(_Verbose):
         assert self.__initialized, "Thread.__init__() not called"
         assert self.__initialized, "Thread.__init__() not called"
         self.__name = str(name)
         self.__name = str(name)
 
 
-    def isAlive(self):
+    def is_alive(self):
         assert self.__initialized, "Thread.__init__() not called"
         assert self.__initialized, "Thread.__init__() not called"
         return self.__started and not self.__stopped
         return self.__started and not self.__stopped
 
 
+    isAlive = is_alive
+
     def isDaemon(self):
     def isDaemon(self):
         assert self.__initialized, "Thread.__init__() not called"
         assert self.__initialized, "Thread.__init__() not called"
         return self.__daemonic
         return self.__daemonic
@@ -634,7 +639,7 @@ class _MainThread(Thread):
         Thread.__init__(self, name="MainThread")
         Thread.__init__(self, name="MainThread")
         self._Thread__started = True
         self._Thread__started = True
         _active_limbo_lock.acquire()
         _active_limbo_lock.acquire()
-        _active[_get_ident()] = self
+        _active[get_ident()] = self
         _active_limbo_lock.release()
         _active_limbo_lock.release()
 
 
     def _set_daemon(self):
     def _set_daemon(self):
@@ -653,12 +658,6 @@ class _MainThread(Thread):
             self._note("%s: exiting", self)
             self._note("%s: exiting", self)
         self._Thread__delete()
         self._Thread__delete()
 
 
-def _pickSomeNonDaemonThread():
-    for t in enumerate():
-        if not t.isDaemon() and t.isAlive():
-            return t
-    return None
-
 
 
 # Dummy thread class to represent threads not started here.
 # Dummy thread class to represent threads not started here.
 # These aren't garbage collected when they die, nor can they be waited for.
 # These aren't garbage collected when they die, nor can they be waited for.
@@ -680,7 +679,7 @@ class _DummyThread(Thread):
 
 
         self._Thread__started = True
         self._Thread__started = True
         _active_limbo_lock.acquire()
         _active_limbo_lock.acquire()
-        _active[_get_ident()] = self
+        _active[get_ident()] = self
         _active_limbo_lock.release()
         _active_limbo_lock.release()
 
 
     def _set_daemon(self):
     def _set_daemon(self):
@@ -692,19 +691,23 @@ class _DummyThread(Thread):
 
 
 # Global API functions
 # Global API functions
 
 
-def currentThread():
+def current_thread():
     try:
     try:
-        return _active[_get_ident()]
+        return _active[get_ident()]
     except KeyError:
     except KeyError:
-        ##print "currentThread(): no current thread for", _get_ident()
+        ##print "current_thread(): no current thread for", get_ident()
         return _DummyThread()
         return _DummyThread()
 
 
-def activeCount():
+currentThread = current_thread
+
+def active_count():
     _active_limbo_lock.acquire()
     _active_limbo_lock.acquire()
     count = len(_active) + len(_limbo)
     count = len(_active) + len(_limbo)
     _active_limbo_lock.release()
     _active_limbo_lock.release()
     return count
     return count
 
 
+activeCount = active_count
+
 def enumerate():
 def enumerate():
     _active_limbo_lock.acquire()
     _active_limbo_lock.acquire()
     active = list(_active.values()) + list(_limbo.values())
     active = list(_active.values()) + list(_limbo.values())
@@ -717,7 +720,21 @@ def enumerate():
 # and make it available for the interpreter
 # and make it available for the interpreter
 # (Py_Main) as threading._shutdown.
 # (Py_Main) as threading._shutdown.
 
 
-_shutdown = _MainThread()._exitfunc
+_main_thread = _MainThread()
+_shutdown = _main_thread._exitfunc
+
+def _pickSomeNonDaemonThread():
+    for t in enumerate():
+        if not t.isDaemon() and t.isAlive():
+            return t
+    return None
+
+def main_thread():
+    """Return the main thread object.
+    In normal conditions, the main thread is the thread from which the
+    Python interpreter was started.
+    """
+    return _main_thread
 
 
 # get thread-local implementation, either from the thread
 # get thread-local implementation, either from the thread
 # module, or from the python fallback
 # module, or from the python fallback

+ 13 - 1
direct/src/task/Task.py

@@ -74,7 +74,9 @@ Task = PythonTask
 # Copy the module-level enums above into the class level.  This funny
 # Copy the module-level enums above into the class level.  This funny
 # syntax is necessary because it's a C++-wrapped extension type, not a
 # syntax is necessary because it's a C++-wrapped extension type, not a
 # true Python class.
 # true Python class.
-Task.DtoolClassDict['done'] = done
+# We can't override 'done', which is already a known method.  We have a
+# special check in PythonTask for when the method is being returned.
+#Task.DtoolClassDict['done'] = done
 Task.DtoolClassDict['cont'] = cont
 Task.DtoolClassDict['cont'] = cont
 Task.DtoolClassDict['again'] = again
 Task.DtoolClassDict['again'] = again
 Task.DtoolClassDict['pickup'] = pickup
 Task.DtoolClassDict['pickup'] = pickup
@@ -333,6 +335,7 @@ class TaskManager:
         funcOrTask - either an existing Task object (not already added
         funcOrTask - either an existing Task object (not already added
         to the task manager), or a callable function object.  If this
         to the task manager), or a callable function object.  If this
         is a function, a new Task object will be created and returned.
         is a function, a new Task object will be created and returned.
+        You may also pass in a coroutine object.
 
 
         name - the name to assign to the Task.  Required, unless you
         name - the name to assign to the Task.  Required, unless you
         are passing in a Task object that already has a name.
         are passing in a Task object that already has a name.
@@ -385,6 +388,15 @@ class TaskManager:
             task = funcOrTask
             task = funcOrTask
         elif hasattr(funcOrTask, '__call__'):
         elif hasattr(funcOrTask, '__call__'):
             task = PythonTask(funcOrTask)
             task = PythonTask(funcOrTask)
+            if name is None:
+                name = getattr(funcOrTask, '__qualname__', None) or \
+                       getattr(funcOrTask, '__name__', None)
+        elif hasattr(funcOrTask, 'cr_await') or type(funcOrTask) == types.GeneratorType:
+            # It's a coroutine, or something emulating one.
+            task = PythonTask(funcOrTask)
+            if name is None:
+                name = getattr(funcOrTask, '__qualname__', None) or \
+                       getattr(funcOrTask, '__name__', None)
         else:
         else:
             self.notify.error(
             self.notify.error(
                 'add: Tried to add a task that was not a Task or a func')
                 'add: Tried to add a task that was not a Task or a func')

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


+ 92 - 90
dtool/src/cppparser/cppBison.h.prebuilt

@@ -143,51 +143,52 @@ extern int cppyydebug;
     KW_IS_TRIVIAL = 353,
     KW_IS_TRIVIAL = 353,
     KW_IS_UNION = 354,
     KW_IS_UNION = 354,
     KW_LONG = 355,
     KW_LONG = 355,
-    KW_MAKE_MAP_PROPERTY = 356,
-    KW_MAKE_PROPERTY = 357,
-    KW_MAKE_PROPERTY2 = 358,
-    KW_MAKE_SEQ = 359,
-    KW_MAKE_SEQ_PROPERTY = 360,
-    KW_MUTABLE = 361,
-    KW_NAMESPACE = 362,
-    KW_NEW = 363,
-    KW_NOEXCEPT = 364,
-    KW_NULLPTR = 365,
-    KW_OPERATOR = 366,
-    KW_OVERRIDE = 367,
-    KW_PRIVATE = 368,
-    KW_PROTECTED = 369,
-    KW_PUBLIC = 370,
-    KW_REGISTER = 371,
-    KW_REINTERPRET_CAST = 372,
-    KW_RETURN = 373,
-    KW_SHORT = 374,
-    KW_SIGNED = 375,
-    KW_SIZEOF = 376,
-    KW_STATIC = 377,
-    KW_STATIC_ASSERT = 378,
-    KW_STATIC_CAST = 379,
-    KW_STRUCT = 380,
-    KW_TEMPLATE = 381,
-    KW_THREAD_LOCAL = 382,
-    KW_THROW = 383,
-    KW_TRUE = 384,
-    KW_TRY = 385,
-    KW_TYPEDEF = 386,
-    KW_TYPEID = 387,
-    KW_TYPENAME = 388,
-    KW_UNDERLYING_TYPE = 389,
-    KW_UNION = 390,
-    KW_UNSIGNED = 391,
-    KW_USING = 392,
-    KW_VIRTUAL = 393,
-    KW_VOID = 394,
-    KW_VOLATILE = 395,
-    KW_WCHAR_T = 396,
-    KW_WHILE = 397,
-    START_CPP = 398,
-    START_CONST_EXPR = 399,
-    START_TYPE = 400
+    KW_MAKE_MAP_KEYS_SEQ = 356,
+    KW_MAKE_MAP_PROPERTY = 357,
+    KW_MAKE_PROPERTY = 358,
+    KW_MAKE_PROPERTY2 = 359,
+    KW_MAKE_SEQ = 360,
+    KW_MAKE_SEQ_PROPERTY = 361,
+    KW_MUTABLE = 362,
+    KW_NAMESPACE = 363,
+    KW_NEW = 364,
+    KW_NOEXCEPT = 365,
+    KW_NULLPTR = 366,
+    KW_OPERATOR = 367,
+    KW_OVERRIDE = 368,
+    KW_PRIVATE = 369,
+    KW_PROTECTED = 370,
+    KW_PUBLIC = 371,
+    KW_REGISTER = 372,
+    KW_REINTERPRET_CAST = 373,
+    KW_RETURN = 374,
+    KW_SHORT = 375,
+    KW_SIGNED = 376,
+    KW_SIZEOF = 377,
+    KW_STATIC = 378,
+    KW_STATIC_ASSERT = 379,
+    KW_STATIC_CAST = 380,
+    KW_STRUCT = 381,
+    KW_TEMPLATE = 382,
+    KW_THREAD_LOCAL = 383,
+    KW_THROW = 384,
+    KW_TRUE = 385,
+    KW_TRY = 386,
+    KW_TYPEDEF = 387,
+    KW_TYPEID = 388,
+    KW_TYPENAME = 389,
+    KW_UNDERLYING_TYPE = 390,
+    KW_UNION = 391,
+    KW_UNSIGNED = 392,
+    KW_USING = 393,
+    KW_VIRTUAL = 394,
+    KW_VOID = 395,
+    KW_VOLATILE = 396,
+    KW_WCHAR_T = 397,
+    KW_WHILE = 398,
+    START_CPP = 399,
+    START_CONST_EXPR = 400,
+    START_TYPE = 401
   };
   };
 #endif
 #endif
 /* Tokens.  */
 /* Tokens.  */
@@ -289,51 +290,52 @@ extern int cppyydebug;
 #define KW_IS_TRIVIAL 353
 #define KW_IS_TRIVIAL 353
 #define KW_IS_UNION 354
 #define KW_IS_UNION 354
 #define KW_LONG 355
 #define KW_LONG 355
-#define KW_MAKE_MAP_PROPERTY 356
-#define KW_MAKE_PROPERTY 357
-#define KW_MAKE_PROPERTY2 358
-#define KW_MAKE_SEQ 359
-#define KW_MAKE_SEQ_PROPERTY 360
-#define KW_MUTABLE 361
-#define KW_NAMESPACE 362
-#define KW_NEW 363
-#define KW_NOEXCEPT 364
-#define KW_NULLPTR 365
-#define KW_OPERATOR 366
-#define KW_OVERRIDE 367
-#define KW_PRIVATE 368
-#define KW_PROTECTED 369
-#define KW_PUBLIC 370
-#define KW_REGISTER 371
-#define KW_REINTERPRET_CAST 372
-#define KW_RETURN 373
-#define KW_SHORT 374
-#define KW_SIGNED 375
-#define KW_SIZEOF 376
-#define KW_STATIC 377
-#define KW_STATIC_ASSERT 378
-#define KW_STATIC_CAST 379
-#define KW_STRUCT 380
-#define KW_TEMPLATE 381
-#define KW_THREAD_LOCAL 382
-#define KW_THROW 383
-#define KW_TRUE 384
-#define KW_TRY 385
-#define KW_TYPEDEF 386
-#define KW_TYPEID 387
-#define KW_TYPENAME 388
-#define KW_UNDERLYING_TYPE 389
-#define KW_UNION 390
-#define KW_UNSIGNED 391
-#define KW_USING 392
-#define KW_VIRTUAL 393
-#define KW_VOID 394
-#define KW_VOLATILE 395
-#define KW_WCHAR_T 396
-#define KW_WHILE 397
-#define START_CPP 398
-#define START_CONST_EXPR 399
-#define START_TYPE 400
+#define KW_MAKE_MAP_KEYS_SEQ 356
+#define KW_MAKE_MAP_PROPERTY 357
+#define KW_MAKE_PROPERTY 358
+#define KW_MAKE_PROPERTY2 359
+#define KW_MAKE_SEQ 360
+#define KW_MAKE_SEQ_PROPERTY 361
+#define KW_MUTABLE 362
+#define KW_NAMESPACE 363
+#define KW_NEW 364
+#define KW_NOEXCEPT 365
+#define KW_NULLPTR 366
+#define KW_OPERATOR 367
+#define KW_OVERRIDE 368
+#define KW_PRIVATE 369
+#define KW_PROTECTED 370
+#define KW_PUBLIC 371
+#define KW_REGISTER 372
+#define KW_REINTERPRET_CAST 373
+#define KW_RETURN 374
+#define KW_SHORT 375
+#define KW_SIGNED 376
+#define KW_SIZEOF 377
+#define KW_STATIC 378
+#define KW_STATIC_ASSERT 379
+#define KW_STATIC_CAST 380
+#define KW_STRUCT 381
+#define KW_TEMPLATE 382
+#define KW_THREAD_LOCAL 383
+#define KW_THROW 384
+#define KW_TRUE 385
+#define KW_TRY 386
+#define KW_TYPEDEF 387
+#define KW_TYPEID 388
+#define KW_TYPENAME 389
+#define KW_UNDERLYING_TYPE 390
+#define KW_UNION 391
+#define KW_UNSIGNED 392
+#define KW_USING 393
+#define KW_VIRTUAL 394
+#define KW_VOID 395
+#define KW_VOLATILE 396
+#define KW_WCHAR_T 397
+#define KW_WHILE 398
+#define START_CPP 399
+#define START_CONST_EXPR 400
+#define START_TYPE 401
 
 
 /* Value type.  */
 /* Value type.  */
 
 

+ 252 - 95
dtool/src/cppparser/cppBison.yxx

@@ -304,6 +304,7 @@ pop_struct() {
 %token KW_IS_TRIVIAL
 %token KW_IS_TRIVIAL
 %token KW_IS_UNION
 %token KW_IS_UNION
 %token KW_LONG
 %token KW_LONG
+%token KW_MAKE_MAP_KEYS_SEQ
 %token KW_MAKE_MAP_PROPERTY
 %token KW_MAKE_MAP_PROPERTY
 %token KW_MAKE_PROPERTY
 %token KW_MAKE_PROPERTY
 %token KW_MAKE_PROPERTY2
 %token KW_MAKE_PROPERTY2
@@ -397,6 +398,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 +550,341 @@ 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;
+    } else {
+      make_property->_del_function = deleter->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);
+  }
+}
+        | KW_MAKE_SEQ_PROPERTY '(' name ',' IDENTIFIER ',' IDENTIFIER ',' IDENTIFIER ',' IDENTIFIER ',' IDENTIFIER ')' ';'
+{
+  CPPDeclaration *length_getter = $5->find_symbol(current_scope, global_scope, current_lexer);
+  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);
+    length_getter = NULL;
+  }
+
+  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);
+    getter = nullptr;
+  }
+
+  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();
     make_property->_length_function = length_getter->as_function_group();
-    if (deleter) {
+
+    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();
+    }
+
+    CPPDeclaration *deleter = $11->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: " + $11->get_fully_scoped_name(), @11);
+    } else {
       make_property->_del_function = deleter->as_function_group();
       make_property->_del_function = deleter->as_function_group();
     }
     }
+
+    CPPDeclaration *inserter = $13->find_symbol(current_scope, global_scope, current_lexer);
+    if (inserter == nullptr || inserter->get_subtype() != CPPDeclaration::ST_function_group) {
+      yyerror("reference to non-existent or invalid append method: " + $13->get_fully_scoped_name(), @13);
+    } else {
+      make_property->_insert_function = inserter->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);
+  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 = 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_MAP_KEYS_SEQ '(' name ',' IDENTIFIER ',' IDENTIFIER ')' ';'
+{
+  CPPDeclaration *length_getter = $5->find_symbol(current_scope, global_scope, current_lexer);
+  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);
+    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) {
-    yyerror("Reference to non-existent or invalid getter: " + $7->get_fully_scoped_name(), @7);
+  if (getter == nullptr || getter->get_subtype() != CPPDeclaration::ST_function_group) {
+    yyerror("reference to non-existent or invalid getter: " + $7->get_fully_scoped_name(), @7);
+    getter = nullptr;
   }
   }
 
 
-  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);
+  if (getter != nullptr && length_getter != nullptr) {
+    CPPMakeProperty *make_property = nullptr;
+    for (size_t i = 0; i < current_scope->_declarations.size(); ++i) {
+      make_property = current_scope->_declarations[i]->as_make_property();
+      if (make_property != nullptr) {
+        if (make_property->get_fully_scoped_name() == $3->get_fully_scoped_name()) {
+          break;
+        } else {
+          make_property = nullptr;
+        }
+      }
+    }
+    if (make_property != nullptr) {
+      make_property->_get_key_function = getter->as_function_group();
+      make_property->_length_function = length_getter->as_function_group();
+    } else {
+      yyerror("reference to non-existent MAKE_MAP_PROPERTY: " + $3->get_fully_scoped_name(), @3);
+    }
   }
   }
+}
+        | 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);
 
 
-  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);
+  } 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();
+    }
+
+    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 = $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();
+    }
+
+    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 +1864,18 @@ maybe_trailing_return_type:
         ;
         ;
 
 
 
 
+maybe_comma_identifier:
+        empty
+{
+  $$ = NULL;
+}
+        | ',' IDENTIFIER
+{
+  $$ = $2;
+}
+        ;
+
+
 function_parameter_list:
 function_parameter_list:
         empty
         empty
 {
 {
@@ -4051,6 +4204,10 @@ name:
         | KW_STATIC
         | KW_STATIC
 {
 {
   $$ = new CPPIdentifier("static", @1);
   $$ = new CPPIdentifier("static", @1);
+}
+        | KW_DEFAULT
+{
+  $$ = new CPPIdentifier("default", @1);
 }
 }
         ;
         ;
 
 

+ 8 - 0
dtool/src/cppparser/cppExtensionType.cxx

@@ -131,6 +131,14 @@ is_copy_constructible() const {
   return (_type == T_enum || _type == T_enum_class || _type == T_enum_struct);
   return (_type == T_enum || _type == T_enum_class || _type == T_enum_struct);
 }
 }
 
 
+/**
+ * Returns true if the type is copy-assignable.
+ */
+bool CPPExtensionType::
+is_copy_assignable() const {
+  return (_type == T_enum || _type == T_enum_class || _type == T_enum_struct);
+}
+
 /**
 /**
  *
  *
  */
  */

+ 1 - 0
dtool/src/cppparser/cppExtensionType.h

@@ -52,6 +52,7 @@ public:
   virtual bool is_constructible(const CPPType *type) const;
   virtual bool is_constructible(const CPPType *type) const;
   virtual bool is_default_constructible() const;
   virtual bool is_default_constructible() const;
   virtual bool is_copy_constructible() const;
   virtual bool is_copy_constructible() const;
+  virtual bool is_copy_assignable() const;
 
 
   virtual CPPDeclaration *substitute_decl(SubstDecl &subst,
   virtual CPPDeclaration *substitute_decl(SubstDecl &subst,
                                           CPPScope *current_scope,
                                           CPPScope *current_scope,

+ 2 - 0
dtool/src/cppparser/cppFunctionType.h

@@ -43,6 +43,8 @@ public:
     F_volatile_method   = 0x4000,
     F_volatile_method   = 0x4000,
     F_lvalue_method     = 0x8000,
     F_lvalue_method     = 0x8000,
     F_rvalue_method     = 0x10000,
     F_rvalue_method     = 0x10000,
+    F_copy_assignment_operator = 0x20000,
+    F_move_assignment_operator = 0x40000,
   };
   };
 
 
   CPPFunctionType(CPPType *return_type, CPPParameterList *parameters,
   CPPFunctionType(CPPType *return_type, CPPParameterList *parameters,

+ 19 - 8
dtool/src/cppparser/cppInstance.cxx

@@ -328,8 +328,8 @@ get_fully_scoped_name() const {
 
 
 /**
 /**
  * If this is a function type instance, checks whether the function name
  * If this is a function type instance, checks whether the function name
- * matches the class name (or ~name), and if so, flags it as a constructor (or
- * destructor).
+ * matches the class name (or ~name), and if so, flags it as a constructor,
+ * destructor or assignment operator
  */
  */
 void CPPInstance::
 void CPPInstance::
 check_for_constructor(CPPScope *current_scope, CPPScope *global_scope) {
 check_for_constructor(CPPScope *current_scope, CPPScope *global_scope) {
@@ -344,13 +344,16 @@ check_for_constructor(CPPScope *current_scope, CPPScope *global_scope) {
     string class_name = scope->get_local_name();
     string class_name = scope->get_local_name();
 
 
     if (!method_name.empty() && !class_name.empty()) {
     if (!method_name.empty() && !class_name.empty()) {
-      if (method_name == class_name) {
+      // Check either a constructor or assignment operator.
+      if (method_name == class_name || method_name == "operator =") {
         CPPType *void_type = CPPType::new_type
         CPPType *void_type = CPPType::new_type
           (new CPPSimpleType(CPPSimpleType::T_void));
           (new CPPSimpleType(CPPSimpleType::T_void));
 
 
-        int flags = func->_flags | CPPFunctionType::F_constructor;
+        int flags = func->_flags;
+        if (method_name == class_name) {
+          flags |= CPPFunctionType::F_constructor;
+        }
 
 
-        // Check if it might be a copy or move constructor.
         CPPParameterList *params = func->_parameters;
         CPPParameterList *params = func->_parameters;
         if (params->_parameters.size() == 1 && !params->_includes_ellipsis) {
         if (params->_parameters.size() == 1 && !params->_includes_ellipsis) {
           CPPType *param_type = params->_parameters[0]->_type;
           CPPType *param_type = params->_parameters[0]->_type;
@@ -360,10 +363,18 @@ check_for_constructor(CPPScope *current_scope, CPPScope *global_scope) {
             param_type = ref_type->_pointing_at->remove_cv();
             param_type = ref_type->_pointing_at->remove_cv();
 
 
             if (class_name == param_type->get_simple_name()) {
             if (class_name == param_type->get_simple_name()) {
-              if (ref_type->_value_category == CPPReferenceType::VC_rvalue) {
-                flags |= CPPFunctionType::F_move_constructor;
+              if (flags & CPPFunctionType::F_constructor) {
+                if (ref_type->_value_category == CPPReferenceType::VC_rvalue) {
+                  flags |= CPPFunctionType::F_move_constructor;
+                } else {
+                  flags |= CPPFunctionType::F_copy_constructor;
+                }
               } else {
               } else {
-                flags |= CPPFunctionType::F_copy_constructor;
+                if (ref_type->_value_category == CPPReferenceType::VC_rvalue) {
+                  flags |= CPPFunctionType::F_move_assignment_operator;
+                } else {
+                  flags |= CPPFunctionType::F_copy_assignment_operator;
+                }
               }
               }
             }
             }
           }
           }

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

@@ -18,37 +18,19 @@
  *
  *
  */
  */
 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),
+  _insert_function(nullptr),
+  _get_key_function(nullptr)
 {
 {
   _ident->_native_scope = current_scope;
   _ident->_native_scope = current_scope;
 }
 }
@@ -116,6 +98,10 @@ output(ostream &out, int indent_level, CPPScope *scope, bool complete) const {
     out << ", " << _del_function->_name;
     out << ", " << _del_function->_name;
   }
   }
 
 
+  if (_insert_function != NULL) {
+    out << ", " << _insert_function->_name;
+  }
+
   out << ");";
   out << ");";
 }
 }
 
 

+ 65 - 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,14 +102,15 @@ 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;
   CPPFunctionGroup *_set_function;
   CPPFunctionGroup *_set_function;
   CPPFunctionGroup *_clear_function;
   CPPFunctionGroup *_clear_function;
   CPPFunctionGroup *_del_function;
   CPPFunctionGroup *_del_function;
+  CPPFunctionGroup *_insert_function;
+  CPPFunctionGroup *_get_key_function;
 };
 };
 
 
 #endif
 #endif

+ 8 - 0
dtool/src/cppparser/cppPointerType.cxx

@@ -177,6 +177,14 @@ is_copy_constructible() const {
   return true;
   return true;
 }
 }
 
 
+/**
+ * Returns true if the type is copy-assignable.
+ */
+bool CPPPointerType::
+is_copy_assignable() const {
+  return true;
+}
+
 /**
 /**
  * This is a little more forgiving than is_equal(): it returns true if the
  * This is a little more forgiving than is_equal(): it returns true if the
  * types appear to be referring to the same thing, even if they may have
  * types appear to be referring to the same thing, even if they may have

+ 1 - 0
dtool/src/cppparser/cppPointerType.h

@@ -41,6 +41,7 @@ public:
   virtual bool is_constructible(const CPPType *other) const;
   virtual bool is_constructible(const CPPType *other) const;
   virtual bool is_default_constructible() const;
   virtual bool is_default_constructible() const;
   virtual bool is_copy_constructible() const;
   virtual bool is_copy_constructible() const;
+  virtual bool is_copy_assignable() const;
   virtual bool is_equivalent(const CPPType &other) const;
   virtual bool is_equivalent(const CPPType &other) const;
 
 
   virtual void output(ostream &out, int indent_level, CPPScope *scope,
   virtual void output(ostream &out, int indent_level, CPPScope *scope,

+ 1 - 0
dtool/src/cppparser/cppPreprocessor.cxx

@@ -2641,6 +2641,7 @@ check_keyword(const string &name) {
   if (name == "__is_trivial") return KW_IS_TRIVIAL;
   if (name == "__is_trivial") return KW_IS_TRIVIAL;
   if (name == "__is_union") return KW_IS_UNION;
   if (name == "__is_union") return KW_IS_UNION;
   if (name == "long") return KW_LONG;
   if (name == "long") return KW_LONG;
+  if (name == "__make_map_keys_seq") return KW_MAKE_MAP_KEYS_SEQ;
   if (name == "__make_map_property") return KW_MAKE_MAP_PROPERTY;
   if (name == "__make_map_property") return KW_MAKE_MAP_PROPERTY;
   if (name == "__make_property") return KW_MAKE_PROPERTY;
   if (name == "__make_property") return KW_MAKE_PROPERTY;
   if (name == "__make_property2") return KW_MAKE_PROPERTY2;
   if (name == "__make_property2") return KW_MAKE_PROPERTY2;

+ 8 - 0
dtool/src/cppparser/cppSimpleType.cxx

@@ -103,6 +103,14 @@ is_copy_constructible() const {
   return (_type != T_void);
   return (_type != T_void);
 }
 }
 
 
+/**
+ * Returns true if the type is copy-assignable.
+ */
+bool CPPSimpleType::
+is_copy_assignable() const {
+  return (_type != T_void);
+}
+
 /**
 /**
  * Returns true if the type is destructible.
  * Returns true if the type is destructible.
  */
  */

+ 1 - 0
dtool/src/cppparser/cppSimpleType.h

@@ -75,6 +75,7 @@ public:
   virtual bool is_constructible(const CPPType *type) const;
   virtual bool is_constructible(const CPPType *type) const;
   virtual bool is_default_constructible() const;
   virtual bool is_default_constructible() const;
   virtual bool is_copy_constructible() const;
   virtual bool is_copy_constructible() const;
+  virtual bool is_copy_assignable() const;
   virtual bool is_destructible() const;
   virtual bool is_destructible() const;
   virtual bool is_parameter_expr() const;
   virtual bool is_parameter_expr() const;
 
 

+ 176 - 0
dtool/src/cppparser/cppStructType.cxx

@@ -435,6 +435,17 @@ is_copy_constructible() const {
   return is_copy_constructible(V_public);
   return is_copy_constructible(V_public);
 }
 }
 
 
+/**
+ * Returns true if the type is copy-assignable.
+ */
+bool CPPStructType::
+is_copy_assignable() const {
+  if (is_abstract()) {
+    return false;
+  }
+  return is_copy_assignable(V_public);
+}
+
 /**
 /**
  * Returns true if the type is destructible.
  * Returns true if the type is destructible.
  */
  */
@@ -606,6 +617,97 @@ is_move_constructible(CPPVisibility min_vis) const {
   return is_copy_constructible(min_vis);
   return is_copy_constructible(min_vis);
 }
 }
 
 
+/**
+ * Returns true if the type is copy-assignable, without checking whether the
+ * class is abstract.
+ */
+bool CPPStructType::
+is_copy_assignable(CPPVisibility min_vis) const {
+  CPPInstance *assignment_operator = get_copy_assignment_operator();
+  if (assignment_operator != (CPPInstance *)NULL) {
+    // It has a copy assignment operator.
+    if (assignment_operator->_vis > min_vis) {
+      // Inaccessible copy assignment operator.
+      return false;
+    }
+
+    if (assignment_operator->_storage_class & CPPInstance::SC_deleted) {
+      // Deleted copy assignment operator.
+      return false;
+    }
+
+    // NB: if it's defaulted, it may still be deleted.
+    if ((assignment_operator->_storage_class & CPPInstance::SC_defaulted) == 0) {
+      return true;
+    }
+  }
+
+  // Implicit copy assignment operator.  Check if the implicit or defaulted
+  // copy assignment operator is deleted.
+  if (!assignment_operator && (get_move_constructor() || get_move_assignment_operator())) {
+    // It's not explicitly defaulted, and there is a move constructor or move
+    // assignment operator, so the implicitly-declared one is deleted.
+    return false;
+  }
+
+  Derivation::const_iterator di;
+  for (di = _derivation.begin(); di != _derivation.end(); ++di) {
+    CPPStructType *base = (*di)._base->as_struct_type();
+    if (base != NULL) {
+      if (!base->is_copy_assignable(V_protected)) {
+        return false;
+      }
+    }
+  }
+
+  // Make sure all members are assignable.
+  CPPScope::Variables::const_iterator vi;
+  for (vi = _scope->_variables.begin(); vi != _scope->_variables.end(); ++vi) {
+    CPPInstance *instance = (*vi).second;
+    assert(instance != NULL);
+
+    if (instance->_storage_class & CPPInstance::SC_static) {
+      // Static members don't count.
+      continue;
+    }
+
+    if (!instance->_type->is_copy_assignable()) {
+      // Const or reference member, can't do it.
+      return false;
+    }
+  }
+
+  return true;
+}
+
+/**
+ * Returns true if the type is move-assignable.
+ */
+bool CPPStructType::
+is_move_assignable(CPPVisibility min_vis) const {
+  CPPInstance *assignment_operator = get_move_assignment_operator();
+  if (assignment_operator != (CPPInstance *)NULL) {
+    // It has a user-declared move assignment_operator.
+    if (assignment_operator->_vis > min_vis) {
+      // Inaccessible move assignment_operator.
+      return false;
+    }
+
+    if (assignment_operator->_storage_class & CPPInstance::SC_deleted) {
+      // It is deleted.
+      return false;
+    }
+
+    if (is_abstract()) {
+      return false;
+    }
+
+    return true;
+  }
+
+  return is_copy_assignable(min_vis);
+}
+
 /**
 /**
  * Returns true if the type is destructible.
  * Returns true if the type is destructible.
  */
  */
@@ -886,6 +988,80 @@ get_move_constructor() const {
   return (CPPInstance *)NULL;
   return (CPPInstance *)NULL;
 }
 }
 
 
+/**
+ * Returns the assignment operator defined for the struct type, if any, or
+ * NULL if no assignment operator is found.
+ */
+CPPFunctionGroup *CPPStructType::
+get_assignment_operator() const {
+  // Just look for the function with the name "operator ="
+  CPPScope::Functions::const_iterator fi;
+  fi = _scope->_functions.find("operator =");
+  if (fi != _scope->_functions.end()) {
+    return fi->second;
+  } else {
+    return (CPPFunctionGroup *)NULL;
+  }
+}
+
+/**
+ * Returns the copy assignment operator defined for the struct type, or NULL
+ * if no user-declared copy assignment operator exists.
+ */
+CPPInstance *CPPStructType::
+get_copy_assignment_operator() const {
+  CPPFunctionGroup *fgroup = get_assignment_operator();
+  if (fgroup == (CPPFunctionGroup *)NULL) {
+    return (CPPInstance *)NULL;
+  }
+
+  CPPFunctionGroup::Instances::const_iterator ii;
+  for (ii = fgroup->_instances.begin();
+       ii != fgroup->_instances.end();
+       ++ii) {
+    CPPInstance *inst = (*ii);
+    assert(inst->_type != (CPPType *)NULL);
+
+    CPPFunctionType *ftype = inst->_type->as_function_type();
+    assert(ftype != (CPPFunctionType *)NULL);
+
+    if ((ftype->_flags & CPPFunctionType::F_copy_assignment_operator) != 0) {
+      return inst;
+    }
+  }
+
+  return (CPPInstance *)NULL;
+}
+
+/**
+ * Returns the move assignment operator defined for the struct type, or NULL
+ * if no user-declared move assignment operator exists.
+ */
+CPPInstance *CPPStructType::
+get_move_assignment_operator() const {
+  CPPFunctionGroup *fgroup = get_assignment_operator();
+  if (fgroup == (CPPFunctionGroup *)NULL) {
+    return (CPPInstance *)NULL;
+  }
+
+  CPPFunctionGroup::Instances::const_iterator ii;
+  for (ii = fgroup->_instances.begin();
+       ii != fgroup->_instances.end();
+       ++ii) {
+    CPPInstance *inst = (*ii);
+    assert(inst->_type != (CPPType *)NULL);
+
+    CPPFunctionType *ftype = inst->_type->as_function_type();
+    assert(ftype != (CPPFunctionType *)NULL);
+
+    if ((ftype->_flags & CPPFunctionType::F_move_assignment_operator) != 0) {
+      return inst;
+    }
+  }
+
+  return (CPPInstance *)NULL;
+}
+
 /**
 /**
  * Returns the destructor defined for the struct type, if any, or NULL if no
  * Returns the destructor defined for the struct type, if any, or NULL if no
  * user-declared destructor is found.
  * user-declared destructor is found.

+ 7 - 1
dtool/src/cppparser/cppStructType.h

@@ -56,10 +56,13 @@ public:
   virtual bool is_constructible(const CPPType *arg_type) const;
   virtual bool is_constructible(const CPPType *arg_type) const;
   virtual bool is_default_constructible() const;
   virtual bool is_default_constructible() const;
   virtual bool is_copy_constructible() const;
   virtual bool is_copy_constructible() const;
+  virtual bool is_copy_assignable() const;
   virtual bool is_destructible() const;
   virtual bool is_destructible() const;
   bool is_default_constructible(CPPVisibility min_vis) const;
   bool is_default_constructible(CPPVisibility min_vis) const;
   bool is_copy_constructible(CPPVisibility min_vis) const;
   bool is_copy_constructible(CPPVisibility min_vis) const;
-  bool is_move_constructible(CPPVisibility min_vis) const;
+  bool is_move_constructible(CPPVisibility min_vis  = V_public) const;
+  bool is_copy_assignable(CPPVisibility min_vis) const;
+  bool is_move_assignable(CPPVisibility min_vis = V_public) const;
   bool is_destructible(CPPVisibility min_vis) const;
   bool is_destructible(CPPVisibility min_vis) const;
   virtual bool is_convertible_to(const CPPType *other) const;
   virtual bool is_convertible_to(const CPPType *other) const;
 
 
@@ -69,6 +72,9 @@ public:
   CPPInstance *get_default_constructor() const;
   CPPInstance *get_default_constructor() const;
   CPPInstance *get_copy_constructor() const;
   CPPInstance *get_copy_constructor() const;
   CPPInstance *get_move_constructor() const;
   CPPInstance *get_move_constructor() const;
+  CPPFunctionGroup *get_assignment_operator() const;
+  CPPInstance *get_copy_assignment_operator() const;
+  CPPInstance *get_move_assignment_operator() const;
   CPPInstance *get_destructor() const;
   CPPInstance *get_destructor() const;
 
 
   virtual CPPDeclaration *
   virtual CPPDeclaration *

+ 8 - 0
dtool/src/cppparser/cppType.cxx

@@ -110,6 +110,14 @@ is_copy_constructible() const {
   return false;
   return false;
 }
 }
 
 
+/**
+ * Returns true if the type is copy-assignable.
+ */
+bool CPPType::
+is_copy_assignable() const {
+  return false;
+}
+
 /**
 /**
  * Returns true if the type is destructible.
  * Returns true if the type is destructible.
  */
  */

+ 1 - 0
dtool/src/cppparser/cppType.h

@@ -51,6 +51,7 @@ public:
   virtual bool is_constructible(const CPPType *type) const;
   virtual bool is_constructible(const CPPType *type) const;
   virtual bool is_default_constructible() const;
   virtual bool is_default_constructible() const;
   virtual bool is_copy_constructible() const;
   virtual bool is_copy_constructible() const;
+  virtual bool is_copy_assignable() const;
   virtual bool is_destructible() const;
   virtual bool is_destructible() const;
   virtual bool is_parameter_expr() const;
   virtual bool is_parameter_expr() const;
 
 

+ 8 - 0
dtool/src/cppparser/cppTypedefType.cxx

@@ -205,6 +205,14 @@ is_copy_constructible() const {
   return _type->is_copy_constructible();
   return _type->is_copy_constructible();
 }
 }
 
 
+/**
+ * Returns true if the type is copy-assignable.
+ */
+bool CPPTypedefType::
+is_copy_assignable() const {
+  return _type->is_copy_assignable();
+}
+
 /**
 /**
  * Returns true if the type is destructible.
  * Returns true if the type is destructible.
  */
  */

+ 1 - 0
dtool/src/cppparser/cppTypedefType.h

@@ -48,6 +48,7 @@ public:
   virtual bool is_constructible(const CPPType *type) const;
   virtual bool is_constructible(const CPPType *type) const;
   virtual bool is_default_constructible() const;
   virtual bool is_default_constructible() const;
   virtual bool is_copy_constructible() const;
   virtual bool is_copy_constructible() const;
+  virtual bool is_copy_assignable() const;
   virtual bool is_destructible() const;
   virtual bool is_destructible() const;
 
 
   virtual bool is_fully_specified() const;
   virtual bool is_fully_specified() const;

+ 13 - 1
dtool/src/dtoolbase/dtoolbase.h

@@ -94,6 +94,14 @@
 #define RETURNS_ALIGNED(x)
 #define RETURNS_ALIGNED(x)
 #endif
 #endif
 
 
+#ifdef __GNUC__
+#define LIKELY(x) __builtin_expect(!!(x), 1)
+#define UNLIKELY(x) __builtin_expect(!!(x), 0)
+#else
+#define LIKELY(x) (x)
+#define UNLIKELY(x) (x)
+#endif
+
 /*
 /*
   include win32 defns for everything up to WinServer2003, and assume
   include win32 defns for everything up to WinServer2003, and assume
   I'm smart enough to use GetProcAddress for backward compat on
   I'm smart enough to use GetProcAddress for backward compat on
@@ -332,7 +340,7 @@ typedef struct _object PyObject;
 
 
 #ifdef __WORDSIZE
 #ifdef __WORDSIZE
 #define NATIVE_WORDSIZE __WORDSIZE
 #define NATIVE_WORDSIZE __WORDSIZE
-#elif defined(_LP64)
+#elif defined(_LP64) || defined(_WIN64)
 #define NATIVE_WORDSIZE 64
 #define NATIVE_WORDSIZE 64
 #else
 #else
 #define NATIVE_WORDSIZE 32
 #define NATIVE_WORDSIZE 32
@@ -456,6 +464,8 @@ 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 MAKE_MAP_KEYS_SEQ(property_name, ...) __make_map_keys_seq(property_name, __VA_ARGS__)
 #define EXTENSION(x) __extension x
 #define EXTENSION(x) __extension x
 #define EXTEND __extension
 #define EXTEND __extension
 #else
 #else
@@ -466,6 +476,8 @@ 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 MAKE_MAP_KEYS_SEQ(property_name, ...)
 #define EXTENSION(x)
 #define EXTENSION(x)
 #define EXTEND
 #define EXTEND
 #endif
 #endif

+ 13 - 16
dtool/src/dtoolbase/dtoolbase_cc.h

@@ -34,9 +34,9 @@ using namespace std;
 #define ALWAYS_INLINE inline
 #define ALWAYS_INLINE inline
 #define TYPENAME typename
 #define TYPENAME typename
 #define CONSTEXPR constexpr
 #define CONSTEXPR constexpr
+#define ALWAYS_INLINE_CONSTEXPR constexpr
 #define NOEXCEPT noexcept
 #define NOEXCEPT noexcept
 #define FINAL final
 #define FINAL final
-#define OVERRIDE override
 #define MOVE(x) x
 #define MOVE(x) x
 #define DEFAULT_CTOR = default
 #define DEFAULT_CTOR = default
 #define DEFAULT_DTOR = default
 #define DEFAULT_DTOR = default
@@ -166,7 +166,6 @@ template<class T> typename remove_reference<T>::type &&move(T &&t) {
 #  endif
 #  endif
 #  if __has_extension(cxx_override_control) && (__cplusplus >= 201103L)
 #  if __has_extension(cxx_override_control) && (__cplusplus >= 201103L)
 #    define FINAL final
 #    define FINAL final
-#    define OVERRIDE override
 #  endif
 #  endif
 #  if __has_extension(cxx_defaulted_functions)
 #  if __has_extension(cxx_defaulted_functions)
 #     define DEFAULT_CTOR = default
 #     define DEFAULT_CTOR = default
@@ -176,13 +175,7 @@ template<class T> typename remove_reference<T>::type &&move(T &&t) {
 #  if __has_extension(cxx_deleted_functions)
 #  if __has_extension(cxx_deleted_functions)
 #     define DELETED = delete
 #     define DELETED = delete
 #  endif
 #  endif
-#elif defined(__GNUC__) && (__cplusplus >= 201103L) // GCC
-
-// GCC defines several macros which we can query.  List of all supported
-// builtin macros: https://gcc.gnu.org/projects/cxx-status.html
-#  if __cpp_constexpr >= 200704
-#    define CONSTEXPR constexpr
-#  endif
+#elif defined(__GNUC__) // GCC
 
 
 // Starting at GCC 4.4
 // Starting at GCC 4.4
 #  if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 4)
 #  if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 4)
@@ -194,15 +187,21 @@ template<class T> typename remove_reference<T>::type &&move(T &&t) {
 
 
 // Starting at GCC 4.6
 // Starting at GCC 4.6
 #  if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
 #  if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
+#    define CONSTEXPR constexpr
 #    define NOEXCEPT noexcept
 #    define NOEXCEPT noexcept
 #    define USE_MOVE_SEMANTICS
 #    define USE_MOVE_SEMANTICS
-#    define FINAL final
 #    define MOVE(x) move(x)
 #    define MOVE(x) move(x)
 #  endif
 #  endif
 
 
 // Starting at GCC 4.7
 // Starting at GCC 4.7
 #  if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)
 #  if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)
-#    define OVERRIDE override
+#    define FINAL final
+#  endif
+
+// GCC defines several macros which we can query.  List of all supported
+// builtin macros: https://gcc.gnu.org/projects/cxx-status.html
+#  if !defined(CONSTEXPR) && __cpp_constexpr >= 200704
+#    define CONSTEXPR constexpr
 #  endif
 #  endif
 
 
 #elif defined(_MSC_VER) && _MSC_VER >= 1900 // Visual Studio 2015
 #elif defined(_MSC_VER) && _MSC_VER >= 1900 // Visual Studio 2015
@@ -210,11 +209,9 @@ template<class T> typename remove_reference<T>::type &&move(T &&t) {
 #  define NOEXCEPT noexcept
 #  define NOEXCEPT noexcept
 #  define USE_MOVE_SEMANTICS
 #  define USE_MOVE_SEMANTICS
 #  define FINAL final
 #  define FINAL final
-#  define OVERRIDE override
 #  define MOVE(x) move(x)
 #  define MOVE(x) move(x)
 #elif defined(_MSC_VER) && _MSC_VER >= 1600 // Visual Studio 2010
 #elif defined(_MSC_VER) && _MSC_VER >= 1600 // Visual Studio 2010
 #  define NOEXCEPT throw()
 #  define NOEXCEPT throw()
-#  define OVERRIDE override
 #  define USE_MOVE_SEMANTICS
 #  define USE_MOVE_SEMANTICS
 #  define FINAL sealed
 #  define FINAL sealed
 #  define MOVE(x) move(x)
 #  define MOVE(x) move(x)
@@ -230,6 +227,9 @@ template<class T> typename remove_reference<T>::type &&move(T &&t) {
 // Fallbacks if features are not supported
 // Fallbacks if features are not supported
 #ifndef CONSTEXPR
 #ifndef CONSTEXPR
 #  define CONSTEXPR INLINE
 #  define CONSTEXPR INLINE
+#  define ALWAYS_INLINE_CONSTEXPR ALWAYS_INLINE
+#else
+#  define ALWAYS_INLINE_CONSTEXPR ALWAYS_INLINE CONSTEXPR
 #endif
 #endif
 #ifndef NOEXCEPT
 #ifndef NOEXCEPT
 #  define NOEXCEPT
 #  define NOEXCEPT
@@ -240,9 +240,6 @@ template<class T> typename remove_reference<T>::type &&move(T &&t) {
 #ifndef FINAL
 #ifndef FINAL
 #  define FINAL
 #  define FINAL
 #endif
 #endif
-#ifndef OVERRIDE
-#  define OVERRIDE
-#endif
 #ifndef DEFAULT_CTOR
 #ifndef DEFAULT_CTOR
 #  define DEFAULT_CTOR {}
 #  define DEFAULT_CTOR {}
 #endif
 #endif

+ 14 - 17
dtool/src/dtoolbase/memoryHook.cxx

@@ -47,7 +47,9 @@ static_assert(MEMORY_HOOK_ALIGNMENT * 8 >= NATIVE_WORDSIZE,
 static_assert((MEMORY_HOOK_ALIGNMENT & (MEMORY_HOOK_ALIGNMENT - 1)) == 0,
 static_assert((MEMORY_HOOK_ALIGNMENT & (MEMORY_HOOK_ALIGNMENT - 1)) == 0,
               "MEMORY_HOOK_ALIGNMENT should be a power of two");
               "MEMORY_HOOK_ALIGNMENT should be a power of two");
 
 
-#if defined(USE_MEMORY_DLMALLOC)
+#if defined(CPPPARSER)
+
+#elif defined(USE_MEMORY_DLMALLOC)
 
 
 // Memory manager: DLMALLOC This is Doug Lea's memory manager.  It is very
 // Memory manager: DLMALLOC This is Doug Lea's memory manager.  It is very
 // fast, but it is not thread-safe.  However, we provide thread locking within
 // fast, but it is not thread-safe.  However, we provide thread locking within
@@ -202,13 +204,11 @@ MemoryHook() {
 
 
 #endif  // WIN32
 #endif  // WIN32
 
 
-#ifdef DO_MEMORY_USAGE
   _total_heap_single_size = 0;
   _total_heap_single_size = 0;
   _total_heap_array_size = 0;
   _total_heap_array_size = 0;
   _requested_heap_size = 0;
   _requested_heap_size = 0;
   _total_mmap_size = 0;
   _total_mmap_size = 0;
   _max_heap_size = ~(size_t)0;
   _max_heap_size = ~(size_t)0;
-#endif
 }
 }
 
 
 /**
 /**
@@ -216,19 +216,16 @@ MemoryHook() {
  */
  */
 MemoryHook::
 MemoryHook::
 MemoryHook(const MemoryHook &copy) :
 MemoryHook(const MemoryHook &copy) :
-  _page_size(copy._page_size)
-{
-#ifdef DO_MEMORY_USAGE
-  _total_heap_single_size = copy._total_heap_single_size;
-  _total_heap_array_size = copy._total_heap_array_size;
-  _requested_heap_size = copy._requested_heap_size;
-  _total_mmap_size = copy._total_mmap_size;
-  _max_heap_size = copy._max_heap_size;
-#endif
-
-  ((MutexImpl &)copy._lock).acquire();
+  _page_size(copy._page_size),
+  _total_heap_single_size(copy._total_heap_single_size),
+  _total_heap_array_size(copy._total_heap_array_size),
+  _requested_heap_size(copy._requested_heap_size),
+  _total_mmap_size(copy._total_mmap_size),
+  _max_heap_size(copy._max_heap_size) {
+
+  copy._lock.acquire();
   _deleted_chains = copy._deleted_chains;
   _deleted_chains = copy._deleted_chains;
-  ((MutexImpl &)copy._lock).release();
+  copy._lock.release();
 }
 }
 
 
 /**
 /**
@@ -631,7 +628,6 @@ alloc_fail(size_t attempted_size) {
   abort();
   abort();
 }
 }
 
 
-#ifdef DO_MEMORY_USAGE
 /**
 /**
  * This callback method is called whenever the total allocated heap size
  * This callback method is called whenever the total allocated heap size
  * exceeds _max_heap_size.  It's mainly intended for reporting memory leaks,
  * exceeds _max_heap_size.  It's mainly intended for reporting memory leaks,
@@ -642,6 +638,7 @@ alloc_fail(size_t attempted_size) {
  */
  */
 void MemoryHook::
 void MemoryHook::
 overflow_heap_size() {
 overflow_heap_size() {
+#ifdef DO_MEMORY_USAGE
   _max_heap_size = ~(size_t)0;
   _max_heap_size = ~(size_t)0;
-}
 #endif  // DO_MEMORY_USAGE
 #endif  // DO_MEMORY_USAGE
+}

+ 1 - 3
dtool/src/dtoolbase/memoryHook.h

@@ -67,7 +67,6 @@ public:
 
 
   INLINE static size_t get_ptr_size(void *ptr);
   INLINE static size_t get_ptr_size(void *ptr);
 
 
-#ifdef DO_MEMORY_USAGE
 protected:
 protected:
   TVOLATILE AtomicAdjust::Integer _total_heap_single_size;
   TVOLATILE AtomicAdjust::Integer _total_heap_single_size;
   TVOLATILE AtomicAdjust::Integer _total_heap_array_size;
   TVOLATILE AtomicAdjust::Integer _total_heap_array_size;
@@ -79,7 +78,6 @@ protected:
   size_t _max_heap_size;
   size_t _max_heap_size;
 
 
   virtual void overflow_heap_size();
   virtual void overflow_heap_size();
-#endif  // DO_MEMORY_USAGE
 
 
 private:
 private:
   size_t _page_size;
   size_t _page_size;
@@ -87,7 +85,7 @@ private:
   typedef map<size_t, DeletedBufferChain *> DeletedChains;
   typedef map<size_t, DeletedBufferChain *> DeletedChains;
   DeletedChains _deleted_chains;
   DeletedChains _deleted_chains;
 
 
-  MutexImpl _lock;
+  mutable MutexImpl _lock;
 };
 };
 
 
 #include "memoryHook.I"
 #include "memoryHook.I"

+ 0 - 2
dtool/src/dtoolbase/typeRegistryNode.cxx

@@ -26,9 +26,7 @@ TypeRegistryNode(TypeHandle handle, const string &name, TypeHandle &ref) :
   _handle(handle), _name(name), _ref(ref)
   _handle(handle), _name(name), _ref(ref)
 {
 {
   clear_subtree();
   clear_subtree();
-#ifdef DO_MEMORY_USAGE
   memset(_memory_usage, 0, sizeof(_memory_usage));
   memset(_memory_usage, 0, sizeof(_memory_usage));
-#endif
 }
 }
 
 
 /**
 /**

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

@@ -47,9 +47,7 @@ public:
   Classes _parent_classes;
   Classes _parent_classes;
   Classes _child_classes;
   Classes _child_classes;
 
 
-#ifdef DO_MEMORY_USAGE
   AtomicAdjust::Integer _memory_usage[TypeHandle::MC_limit];
   AtomicAdjust::Integer _memory_usage[TypeHandle::MC_limit];
-#endif
 
 
   static bool _paranoid_inheritance;
   static bool _paranoid_inheritance;
 
 

+ 16 - 0
dtool/src/dtoolutil/executionEnvironment.cxx

@@ -341,6 +341,22 @@ ns_get_environment_variable(const string &var) const {
     }
     }
   }
   }
 
 
+#elif !defined(__APPLE__)
+  // Similarly, we define fallbacks on POSIX systems for the variables defined
+  // in the XDG Base Directory specification, so that they can be safely used
+  // in Config.prc files.
+  if (var == "XDG_CONFIG_HOME") {
+    Filename home_dir = Filename::get_home_directory();
+    return home_dir.get_fullpath() + "/.config";
+
+  } else if (var == "XDG_CACHE_HOME") {
+    Filename home_dir = Filename::get_home_directory();
+    return home_dir.get_fullpath() + "/.cache";
+
+  } else if (var == "XDG_DATA_HOME") {
+    Filename home_dir = Filename::get_home_directory();
+    return home_dir.get_fullpath() + "/.local/share";
+  }
 #endif // _WIN32
 #endif // _WIN32
 
 
   return string();
   return string();

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

@@ -51,6 +51,15 @@ 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_PROPERTY(binary_name, get_binary_name, set_binary_name);
+  MAKE_PROPERTY(dtool_name, get_dtool_name, set_dtool_name);
+  MAKE_PROPERTY(cwd, get_cwd);
+
 private:
 private:
   bool ns_has_environment_variable(const string &var) const;
   bool ns_has_environment_variable(const string &var) const;
   string ns_get_environment_variable(const string &var) const;
   string ns_get_environment_variable(const string &var) const;

+ 11 - 4
dtool/src/dtoolutil/filename.cxx

@@ -600,8 +600,14 @@ get_user_appdata_directory() {
     user_appdata_directory.set_basename("files");
     user_appdata_directory.set_basename("files");
 
 
 #else
 #else
-    // Posix case.
-    user_appdata_directory = get_home_directory();
+    // Posix case.  We follow the XDG base directory spec.
+    struct stat st;
+    const char *datadir = getenv("XDG_DATA_HOME");
+    if (datadir != nullptr && stat(datadir, &st) == 0 && S_ISDIR(st.st_mode)) {
+      user_appdata_directory = datadir;
+    } else {
+      user_appdata_directory = Filename(get_home_directory(), ".local/share");
+    }
 
 
 #endif  // WIN32
 #endif  // WIN32
 
 
@@ -649,9 +655,10 @@ get_common_appdata_directory() {
     common_appdata_directory.set_dirname(_internal_data_dir);
     common_appdata_directory.set_dirname(_internal_data_dir);
     common_appdata_directory.set_basename("files");
     common_appdata_directory.set_basename("files");
 
 
+#elif defined(__FreeBSD__)
+    common_appdata_directory = "/usr/local/share";
 #else
 #else
-    // Posix case.
-    common_appdata_directory = "/var";
+    common_appdata_directory = "/usr/share";
 #endif  // WIN32
 #endif  // WIN32
 
 
     if (common_appdata_directory.empty()) {
     if (common_appdata_directory.empty()) {

+ 1 - 1
dtool/src/dtoolutil/filename_ext.cxx

@@ -55,7 +55,7 @@ __init__(PyObject *path) {
 
 
   if (Py_TYPE(path) == &Dtool_Filename._PyType) {
   if (Py_TYPE(path) == &Dtool_Filename._PyType) {
     // Copy constructor.
     // Copy constructor.
-    (*_this) = *((Filename *)((Dtool_PyInstDef *)path)->_ptr_to_object);
+    *_this = *(Filename *)DtoolInstance_VOID_PTR(path);
     return;
     return;
   }
   }
 
 

+ 5 - 5
dtool/src/dtoolutil/load_dso.cxx

@@ -47,20 +47,20 @@ load_dso(const DSearchPath &path, const Filename &filename) {
   if (!abspath.is_regular_file()) {
   if (!abspath.is_regular_file()) {
     return NULL;
     return NULL;
   }
   }
-  string os_specific = abspath.to_os_specific();
+  wstring os_specific_w = abspath.to_os_specific_w();
 
 
   // Try using LoadLibraryEx, if possible.
   // Try using LoadLibraryEx, if possible.
-  typedef HMODULE (WINAPI *tLoadLibraryEx)(LPCTSTR, HANDLE, DWORD);
+  typedef HMODULE (WINAPI *tLoadLibraryEx)(LPCWSTR, HANDLE, DWORD);
   tLoadLibraryEx pLoadLibraryEx;
   tLoadLibraryEx pLoadLibraryEx;
   HINSTANCE hLib = LoadLibrary("kernel32.dll");
   HINSTANCE hLib = LoadLibrary("kernel32.dll");
   if (hLib) {
   if (hLib) {
-    pLoadLibraryEx = (tLoadLibraryEx)GetProcAddress(hLib, "LoadLibraryExA");
+    pLoadLibraryEx = (tLoadLibraryEx)GetProcAddress(hLib, "LoadLibraryExW");
     if (pLoadLibraryEx) {
     if (pLoadLibraryEx) {
-      return pLoadLibraryEx(os_specific.c_str(), NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
+      return pLoadLibraryEx(os_specific_w.c_str(), NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
     }
     }
   }
   }
 
 
-  return LoadLibrary(os_specific.c_str());
+  return LoadLibraryW(os_specific_w.c_str());
 }
 }
 
 
 bool
 bool

+ 3 - 3
dtool/src/dtoolutil/pandaFileStream.h

@@ -29,7 +29,7 @@
 class EXPCL_DTOOL IFileStream : public istream {
 class EXPCL_DTOOL IFileStream : public istream {
 PUBLISHED:
 PUBLISHED:
   INLINE IFileStream();
   INLINE IFileStream();
-  INLINE IFileStream(const char *filename, ios::openmode mode = ios::in);
+  INLINE explicit IFileStream(const char *filename, ios::openmode mode = ios::in);
   INLINE ~IFileStream();
   INLINE ~IFileStream();
 
 
   INLINE void open(const char *filename, ios::openmode mode = ios::in);
   INLINE void open(const char *filename, ios::openmode mode = ios::in);
@@ -57,7 +57,7 @@ private:
 class EXPCL_DTOOL OFileStream : public ostream {
 class EXPCL_DTOOL OFileStream : public ostream {
 PUBLISHED:
 PUBLISHED:
   INLINE OFileStream();
   INLINE OFileStream();
-  INLINE OFileStream(const char *filename, ios::openmode mode = ios::out);
+  INLINE explicit OFileStream(const char *filename, ios::openmode mode = ios::out);
   INLINE ~OFileStream();
   INLINE ~OFileStream();
 
 
   INLINE void open(const char *filename, ios::openmode mode = ios::out);
   INLINE void open(const char *filename, ios::openmode mode = ios::out);
@@ -86,7 +86,7 @@ private:
 class EXPCL_DTOOL FileStream : public iostream {
 class EXPCL_DTOOL FileStream : public iostream {
 PUBLISHED:
 PUBLISHED:
   INLINE FileStream();
   INLINE FileStream();
-  INLINE FileStream(const char *filename, ios::openmode mode = ios::in);
+  INLINE explicit FileStream(const char *filename, ios::openmode mode = ios::in);
   INLINE ~FileStream();
   INLINE ~FileStream();
 
 
   INLINE void open(const char *filename, ios::openmode mode = ios::in);
   INLINE void open(const char *filename, ios::openmode mode = ios::in);

+ 15 - 0
dtool/src/dtoolutil/pandaSystem.h

@@ -48,6 +48,21 @@ PUBLISHED:
 
 
   static string get_platform();
   static string get_platform();
 
 
+  MAKE_PROPERTY(version_string, get_version_string);
+  MAKE_PROPERTY(major_version, get_major_version);
+  MAKE_PROPERTY(minor_version, get_minor_version);
+  MAKE_PROPERTY(sequence_version, get_sequence_version);
+  MAKE_PROPERTY(official_version, is_official_version);
+
+  MAKE_PROPERTY(memory_alignment, get_memory_alignment);
+
+  MAKE_PROPERTY(distributor, get_distributor);
+  MAKE_PROPERTY(compiler, get_compiler);
+  MAKE_PROPERTY(build_date, get_build_date);
+  MAKE_PROPERTY(git_commit, get_git_commit);
+
+  MAKE_PROPERTY(platform, get_platform);
+
   bool has_system(const string &system) const;
   bool has_system(const string &system) const;
   size_t get_num_systems() const;
   size_t get_num_systems() const;
   string get_system(size_t n) const;
   string get_system(size_t n) const;

+ 1 - 0
dtool/src/dtoolutil/textEncoder.h

@@ -46,6 +46,7 @@ PUBLISHED:
 
 
   INLINE static void set_default_encoding(Encoding encoding);
   INLINE static void set_default_encoding(Encoding encoding);
   INLINE static Encoding get_default_encoding();
   INLINE static Encoding get_default_encoding();
+  MAKE_PROPERTY(default_encoding, get_default_encoding, set_default_encoding);
 
 
   INLINE void set_text(const string &text);
   INLINE void set_text(const string &text);
   INLINE void set_text(const string &text, Encoding encoding);
   INLINE void set_text(const string &text, Encoding encoding);

+ 28 - 0
dtool/src/interrogate/README.md

@@ -0,0 +1,28 @@
+A key advantage of Panda3D is that it provides developers with the
+ability to use both C++ and Python simultaneously. Essentially, Panda3D
+gives programmers the best of both worlds, as they are able to take
+advantage of the high performance and low-level programming found in
+C++ in addition to the flexibility, interactive scripting, and
+rapid-prototyping capabilities of Python. This feature is made possible
+due to Python’s ability to call C libraries, and ultimately make use of
+Panda3D’s Interrogate System: an automated C++ Extension Module
+generation utility similar to SWIG.  Although Python is the favored
+scripting language of Panda3D, the engine is highly extensible in this
+aspect, as any language that has a foreign function interface can make
+use of the Interrogate System.
+
+The Interrogate System works like a compiler by scanning and parsing
+C++ code for the Panda3D-specific, “PUBLISHED” keyword. This keyword
+marks the particular methods of a class that are to be exposed within a
+C++ Extension Module for that class which is eventually generated. One
+benefit of using the “PUBLISHED” keyword is that it alleviates the need
+for an interface file that provides function prototypes for the class
+methods that will be exposed within the extension module, as is the
+case with SWIG. Interrogate turns a class into a loose collection of
+Python interface wrapper functions that make up the C++ Extension
+Module.
+
+This package depends on the 'cppparser' package, which contains the
+code that parses the C++ headers, and the 'interrogatedb' package,
+which contains the intermediate representation of the interfaces that
+can be saved to a database file for FFI generation tools to consume.

+ 17 - 11
dtool/src/interrogate/functionRemap.cxx

@@ -772,16 +772,14 @@ setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface_mak
     first_param = 1;
     first_param = 1;
   }
   }
 
 
-  if (_has_this || _type == T_constructor) {
-    if (_parameters.size() > (size_t)first_param && _parameters[first_param]._name == "self" &&
-        TypeManager::is_pointer_to_PyObject(_parameters[first_param]._remap->get_orig_type())) {
-      // Here's a special case.  If the first parameter of a nonstatic method
-      // is a PyObject * called "self", then we will automatically fill it in
-      // from the this pointer, and remove it from the generated parameter
-      // list.
-      _parameters.erase(_parameters.begin() + first_param);
-      _flags |= F_explicit_self;
-    }
+  if (_parameters.size() > (size_t)first_param && _parameters[first_param]._name == "self" &&
+      TypeManager::is_pointer_to_PyObject(_parameters[first_param]._remap->get_orig_type())) {
+    // Here's a special case.  If the first parameter of a nonstatic method
+    // is a PyObject * called "self", then we will automatically fill it in
+    // from the this pointer, and remove it from the generated parameter
+    // list.
+    _parameters.erase(_parameters.begin() + first_param);
+    _flags |= F_explicit_self;
   }
   }
 
 
   if ((int)_parameters.size() == first_param) {
   if ((int)_parameters.size() == first_param) {
@@ -799,6 +797,7 @@ setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface_mak
         (_parameters[first_param + 1]._name == "kwargs" ||
         (_parameters[first_param + 1]._name == "kwargs" ||
           _parameters[first_param + 1]._name == "kwds")) {
           _parameters[first_param + 1]._name == "kwds")) {
       _flags |= F_explicit_args;
       _flags |= F_explicit_args;
+      _args_type = InterfaceMaker::AT_keyword_args;
     }
     }
   }
   }
 
 
@@ -848,7 +847,7 @@ setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface_mak
       }
       }
 
 
     } else if (fname == "__iter__") {
     } else if (fname == "__iter__") {
-      if (_has_this && _parameters.size() == 1 &&
+      if ((int)_parameters.size() == first_param &&
           TypeManager::is_pointer(_return_type->get_new_type())) {
           TypeManager::is_pointer(_return_type->get_new_type())) {
         // It receives no parameters, and returns a pointer.
         // It receives no parameters, and returns a pointer.
         _flags |= F_iter;
         _flags |= F_iter;
@@ -911,6 +910,13 @@ setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface_mak
             break;
             break;
           }
           }
         }
         }
+      } else if (_args_type == InterfaceMaker::AT_single_arg) {
+        // If it takes an argument named "args", we are directly passing the
+        // "args" tuple to the function.
+        if (_parameters[first_param]._name == "args") {
+          _flags |= F_explicit_args;
+          _args_type = InterfaceMaker::AT_varargs;
+        }
       }
       }
     }
     }
     break;
     break;

+ 10 - 9
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,13 @@ 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),
+  _inserter(nullptr),
+  _getkey_function(nullptr),
+  _has_this(false)
 {
 {
 }
 }
 
 
@@ -615,7 +616,7 @@ record_function(const InterrogateType &itype, FunctionIndex func_index) {
 
 
           // If *any* of the variants of this function has a "this" pointer,
           // If *any* of the variants of this function has a "this" pointer,
           // the entire set of functions is deemed to have a "this" pointer.
           // the entire set of functions is deemed to have a "this" pointer.
-          if (remap->_has_this) {
+          if (remap->_has_this || (remap->_flags & FunctionRemap::F_explicit_self) != 0) {
             func->_has_this = true;
             func->_has_this = true;
           }
           }
 
 

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

@@ -126,12 +126,15 @@ 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;
+    Function *_inserter;
+    Function *_getkey_function;
+    bool _has_this;
   };
   };
   typedef vector<Property *> Properties;
   typedef vector<Property *> Properties;
 
 

+ 1 - 1
dtool/src/interrogate/interfaceMakerPython.cxx

@@ -51,7 +51,7 @@ test_assert(ostream &out, int indent_level) const {
     indent(out, indent_level)
     indent(out, indent_level)
       << "Notify *notify = Notify::ptr();\n";
       << "Notify *notify = Notify::ptr();\n";
     indent(out, indent_level)
     indent(out, indent_level)
-      << "if (notify->has_assert_failed()) {\n";
+      << "if (UNLIKELY(notify->has_assert_failed())) {\n";
     indent(out, indent_level + 2)
     indent(out, indent_level + 2)
       << "PyErr_SetString(PyExc_AssertionError, notify->get_assert_error_message().c_str());\n";
       << "PyErr_SetString(PyExc_AssertionError, notify->get_assert_error_message().c_str());\n";
     indent(out, indent_level + 2)
     indent(out, indent_level + 2)

File diff suppressed because it is too large
+ 458 - 275
dtool/src/interrogate/interfaceMakerPythonNative.cxx


+ 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 {

+ 169 - 37
dtool/src/interrogate/interrogateBuilder.cxx

@@ -1798,30 +1798,39 @@ 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;
   CPPFunctionGroup *fgroup = make_property->_length_function;
-  if (fgroup != NULL) {
-    is_seq = true;
-
+  if (fgroup != nullptr) {
+    CPPFunctionGroup::Instances::const_iterator fi;
     for (fi = fgroup->_instances.begin(); fi != fgroup->_instances.end(); ++fi) {
     for (fi = fgroup->_instances.begin(); fi != fgroup->_instances.end(); ++fi) {
       CPPInstance *function = (*fi);
       CPPInstance *function = (*fi);
-      CPPFunctionType *ftype =
-        function->_type->as_function_type();
-      if (ftype != NULL) {
+      CPPFunctionType *ftype = function->_type->as_function_type();
+      if (ftype != nullptr) {
         length_function = get_function(function, "", struct_type,
         length_function = get_function(function, "", struct_type,
                                        struct_type->get_scope(), 0);
                                        struct_type->get_scope(), 0);
         if (length_function != 0) {
         if (length_function != 0) {
@@ -1840,6 +1849,9 @@ get_make_property(CPPMakeProperty *make_property, CPPStructType *struct_type, CP
   CPPInstance *getter = NULL;
   CPPInstance *getter = NULL;
   CPPType *return_type = NULL;
   CPPType *return_type = NULL;
 
 
+  // How many arguments we expect the getter to have.
+  size_t num_args = (size_t)(make_property->_type != CPPMakeProperty::T_normal);
+
   fgroup = make_property->_get_function;
   fgroup = make_property->_get_function;
   if (fgroup != NULL) {
   if (fgroup != NULL) {
     CPPFunctionGroup::Instances::const_iterator fi;
     CPPFunctionGroup::Instances::const_iterator fi;
@@ -1850,12 +1862,29 @@ get_make_property(CPPMakeProperty *make_property, CPPStructType *struct_type, CP
         continue;
         continue;
       }
       }
 
 
+      const CPPParameterList::Parameters &params = ftype->_parameters->_parameters;
+
+      size_t expected_num_args = 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" &&
+          TypeManager::is_pointer_to_PyObject(params[0]->_type)) {
+        // Taking a PyObject *self argument.
+        expected_num_args += 1;
+        index_arg += 1;
+      }
+
       // The getter must either take no arguments, or all defaults.
       // The getter must either take no arguments, or all defaults.
-      if (ftype->_parameters->_parameters.size() == (size_t)is_seq ||
-          (ftype->_parameters->_parameters.size() > (size_t)is_seq &&
-           ftype->_parameters->_parameters[(size_t)is_seq]->_initializer != NULL)) {
+      if (params.size() == expected_num_args ||
+          (params.size() > expected_num_args &&
+           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(ftype->_parameters->_parameters[0]->_type)) {
+        if (make_property->_type == CPPMakeProperty::T_sequence &&
+            !TypeManager::is_integer(params[index_arg]->_type)) {
           continue;
           continue;
         }
         }
 
 
@@ -1887,13 +1916,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;
@@ -1909,44 +1939,127 @@ 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) {
-        deleter = function;
-        break;
+      if (ftype != nullptr) {
+        const CPPParameterList::Parameters &params = ftype->_parameters->_parameters;
+        if (params.size() == num_args ||
+            (params.size() > num_args && params[num_args]->_initializer != nullptr)) {
+          deleter = function;
+          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;
+  // And the "inserter".
+  CPPInstance *inserter = nullptr;
+
+  fgroup = make_property->_insert_function;
+  if (fgroup != nullptr) {
+    CPPFunctionGroup::Instances::const_iterator fi;
+    for (fi = fgroup->_instances.begin(); fi != fgroup->_instances.end(); ++fi) {
+      CPPInstance *function = (*fi);
+      CPPFunctionType *ftype = function->_type->as_function_type();
+      if (ftype != nullptr && ftype->_parameters->_parameters.size() == 2) {
+        inserter = function;
+        break;
+      }
+    }
+
+    if (inserter == nullptr) {
+      cerr << "No instance of insert-function '"
+           << fgroup->_name << "' is suitable!\n";
+      return 0;
+    }
+  }
+
+  // And the function that returns a key by index.
+  CPPInstance *getkey_function = nullptr;
 
 
-  InterrogateElement iproperty;
-  iproperty._name = make_property->get_simple_name();
-  iproperty._scoped_name = descope(make_property->get_local_name(&parser));
+  fgroup = make_property->_get_key_function;
+  if (fgroup != nullptr) {
+    CPPFunctionGroup::Instances::const_iterator fi;
+    for (fi = fgroup->_instances.begin(); fi != fgroup->_instances.end(); ++fi) {
+      CPPInstance *function = (*fi);
+      CPPFunctionType *ftype = function->_type->as_function_type();
+      if (ftype != nullptr) {
+        getkey_function = function;
+        break;
+      }
+    }
+
+    if (getkey_function == nullptr) {
+      cerr << "No instance of get-key-function '"
+           << fgroup->_name << "' is suitable!\n";
+      return 0;
+    }
+  }
+
+  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 = 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;
+    assert(length_function != 0);
   }
   }
 
 
-  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;
+    iproperty._length_function = length_function;
+  }
+
+  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) {
@@ -1963,6 +2076,20 @@ get_make_property(CPPMakeProperty *make_property, CPPStructType *struct_type, CP
     nassertr(iproperty._del_function, 0);
     nassertr(iproperty._del_function, 0);
   }
   }
 
 
+  if (inserter != NULL) {
+    iproperty._flags |= InterrogateElement::F_has_insert_function;
+    iproperty._insert_function = get_function(inserter, "", struct_type,
+                                              struct_type->get_scope(), 0);
+    nassertr(iproperty._insert_function, 0);
+  }
+
+  if (getkey_function != NULL) {
+    iproperty._flags |= InterrogateElement::F_has_getkey_function;
+    iproperty._getkey_function = get_function(getkey_function, "", struct_type,
+                                              struct_type->get_scope(), 0);
+    nassertr(iproperty._getkey_function, 0);
+  }
+
   // See if there happens to be a comment before the MAKE_PROPERTY macro.
   // See if there happens to be a comment before the MAKE_PROPERTY macro.
   if (make_property->_leading_comment != (CPPCommentBlock *)NULL) {
   if (make_property->_leading_comment != (CPPCommentBlock *)NULL) {
     iproperty._comment = trim_blanks(make_property->_leading_comment->_comment);
     iproperty._comment = trim_blanks(make_property->_leading_comment->_comment);
@@ -1999,7 +2126,6 @@ get_make_property(CPPMakeProperty *make_property, CPPStructType *struct_type, CP
     }
     }
   }
   }
 
 
-  idb->add_element(index, iproperty);
   return index;
   return index;
 }
 }
 
 
@@ -2428,6 +2554,10 @@ define_struct_type(InterrogateType &itype, CPPStructType *cpptype,
     break;
     break;
   }
   }
 
 
+  if (cpptype->is_final()) {
+    itype._flags |= InterrogateType::F_final;
+  }
+
   if (cpptype->_file.is_c_file()) {
   if (cpptype->_file.is_c_file()) {
     // This type declaration appears in a .C file.  We can only export types
     // This type declaration appears in a .C file.  We can only export types
     // defined in a .h file.
     // defined in a .h file.
@@ -2605,7 +2735,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);

+ 0 - 52
dtool/src/interrogate/typeManager.cxx

@@ -2517,55 +2517,3 @@ is_local(CPPType *source_type) {
  return false;
  return false;
  */
  */
 }
 }
-
-/**
- * Returns true if the type is trivial (or trivial enough for our purposes).
- */
-bool TypeManager::
-is_trivial(CPPType *source_type) {
-  switch (source_type->get_subtype()) {
-  case CPPDeclaration::ST_const:
-    return is_trivial(source_type->as_const_type()->_wrapped_around);
-
-  case CPPDeclaration::ST_reference:
-    return false;
-
-  case CPPDeclaration::ST_pointer:
-    return true;
-
-  case CPPDeclaration::ST_simple:
-    return true;
-
-  case CPPDeclaration::ST_typedef:
-    return is_trivial(source_type->as_typedef_type()->_type);
-
-  default:
-    if (source_type->is_trivial() || is_handle(source_type)) {
-      return true;
-    } else {
-      // This is a bit of a hack.  is_trivial() returns false for types that
-      // have an empty constructor (since we can't use =default yet). For the
-      // other classes, it's just convenient to consider them trivial even if
-      // they aren't, since they are simple enough.
-      string name = source_type->get_simple_name();
-      return (name == "ButtonHandle" || name == "DatagramIterator" ||
-              name == "BitMask" || name == "Filename" || name == "pixel" ||
-              name == "NodePath" || name == "LoaderOptions" ||
-              name == "PointerToArray" || name == "ConstPointerToArray" ||
-              name == "PStatThread" ||
-              (name.size() >= 6 && name.substr(0, 6) == "LPlane") ||
-              (name.size() > 6 && name.substr(0, 6) == "LPoint") ||
-              (name.size() > 7 && name.substr(0, 7) == "LVector") ||
-              (name.size() > 7 && name.substr(0, 7) == "LMatrix") ||
-              (name.size() > 8 && name.substr(0, 8) == "LVecBase") ||
-              (name.size() >= 9 && name.substr(0, 9) == "LParabola") ||
-              (name.size() >= 9 && name.substr(0, 9) == "LRotation") ||
-              (name.size() >= 11 && name.substr(0, 11) == "LQuaternion") ||
-              (name.size() >= 12 && name.substr(0, 12) == "LOrientation") ||
-              (name.size() > 16 && name.substr(0, 16) == "UnalignedLMatrix") ||
-              (name.size() > 17 && name.substr(0, 17) == "UnalignedLVecBase"));
-    }
-  }
-
-  return false;
-}

+ 0 - 1
dtool/src/interrogate/typeManager.h

@@ -149,7 +149,6 @@ public:
 
 
   static bool is_exported(CPPType *type);
   static bool is_exported(CPPType *type);
   static bool is_local(CPPType *type);
   static bool is_local(CPPType *type);
-  static bool is_trivial(CPPType *type);
 };
 };
 
 
 #endif
 #endif

+ 1 - 1
dtool/src/interrogatedb/interrogateDatabase.cxx

@@ -20,7 +20,7 @@ InterrogateDatabase *InterrogateDatabase::_global_ptr = NULL;
 int InterrogateDatabase::_file_major_version = 0;
 int InterrogateDatabase::_file_major_version = 0;
 int InterrogateDatabase::_file_minor_version = 0;
 int InterrogateDatabase::_file_minor_version = 0;
 int InterrogateDatabase::_current_major_version = 3;
 int InterrogateDatabase::_current_major_version = 3;
-int InterrogateDatabase::_current_minor_version = 2;
+int InterrogateDatabase::_current_minor_version = 3;
 
 
 /**
 /**
  *
  *

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

@@ -25,7 +25,10 @@ InterrogateElement(InterrogateModuleDef *def) :
   _has_function = 0;
   _has_function = 0;
   _clear_function = 0;
   _clear_function = 0;
   _del_function = 0;
   _del_function = 0;
+  _insert_function = 0;
+  _getkey_function = 0;
   _length_function = 0;
   _length_function = 0;
+  _make_property = nullptr;
 }
 }
 
 
 /**
 /**
@@ -51,7 +54,10 @@ operator = (const InterrogateElement &copy) {
   _has_function = copy._has_function;
   _has_function = copy._has_function;
   _clear_function = copy._clear_function;
   _clear_function = copy._clear_function;
   _del_function = copy._del_function;
   _del_function = copy._del_function;
+  _insert_function = copy._insert_function;
+  _getkey_function = copy._getkey_function;
   _length_function = copy._length_function;
   _length_function = copy._length_function;
+  _make_property = copy._make_property;
 }
 }
 
 
 /**
 /**
@@ -183,6 +189,38 @@ get_del_function() const {
   return _del_function;
   return _del_function;
 }
 }
 
 
+/**
+ *
+ */
+INLINE bool InterrogateElement::
+has_insert_function() const {
+  return (_flags & F_has_insert_function) != 0;
+}
+
+/**
+ *
+ */
+INLINE FunctionIndex InterrogateElement::
+get_insert_function() const {
+  return _insert_function;
+}
+
+/**
+ *
+ */
+INLINE bool InterrogateElement::
+has_getkey_function() const {
+  return (_flags & F_has_getkey_function) != 0;
+}
+
+/**
+ *
+ */
+INLINE FunctionIndex InterrogateElement::
+get_getkey_function() const {
+  return _getkey_function;
+}
+
 /**
 /**
  *
  *
  */
  */
@@ -199,6 +237,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) {

+ 8 - 1
dtool/src/interrogatedb/interrogateElement.cxx

@@ -29,7 +29,9 @@ output(ostream &out) const {
       << _has_function << " "
       << _has_function << " "
       << _clear_function << " "
       << _clear_function << " "
       << _del_function << " "
       << _del_function << " "
-      << _length_function << " ";
+      << _length_function << " "
+      << _insert_function << " "
+      << _getkey_function << " ";
   idf_output_string(out, _scoped_name);
   idf_output_string(out, _scoped_name);
   idf_output_string(out, _comment, '\n');
   idf_output_string(out, _comment, '\n');
 }
 }
@@ -45,6 +47,9 @@ input(istream &in) {
     in >> _has_function >> _clear_function;
     in >> _has_function >> _clear_function;
     if (InterrogateDatabase::get_file_minor_version() >= 2) {
     if (InterrogateDatabase::get_file_minor_version() >= 2) {
       in >> _del_function >> _length_function;
       in >> _del_function >> _length_function;
+      if (InterrogateDatabase::get_file_minor_version() >= 3) {
+        in >> _insert_function >> _getkey_function;
+      }
     }
     }
   }
   }
   idf_input_string(in, _scoped_name);
   idf_input_string(in, _scoped_name);
@@ -63,5 +68,7 @@ remap_indices(const IndexRemapper &remap) {
   _has_function = remap.map_from(_has_function);
   _has_function = remap.map_from(_has_function);
   _clear_function = remap.map_from(_clear_function);
   _clear_function = remap.map_from(_clear_function);
   _del_function = remap.map_from(_del_function);
   _del_function = remap.map_from(_del_function);
+  _insert_function = remap.map_from(_insert_function);
+  _getkey_function = remap.map_from(_getkey_function);
   _length_function = remap.map_from(_length_function);
   _length_function = remap.map_from(_length_function);
 }
 }

+ 12 - 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
@@ -49,8 +50,13 @@ public:
   INLINE FunctionIndex get_clear_function() const;
   INLINE FunctionIndex get_clear_function() const;
   INLINE bool has_del_function() const;
   INLINE bool has_del_function() const;
   INLINE FunctionIndex get_del_function() const;
   INLINE FunctionIndex get_del_function() const;
+  INLINE bool has_insert_function() const;
+  INLINE FunctionIndex get_insert_function() const;
+  INLINE bool has_getkey_function() const;
+  INLINE FunctionIndex get_getkey_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);
@@ -67,6 +73,8 @@ private:
     F_has_del_function= 0x0020,
     F_has_del_function= 0x0020,
     F_sequence        = 0x0040,
     F_sequence        = 0x0040,
     F_mapping         = 0x0080,
     F_mapping         = 0x0080,
+    F_has_insert_function= 0x0100,
+    F_has_getkey_function= 0x0200,
   };
   };
 
 
   int _flags;
   int _flags;
@@ -79,6 +87,10 @@ private:
   FunctionIndex _has_function;
   FunctionIndex _has_function;
   FunctionIndex _clear_function;
   FunctionIndex _clear_function;
   FunctionIndex _del_function;
   FunctionIndex _del_function;
+  FunctionIndex _insert_function;
+  FunctionIndex _getkey_function;
+
+  CPPMakeProperty *_make_property;
 
 
   friend class InterrogateBuilder;
   friend class InterrogateBuilder;
 };
 };

+ 8 - 0
dtool/src/interrogatedb/interrogateType.I

@@ -289,6 +289,14 @@ is_union() const {
   return (_flags & F_union) != 0;
   return (_flags & F_union) != 0;
 }
 }
 
 
+/**
+ *
+ */
+INLINE bool InterrogateType::
+is_final() const {
+  return (_flags & F_final) != 0;
+}
+
 /**
 /**
  *
  *
  */
  */

+ 2 - 0
dtool/src/interrogatedb/interrogateType.h

@@ -75,6 +75,7 @@ public:
   INLINE bool is_struct() const;
   INLINE bool is_struct() const;
   INLINE bool is_class() const;
   INLINE bool is_class() const;
   INLINE bool is_union() const;
   INLINE bool is_union() const;
+  INLINE bool is_final() const;
 
 
   INLINE bool is_fully_defined() const;
   INLINE bool is_fully_defined() const;
   INLINE bool is_unpublished() const;
   INLINE bool is_unpublished() const;
@@ -139,6 +140,7 @@ private:
     F_typedef              = 0x200000,
     F_typedef              = 0x200000,
     F_array                = 0x400000,
     F_array                = 0x400000,
     F_scoped_enum          = 0x800000,
     F_scoped_enum          = 0x800000,
+    F_final                =0x1000000,
   };
   };
 
 
 public:
 public:

+ 2 - 4
dtool/src/interrogatedb/p3interrogatedb_composite2.cxx

@@ -5,7 +5,5 @@
 #include "interrogate_interface.cxx"
 #include "interrogate_interface.cxx"
 #include "interrogate_request.cxx"
 #include "interrogate_request.cxx"
 #include "py_panda.cxx"
 #include "py_panda.cxx"
-
-
-
-
+#include "py_compat.cxx"
+#include "py_wrappers.cxx"

+ 54 - 0
dtool/src/interrogatedb/py_compat.cxx

@@ -0,0 +1,54 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file py_compat.cxx
+ * @author rdb
+ * @date 2017-12-03
+ */
+
+#include "py_compat.h"
+
+#ifdef HAVE_PYTHON
+
+PyTupleObject Dtool_EmptyTuple = {PyVarObject_HEAD_INIT(nullptr, 0)};
+
+#if PY_MAJOR_VERSION < 3
+/**
+ * Given a long or int, returns a size_t, or raises an OverflowError if it is
+ * out of range.
+ */
+size_t PyLongOrInt_AsSize_t(PyObject *vv) {
+  if (PyInt_Check(vv)) {
+    long value = PyInt_AS_LONG(vv);
+    if (value < 0) {
+      PyErr_SetString(PyExc_OverflowError,
+                      "can't convert negative value to size_t");
+      return (size_t)-1;
+    }
+    return (size_t)value;
+  }
+
+  if (!PyLong_Check(vv)) {
+    Dtool_Raise_TypeError("a long or int was expected");
+    return (size_t)-1;
+  }
+
+  size_t bytes;
+  int one = 1;
+  int res = _PyLong_AsByteArray((PyLongObject *)vv, (unsigned char *)&bytes,
+                                SIZEOF_SIZE_T, (int)*(unsigned char*)&one, 0);
+
+  if (res < 0) {
+    return (size_t)res;
+  } else {
+    return bytes;
+  }
+}
+#endif
+
+#endif  // HAVE_PYTHON

+ 175 - 0
dtool/src/interrogatedb/py_compat.h

@@ -0,0 +1,175 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file py_compat.h
+ * @author rdb
+ * @date 2017-12-02
+ */
+
+#ifndef PY_COMPAT_H
+#define PY_COMPAT_H
+
+#include "dtoolbase.h"
+
+#ifdef HAVE_PYTHON
+
+// The contents of this file were originally part of py_panda.h.  It
+// specifically contains polyfills that are required to maintain compatibility
+// with Python 2 and older versions of Python 3.
+
+// These compatibility hacks are sorted by Python version that removes the
+// need for the respective hack.
+
+#ifdef _POSIX_C_SOURCE
+#  undef _POSIX_C_SOURCE
+#endif
+
+#ifdef _XOPEN_SOURCE
+#  undef _XOPEN_SOURCE
+#endif
+
+// See PEP 353
+#define PY_SSIZE_T_CLEAN 1
+
+#include "Python.h"
+
+/* Python 2.4 */
+
+// 2.4 macros which aren't available in 2.3
+#ifndef Py_RETURN_NONE
+#  define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None
+#endif
+
+#ifndef Py_RETURN_TRUE
+#  define Py_RETURN_TRUE return Py_INCREF(Py_True), Py_True
+#endif
+
+#ifndef Py_RETURN_FALSE
+#  define Py_RETURN_FALSE return Py_INCREF(Py_False), Py_False
+#endif
+
+/* Python 2.5 */
+
+// Prior to Python 2.5, we didn't have Py_ssize_t.
+#if PY_VERSION_HEX < 0x02050000
+typedef int Py_ssize_t;
+#  define PyInt_FromSsize_t PyInt_FromLong
+#  define PyInt_AsSsize_t PyInt_AsLong
+#endif
+
+/* Python 2.6 */
+
+#ifndef Py_TYPE
+#  define Py_TYPE(ob) (((PyObject*)(ob))->ob_type)
+#endif
+
+/* Python 2.7, 3.1 */
+
+#ifndef PyVarObject_HEAD_INIT
+  #define PyVarObject_HEAD_INIT(type, size) \
+    PyObject_HEAD_INIT(type) size,
+#endif
+
+/* Python 2.7, 3.2 */
+
+#if PY_VERSION_HEX < 0x03020000
+#  define PyErr_NewExceptionWithDoc(name, doc, base, dict) \
+          PyErr_NewException(name, base, dict)
+#endif
+
+/* Python 3.0 */
+
+// Always on in Python 3
+#ifndef Py_TPFLAGS_CHECKTYPES
+#  define Py_TPFLAGS_CHECKTYPES 0
+#endif
+
+// Macros for writing code that will compile in both versions.
+#if PY_MAJOR_VERSION >= 3
+#  define nb_nonzero nb_bool
+#  define nb_divide nb_true_divide
+#  define nb_inplace_divide nb_inplace_true_divide
+
+#  define PyLongOrInt_Check(x) PyLong_Check(x)
+#  define PyLongOrInt_AS_LONG PyLong_AS_LONG
+#  define PyInt_Check PyLong_Check
+#  define PyInt_AsLong PyLong_AsLong
+#  define PyInt_AS_LONG PyLong_AS_LONG
+#  define PyLongOrInt_AsSize_t PyLong_AsSize_t
+#else
+#  define PyLongOrInt_Check(x) (PyInt_Check(x) || PyLong_Check(x))
+// PyInt_FromSize_t automatically picks the right type.
+#  define PyLongOrInt_AS_LONG PyInt_AsLong
+
+EXPCL_INTERROGATEDB size_t PyLongOrInt_AsSize_t(PyObject *);
+#endif
+
+// Which character to use in PyArg_ParseTuple et al for a byte string.
+#if PY_MAJOR_VERSION >= 3
+#  define FMTCHAR_BYTES "y"
+#else
+#  define FMTCHAR_BYTES "s"
+#endif
+
+/* Python 3.2 */
+
+#if PY_VERSION_HEX < 0x03020000
+typedef long Py_hash_t;
+#endif
+
+/* Python 3.3 */
+
+#if PY_MAJOR_VERSION >= 3
+// Python 3 versions before 3.3.3 defined this incorrectly.
+#  undef _PyErr_OCCURRED
+#  define _PyErr_OCCURRED() (PyThreadState_GET()->curexc_type)
+
+// Python versions before 3.3 did not define this.
+#  if PY_VERSION_HEX < 0x03030000
+#    define PyUnicode_AsUTF8 _PyUnicode_AsString
+#    define PyUnicode_AsUTF8AndSize _PyUnicode_AsStringAndSize
+#  endif
+#endif
+
+/* Python 3.6 */
+
+// Used to implement _PyObject_CallNoArg
+extern EXPCL_INTERROGATEDB PyTupleObject Dtool_EmptyTuple;
+
+#ifndef _PyObject_CallNoArg
+#  define _PyObject_CallNoArg(func) PyObject_Call((func), (PyObject *)&Dtool_EmptyTuple, nullptr)
+#endif
+
+// Python versions before 3.6 didn't require longlong support to be enabled.
+#ifndef HAVE_LONG_LONG
+#  define PyLong_FromLongLong(x) PyLong_FromLong((long) (x))
+#  define PyLong_FromUnsignedLongLong(x) PyLong_FromUnsignedLong((unsigned long) (x))
+#  define PyLong_AsLongLong(x) PyLong_AsLong(x)
+#  define PyLong_AsUnsignedLongLong(x) PyLong_AsUnsignedLong(x)
+#  define PyLong_AsUnsignedLongLongMask(x) PyLong_AsUnsignedLongMask(x)
+#  define PyLong_AsLongLongAndOverflow(x) PyLong_AsLongAndOverflow(x)
+#endif
+
+/* Python 3.7 */
+
+#ifndef PyDict_GET_SIZE
+#  define PyDict_GET_SIZE(mp) (((PyDictObject *)mp)->ma_used)
+#endif
+
+/* Other Python implementations */
+
+// _PyErr_OCCURRED is an undocumented macro version of PyErr_Occurred.
+// Some implementations of the CPython API (e.g. PyPy's cpyext) do not define
+// it, so in these cases we just silently fall back to PyErr_Occurred.
+#ifndef _PyErr_OCCURRED
+#  define _PyErr_OCCURRED() PyErr_Occurred()
+#endif
+
+#endif  // HAVE_PYTHON
+
+#endif  // PY_COMPAT_H

+ 42 - 8
dtool/src/interrogatedb/py_panda.I

@@ -11,20 +11,54 @@
  * @date 2016-06-06
  * @date 2016-06-06
  */
  */
 
 
+#ifdef _MSC_VER
+#define _IS_FINAL(T) (__is_sealed(T))
+#elif defined(__GNUC__)
+#define _IS_FINAL(T) (__is_final(T))
+#else
+#define _IS_FINAL(T) (0)
+#endif
+
 /**
 /**
  * Template function that can be used to extract any TypedObject pointer from
  * Template function that can be used to extract any TypedObject pointer from
  * a wrapped Python object.
  * a wrapped Python object.
  */
  */
 template<class T> INLINE bool
 template<class T> INLINE bool
-DTOOL_Call_ExtractThisPointer(PyObject *self, T *&into) {
-  if (DtoolCanThisBeAPandaInstance(self)) {
+DtoolInstance_GetPointer(PyObject *self, T *&into) {
+  if (DtoolInstance_Check(self)) {
     Dtool_PyTypedObject *target_class = Dtool_RuntimeTypeDtoolType(get_type_handle(T).get_index());
     Dtool_PyTypedObject *target_class = Dtool_RuntimeTypeDtoolType(get_type_handle(T).get_index());
-    if (target_class != NULL) {
-      into = (T*) ((Dtool_PyInstDef *)self)->_My_Type->_Dtool_UpcastInterface(self, target_class);
-      return (into != NULL);
+    if (target_class != nullptr) {
+      if (_IS_FINAL(T)) {
+        if (DtoolInstance_TYPE(self) == target_class) {
+          into = (T *)DtoolInstance_VOID_PTR(self);
+        }
+      } else {
+        into = (T *)DtoolInstance_UPCAST(self, *target_class);
+      }
+      return (into != nullptr);
+    }
+  }
+  into = nullptr;
+  return false;
+}
+
+/**
+ * Template function that can be used to extract any TypedObject pointer from
+ * a wrapped Python object.  In this case, the Dtool_PyTypedObject is known.
+ */
+template<class T> INLINE bool
+DtoolInstance_GetPointer(PyObject *self, T *&into, Dtool_PyTypedObject &target_class) {
+  if (DtoolInstance_Check(self)) {
+    if (_IS_FINAL(T)) {
+      if (DtoolInstance_TYPE(self) == &target_class) {
+        into = (T *)DtoolInstance_VOID_PTR(self);
+      }
+    } else {
+      into = (T *)DtoolInstance_UPCAST(self, target_class);
     }
     }
+    return (into != nullptr);
   }
   }
-  into = NULL;
+  into = nullptr;
   return false;
   return false;
 }
 }
 
 
@@ -73,7 +107,7 @@ Dtool_CheckNoArgs(PyObject *args) {
 ALWAYS_INLINE bool
 ALWAYS_INLINE bool
 Dtool_CheckNoArgs(PyObject *args, PyObject *kwds) {
 Dtool_CheckNoArgs(PyObject *args, PyObject *kwds) {
   return PyTuple_GET_SIZE(args) == 0 &&
   return PyTuple_GET_SIZE(args) == 0 &&
-    (kwds == NULL || ((PyDictObject *)kwds)->ma_used == 0);
+    (kwds == nullptr || PyDict_GET_SIZE(kwds) == 0);
 }
 }
 
 
 /**
 /**
@@ -222,7 +256,7 @@ ALWAYS_INLINE PyObject *Dtool_WrapValue(PyObject *value) {
   return value;
   return value;
 }
 }
 
 
-ALWAYS_INLINE PyObject *Dtool_WrapValue(const std::vector<unsigned char> &value) {
+ALWAYS_INLINE PyObject *Dtool_WrapValue(const vector_uchar &value) {
 #if PY_MAJOR_VERSION >= 3
 #if PY_MAJOR_VERSION >= 3
   return PyBytes_FromStringAndSize((char *)value.data(), (Py_ssize_t)value.size());
   return PyBytes_FromStringAndSize((char *)value.data(), (Py_ssize_t)value.size());
 #else
 #else

+ 73 - 207
dtool/src/interrogatedb/py_panda.cxx

@@ -31,76 +31,14 @@ static RuntimeTypeMap runtime_type_map;
 static RuntimeTypeSet runtime_type_set;
 static RuntimeTypeSet runtime_type_set;
 static NamedTypeMap named_type_map;
 static NamedTypeMap named_type_map;
 
 
-#if PY_MAJOR_VERSION < 3
-/**
- * Given a long or int, returns a size_t, or raises an OverflowError if it is
- * out of range.
- */
-size_t PyLongOrInt_AsSize_t(PyObject *vv) {
-  if (PyInt_Check(vv)) {
-    long value = PyInt_AS_LONG(vv);
-    if (value < 0) {
-      PyErr_SetString(PyExc_OverflowError,
-                      "can't convert negative value to size_t");
-      return (size_t)-1;
-    }
-    return (size_t)value;
-  }
-
-  if (!PyLong_Check(vv)) {
-    Dtool_Raise_TypeError("a long or int was expected");
-    return (size_t)-1;
-  }
-
-  size_t bytes;
-  int one = 1;
-  int res = _PyLong_AsByteArray((PyLongObject *)vv, (unsigned char *)&bytes,
-                                SIZEOF_SIZE_T, (int)*(unsigned char*)&one, 0);
-
-  if (res < 0) {
-    return (size_t)res;
-  } else {
-    return bytes;
-  }
-}
-#endif
-
-#if PY_VERSION_HEX < 0x03060000
-INLINE static PyObject *_PyObject_CallNoArg(PyObject *func) {
-  PyObject *args = PyTuple_New(0);
-  PyObject *result = PyObject_Call(func, args, NULL);
-  Py_DECREF(args);
-  return result;
-}
-#endif
-
-/**
- * Given a valid (non-NULL) PyObject, does a simple check to see if it might
- * be an instance of a Panda type.  It does this using a signature that is
- * encoded on each instance.
- */
-bool DtoolCanThisBeAPandaInstance(PyObject *self) {
-  // simple sanity check for the class type..size.. will stop basic foobars..
-  // It is arguably better to use something like this:
-  // PyType_IsSubtype(Py_TYPE(self), &Dtool_DTOOL_SUPER_BASE._PyType) ...but
-  // probably not as fast.
-  if (Py_TYPE(self)->tp_basicsize >= (int)sizeof(Dtool_PyInstDef)) {
-    Dtool_PyInstDef *pyself = (Dtool_PyInstDef *) self;
-    if (pyself->_signature == PY_PANDA_SIGNATURE) {
-      return true;
-    }
-  }
-  return false;
-}
-
 /**
 /**
 
 
  */
  */
 void DTOOL_Call_ExtractThisPointerForType(PyObject *self, Dtool_PyTypedObject *classdef, void **answer) {
 void DTOOL_Call_ExtractThisPointerForType(PyObject *self, Dtool_PyTypedObject *classdef, void **answer) {
-  if (DtoolCanThisBeAPandaInstance(self)) {
-    *answer = ((Dtool_PyInstDef *)self)->_My_Type->_Dtool_UpcastInterface(self, classdef);
+  if (DtoolInstance_Check(self)) {
+    *answer = DtoolInstance_UPCAST(self, *classdef);
   } else {
   } else {
-    *answer = NULL;
+    *answer = nullptr;
   }
   }
 }
 }
 
 
@@ -110,12 +48,12 @@ void DTOOL_Call_ExtractThisPointerForType(PyObject *self, Dtool_PyTypedObject *c
  * was of the wrong type, raises an AttributeError.
  * was of the wrong type, raises an AttributeError.
  */
  */
 bool Dtool_Call_ExtractThisPointer(PyObject *self, Dtool_PyTypedObject &classdef, void **answer) {
 bool Dtool_Call_ExtractThisPointer(PyObject *self, Dtool_PyTypedObject &classdef, void **answer) {
-  if (self == NULL || !DtoolCanThisBeAPandaInstance(self) || ((Dtool_PyInstDef *)self)->_ptr_to_object == NULL) {
+  if (self == nullptr || !DtoolInstance_Check(self) || DtoolInstance_VOID_PTR(self) == nullptr) {
     Dtool_Raise_TypeError("C++ object is not yet constructed, or already destructed.");
     Dtool_Raise_TypeError("C++ object is not yet constructed, or already destructed.");
     return false;
     return false;
   }
   }
 
 
-  *answer = ((Dtool_PyInstDef *)self)->_My_Type->_Dtool_UpcastInterface(self, &classdef);
+  *answer = DtoolInstance_UPCAST(self, classdef);
   return true;
   return true;
 }
 }
 
 
@@ -130,12 +68,12 @@ bool Dtool_Call_ExtractThisPointer(PyObject *self, Dtool_PyTypedObject &classdef
 bool Dtool_Call_ExtractThisPointer_NonConst(PyObject *self, Dtool_PyTypedObject &classdef,
 bool Dtool_Call_ExtractThisPointer_NonConst(PyObject *self, Dtool_PyTypedObject &classdef,
                                             void **answer, const char *method_name) {
                                             void **answer, const char *method_name) {
 
 
-  if (self == NULL || !DtoolCanThisBeAPandaInstance(self) || ((Dtool_PyInstDef *)self)->_ptr_to_object == NULL) {
+  if (self == nullptr || !DtoolInstance_Check(self) || DtoolInstance_VOID_PTR(self) == nullptr) {
     Dtool_Raise_TypeError("C++ object is not yet constructed, or already destructed.");
     Dtool_Raise_TypeError("C++ object is not yet constructed, or already destructed.");
     return false;
     return false;
   }
   }
 
 
-  if (((Dtool_PyInstDef *)self)->_is_const) {
+  if (DtoolInstance_IS_CONST(self)) {
     // All overloads of this function are non-const.
     // All overloads of this function are non-const.
     PyErr_Format(PyExc_TypeError,
     PyErr_Format(PyExc_TypeError,
                  "Cannot call %s() on a const object.",
                  "Cannot call %s() on a const object.",
@@ -143,7 +81,7 @@ bool Dtool_Call_ExtractThisPointer_NonConst(PyObject *self, Dtool_PyTypedObject
     return false;
     return false;
   }
   }
 
 
-  *answer = ((Dtool_PyInstDef *)self)->_My_Type->_Dtool_UpcastInterface(self, &classdef);
+  *answer = DtoolInstance_UPCAST(self, classdef);
   return true;
   return true;
 }
 }
 
 
@@ -173,19 +111,19 @@ void *
 DTOOL_Call_GetPointerThisClass(PyObject *self, Dtool_PyTypedObject *classdef,
 DTOOL_Call_GetPointerThisClass(PyObject *self, Dtool_PyTypedObject *classdef,
                                int param, const string &function_name, bool const_ok,
                                int param, const string &function_name, bool const_ok,
                                bool report_errors) {
                                bool report_errors) {
-  // if (PyErr_Occurred()) { return NULL; }
-  if (self == NULL) {
+  // if (PyErr_Occurred()) { return nullptr; }
+  if (self == nullptr) {
     if (report_errors) {
     if (report_errors) {
-      return Dtool_Raise_TypeError("self is NULL");
+      return Dtool_Raise_TypeError("self is nullptr");
     }
     }
-    return NULL;
+    return nullptr;
   }
   }
 
 
-  if (DtoolCanThisBeAPandaInstance(self)) {
-    void *result = ((Dtool_PyInstDef *)self)->_My_Type->_Dtool_UpcastInterface(self, classdef);
+  if (DtoolInstance_Check(self)) {
+    void *result = DtoolInstance_UPCAST(self, *classdef);
 
 
-    if (result != NULL) {
-      if (const_ok || !((Dtool_PyInstDef *)self)->_is_const) {
+    if (result != nullptr) {
+      if (const_ok || !DtoolInstance_IS_CONST(self)) {
         return result;
         return result;
       }
       }
 
 
@@ -194,7 +132,7 @@ DTOOL_Call_GetPointerThisClass(PyObject *self, Dtool_PyTypedObject *classdef,
                             "%s() argument %d may not be const",
                             "%s() argument %d may not be const",
                             function_name.c_str(), param);
                             function_name.c_str(), param);
       }
       }
-      return NULL;
+      return nullptr;
     }
     }
   }
   }
 
 
@@ -202,17 +140,14 @@ DTOOL_Call_GetPointerThisClass(PyObject *self, Dtool_PyTypedObject *classdef,
     return Dtool_Raise_ArgTypeError(self, param, function_name.c_str(), classdef->_PyType.tp_name);
     return Dtool_Raise_ArgTypeError(self, param, function_name.c_str(), classdef->_PyType.tp_name);
   }
   }
 
 
-  return NULL;
+  return nullptr;
 }
 }
 
 
 void *DTOOL_Call_GetPointerThis(PyObject *self) {
 void *DTOOL_Call_GetPointerThis(PyObject *self) {
-  if (self != NULL) {
-    if (DtoolCanThisBeAPandaInstance(self)) {
-      Dtool_PyInstDef * pyself = (Dtool_PyInstDef *) self;
-      return pyself->_ptr_to_object;
-    }
+  if (self != nullptr && DtoolInstance_Check(self)) {
+    return DtoolInstance_VOID_PTR(self);
   }
   }
-  return NULL;
+  return nullptr;
 }
 }
 
 
 /**
 /**
@@ -329,11 +264,11 @@ PyObject *_Dtool_Raise_BadArgumentsError() {
  * NULL, otherwise Py_None.
  * NULL, otherwise Py_None.
  */
  */
 PyObject *_Dtool_Return_None() {
 PyObject *_Dtool_Return_None() {
-  if (_PyErr_OCCURRED()) {
+  if (UNLIKELY(_PyErr_OCCURRED())) {
     return NULL;
     return NULL;
   }
   }
 #ifndef NDEBUG
 #ifndef NDEBUG
-  if (Notify::ptr()->has_assert_failed()) {
+  if (UNLIKELY(Notify::ptr()->has_assert_failed())) {
     return Dtool_Raise_AssertionError();
     return Dtool_Raise_AssertionError();
   }
   }
 #endif
 #endif
@@ -346,11 +281,11 @@ PyObject *_Dtool_Return_None() {
  * NULL, otherwise the given boolean value as a PyObject *.
  * NULL, otherwise the given boolean value as a PyObject *.
  */
  */
 PyObject *Dtool_Return_Bool(bool value) {
 PyObject *Dtool_Return_Bool(bool value) {
-  if (_PyErr_OCCURRED()) {
+  if (UNLIKELY(_PyErr_OCCURRED())) {
     return NULL;
     return NULL;
   }
   }
 #ifndef NDEBUG
 #ifndef NDEBUG
-  if (Notify::ptr()->has_assert_failed()) {
+  if (UNLIKELY(Notify::ptr()->has_assert_failed())) {
     return Dtool_Raise_AssertionError();
     return Dtool_Raise_AssertionError();
   }
   }
 #endif
 #endif
@@ -365,11 +300,11 @@ PyObject *Dtool_Return_Bool(bool value) {
  * increased.
  * increased.
  */
  */
 PyObject *_Dtool_Return(PyObject *value) {
 PyObject *_Dtool_Return(PyObject *value) {
-  if (_PyErr_OCCURRED()) {
+  if (UNLIKELY(_PyErr_OCCURRED())) {
     return NULL;
     return NULL;
   }
   }
 #ifndef NDEBUG
 #ifndef NDEBUG
-  if (Notify::ptr()->has_assert_failed()) {
+  if (UNLIKELY(Notify::ptr()->has_assert_failed())) {
     return Dtool_Raise_AssertionError();
     return Dtool_Raise_AssertionError();
   }
   }
 #endif
 #endif
@@ -618,10 +553,45 @@ PyObject *Dtool_PyModuleInitHelper(LibraryDef *defs[], const char *modulename) {
     dtool_inited = true;
     dtool_inited = true;
 
 
     if (PyType_Ready(&Dtool_SequenceWrapper_Type) < 0) {
     if (PyType_Ready(&Dtool_SequenceWrapper_Type) < 0) {
-      PyErr_SetString(PyExc_TypeError, "PyType_Ready(Dtool_SequenceWrapper)");
-      return NULL;
+      return Dtool_Raise_TypeError("PyType_Ready(Dtool_SequenceWrapper)");
     }
     }
 
 
+    if (PyType_Ready(&Dtool_MutableSequenceWrapper_Type) < 0) {
+      return Dtool_Raise_TypeError("PyType_Ready(Dtool_MutableSequenceWrapper)");
+    }
+
+    if (PyType_Ready(&Dtool_MappingWrapper_Type) < 0) {
+      return Dtool_Raise_TypeError("PyType_Ready(Dtool_MappingWrapper)");
+    }
+
+    if (PyType_Ready(&Dtool_MutableMappingWrapper_Type) < 0) {
+      return Dtool_Raise_TypeError("PyType_Ready(Dtool_MutableMappingWrapper)");
+    }
+
+    if (PyType_Ready(&Dtool_MappingWrapper_Keys_Type) < 0) {
+      return Dtool_Raise_TypeError("PyType_Ready(Dtool_MappingWrapper_Keys)");
+    }
+
+    if (PyType_Ready(&Dtool_MappingWrapper_Values_Type) < 0) {
+      return Dtool_Raise_TypeError("PyType_Ready(Dtool_MappingWrapper_Values)");
+    }
+
+    if (PyType_Ready(&Dtool_MappingWrapper_Items_Type) < 0) {
+      return Dtool_Raise_TypeError("PyType_Ready(Dtool_MappingWrapper_Items)");
+    }
+
+    if (PyType_Ready(&Dtool_GeneratorWrapper_Type) < 0) {
+      return Dtool_Raise_TypeError("PyType_Ready(Dtool_GeneratorWrapper)");
+    }
+
+    if (PyType_Ready(&Dtool_StaticProperty_Type) < 0) {
+      return Dtool_Raise_TypeError("PyType_Ready(Dtool_StaticProperty_Type)");
+    }
+
+#ifdef Py_TRACE_REFS
+    _Py_AddToAllObjects((PyObject *)&Dtool_EmptyTuple, 0);
+#endif
+
     // Initialize the base class of everything.
     // Initialize the base class of everything.
     Dtool_PyModuleClassInit_DTOOL_SUPER_BASE(NULL);
     Dtool_PyModuleClassInit_DTOOL_SUPER_BASE(NULL);
   }
   }
@@ -720,7 +690,7 @@ PyObject *Dtool_BorrowThisReference(PyObject *self, PyObject *args) {
   PyObject *to_in = NULL;
   PyObject *to_in = NULL;
   if (PyArg_UnpackTuple(args, "Dtool_BorrowThisReference", 2, 2, &to_in, &from_in)) {
   if (PyArg_UnpackTuple(args, "Dtool_BorrowThisReference", 2, 2, &to_in, &from_in)) {
 
 
-    if (DtoolCanThisBeAPandaInstance(from_in) && DtoolCanThisBeAPandaInstance(to_in)) {
+    if (DtoolInstance_Check(from_in) && DtoolInstance_Check(to_in)) {
       Dtool_PyInstDef *from = (Dtool_PyInstDef *) from_in;
       Dtool_PyInstDef *from = (Dtool_PyInstDef *) from_in;
       Dtool_PyInstDef *to = (Dtool_PyInstDef *) to_in;
       Dtool_PyInstDef *to = (Dtool_PyInstDef *) to_in;
 
 
@@ -765,9 +735,8 @@ PyObject *Dtool_AddToDictionary(PyObject *self1, PyObject *args) {
 }
 }
 
 
 Py_hash_t DTOOL_PyObject_HashPointer(PyObject *self) {
 Py_hash_t DTOOL_PyObject_HashPointer(PyObject *self) {
-  if (self != NULL && DtoolCanThisBeAPandaInstance(self)) {
-    Dtool_PyInstDef * pyself = (Dtool_PyInstDef *) self;
-    return (Py_hash_t) pyself->_ptr_to_object;
+  if (self != nullptr && DtoolInstance_Check(self)) {
+    return (Py_hash_t)DtoolInstance_VOID_PTR(self);
   }
   }
   return -1;
   return -1;
 }
 }
@@ -940,14 +909,14 @@ bool Dtool_ExtractArg(PyObject **result, PyObject *args, PyObject *kwds,
                       const char *keyword) {
                       const char *keyword) {
 
 
   if (PyTuple_GET_SIZE(args) == 1) {
   if (PyTuple_GET_SIZE(args) == 1) {
-    if (kwds == NULL || ((PyDictObject *)kwds)->ma_used == 0) {
+    if (kwds == nullptr || PyDict_GET_SIZE(kwds) == 0) {
       *result = PyTuple_GET_ITEM(args, 0);
       *result = PyTuple_GET_ITEM(args, 0);
       return true;
       return true;
     }
     }
   } else if (PyTuple_GET_SIZE(args) == 0) {
   } else if (PyTuple_GET_SIZE(args) == 0) {
     PyObject *key;
     PyObject *key;
     Py_ssize_t ppos = 0;
     Py_ssize_t ppos = 0;
-    if (kwds != NULL && ((PyDictObject *)kwds)->ma_used == 1 &&
+    if (kwds != nullptr && PyDict_GET_SIZE(kwds) == 1 &&
         PyDict_Next(kwds, &ppos, &key, result)) {
         PyDict_Next(kwds, &ppos, &key, result)) {
       // We got the item, we just need to make sure that it had the right key.
       // We got the item, we just need to make sure that it had the right key.
 #if PY_VERSION_HEX >= 0x03060000
 #if PY_VERSION_HEX >= 0x03060000
@@ -968,7 +937,7 @@ bool Dtool_ExtractArg(PyObject **result, PyObject *args, PyObject *kwds,
  */
  */
 bool Dtool_ExtractArg(PyObject **result, PyObject *args, PyObject *kwds) {
 bool Dtool_ExtractArg(PyObject **result, PyObject *args, PyObject *kwds) {
   if (PyTuple_GET_SIZE(args) == 1 &&
   if (PyTuple_GET_SIZE(args) == 1 &&
-      (kwds == NULL || ((PyDictObject *)kwds)->ma_used == 0)) {
+      (kwds == nullptr || PyDict_GET_SIZE(kwds) == 0)) {
     *result = PyTuple_GET_ITEM(args, 0);
     *result = PyTuple_GET_ITEM(args, 0);
     return true;
     return true;
   }
   }
@@ -986,12 +955,12 @@ bool Dtool_ExtractOptionalArg(PyObject **result, PyObject *args, PyObject *kwds,
                               const char *keyword) {
                               const char *keyword) {
 
 
   if (PyTuple_GET_SIZE(args) == 1) {
   if (PyTuple_GET_SIZE(args) == 1) {
-    if (kwds == NULL || ((PyDictObject *)kwds)->ma_used == 0) {
+    if (kwds == nullptr || PyDict_GET_SIZE(kwds) == 0) {
       *result = PyTuple_GET_ITEM(args, 0);
       *result = PyTuple_GET_ITEM(args, 0);
       return true;
       return true;
     }
     }
   } else if (PyTuple_GET_SIZE(args) == 0) {
   } else if (PyTuple_GET_SIZE(args) == 0) {
-    if (kwds != NULL && ((PyDictObject *)kwds)->ma_used == 1) {
+    if (kwds != nullptr && PyDict_GET_SIZE(kwds) == 1) {
       PyObject *key;
       PyObject *key;
       Py_ssize_t ppos = 0;
       Py_ssize_t ppos = 0;
       if (!PyDict_Next(kwds, &ppos, &key, result)) {
       if (!PyDict_Next(kwds, &ppos, &key, result)) {
@@ -1018,7 +987,7 @@ bool Dtool_ExtractOptionalArg(PyObject **result, PyObject *args, PyObject *kwds,
  * Variant of Dtool_ExtractOptionalArg that does not accept a keyword argument.
  * Variant of Dtool_ExtractOptionalArg that does not accept a keyword argument.
  */
  */
 bool Dtool_ExtractOptionalArg(PyObject **result, PyObject *args, PyObject *kwds) {
 bool Dtool_ExtractOptionalArg(PyObject **result, PyObject *args, PyObject *kwds) {
-  if (kwds != NULL && ((PyDictObject *)kwds)->ma_used != 0) {
+  if (kwds != nullptr && PyDict_GET_SIZE(kwds) != 0) {
     return false;
     return false;
   }
   }
   if (PyTuple_GET_SIZE(args) == 1) {
   if (PyTuple_GET_SIZE(args) == 1) {
@@ -1028,107 +997,4 @@ bool Dtool_ExtractOptionalArg(PyObject **result, PyObject *args, PyObject *kwds)
   return (PyTuple_GET_SIZE(args) == 0);
   return (PyTuple_GET_SIZE(args) == 0);
 }
 }
 
 
-/**
- * This class is returned from properties that require a settable interface,
- * ie. something.children[i] = 3.
- */
-static void Dtool_SequenceWrapper_dealloc(PyObject *self) {
-  Dtool_SequenceWrapper *wrap = (Dtool_SequenceWrapper *)self;
-  nassertv(wrap);
-  Py_DECREF(wrap->_base);
-}
-
-static Py_ssize_t Dtool_SequenceWrapper_length(PyObject *self) {
-  Dtool_SequenceWrapper *wrap = (Dtool_SequenceWrapper *)self;
-  nassertr(wrap, -1);
-  nassertr(wrap->_len_func, -1);
-  return wrap->_len_func(wrap->_base);
-}
-
-static PyObject *Dtool_SequenceWrapper_getitem(PyObject *self, Py_ssize_t index) {
-  Dtool_SequenceWrapper *wrap = (Dtool_SequenceWrapper *)self;
-  nassertr(wrap, NULL);
-  nassertr(wrap->_getitem_func, NULL);
-  return wrap->_getitem_func(wrap->_base, index);
-}
-
-static int Dtool_SequenceWrapper_setitem(PyObject *self, Py_ssize_t index, PyObject *value) {
-  Dtool_SequenceWrapper *wrap = (Dtool_SequenceWrapper *)self;
-  nassertr(wrap, -1);
-  nassertr(wrap->_setitem_func, -1);
-  return wrap->_setitem_func(wrap->_base, index, value);
-}
-
-static PySequenceMethods Dtool_SequenceWrapper_SequenceMethods = {
-  Dtool_SequenceWrapper_length,
-  0, // sq_concat
-  0, // sq_repeat
-  Dtool_SequenceWrapper_getitem,
-  0, // sq_slice
-  Dtool_SequenceWrapper_setitem,
-  0, // sq_ass_slice
-  0, // sq_contains
-  0, // sq_inplace_concat
-  0, // sq_inplace_repeat
-};
-
-PyTypeObject Dtool_SequenceWrapper_Type = {
-  PyVarObject_HEAD_INIT(NULL, 0)
-  "sequence wrapper",
-  sizeof(Dtool_SequenceWrapper),
-  0, // tp_itemsize
-  Dtool_SequenceWrapper_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,
-  0, // tp_as_mapping
-  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

+ 31 - 146
dtool/src/interrogatedb/py_panda.h

@@ -20,124 +20,15 @@
 #define  Py_DEBUG
 #define  Py_DEBUG
 #endif
 #endif
 
 
-#ifndef NO_RUNTIME_TYPES
-
-#include "dtoolbase.h"
-#include "typedObject.h"
-#include "typeRegistry.h"
-
-#endif
-
 #include "pnotify.h"
 #include "pnotify.h"
+#include "vector_uchar.h"
 
 
 #if defined(HAVE_PYTHON) && !defined(CPPPARSER)
 #if defined(HAVE_PYTHON) && !defined(CPPPARSER)
 
 
-#ifdef _POSIX_C_SOURCE
-#undef _POSIX_C_SOURCE
-#endif
-
-#ifdef _XOPEN_SOURCE
-#undef _XOPEN_SOURCE
-#endif
-
-#define PY_SSIZE_T_CLEAN 1
-
-#include "Python.h"
+// py_compat.h includes Python.h.
+#include "py_compat.h"
 #include "structmember.h"
 #include "structmember.h"
 
 
-#ifndef HAVE_LONG_LONG
-#define PyLong_FromLongLong(x) PyLong_FromLong((long) (x))
-#define PyLong_FromUnsignedLongLong(x) PyLong_FromUnsignedLong((unsigned long) (x))
-#define PyLong_AsLongLong(x) PyLong_AsLong(x)
-#define PyLong_AsUnsignedLongLong(x) PyLong_AsUnsignedLong(x)
-#define PyLong_AsUnsignedLongLongMask(x) PyLong_AsUnsignedLongMask(x)
-#define PyLong_AsLongLongAndOverflow(x) PyLong_AsLongAndOverflow(x)
-#endif
-
-#if PY_VERSION_HEX < 0x02050000
-
-// Prior to Python 2.5, we didn't have Py_ssize_t.
-typedef int Py_ssize_t;
-#define PyInt_FromSsize_t PyInt_FromLong
-#define PyInt_AsSsize_t PyInt_AsLong
-
-#endif  // PY_VERSION_HEX
-
-// 2.4 macros which aren't available in 2.3
-#ifndef Py_RETURN_NONE
-inline PyObject* doPy_RETURN_NONE()
-{   Py_INCREF(Py_None); return Py_None; }
-#define Py_RETURN_NONE return doPy_RETURN_NONE()
-#endif
-
-#ifndef Py_RETURN_TRUE
-inline PyObject* doPy_RETURN_TRUE()
-{Py_INCREF(Py_True); return Py_True;}
-#define Py_RETURN_TRUE return doPy_RETURN_TRUE()
-#endif
-
-#ifndef Py_RETURN_FALSE
-inline PyObject* doPy_RETURN_FALSE()
-{Py_INCREF(Py_False); return Py_False;}
-#define Py_RETURN_FALSE return doPy_RETURN_FALSE()
-#endif
-
-#ifndef PyVarObject_HEAD_INIT
-#define PyVarObject_HEAD_INIT(type, size) \
-  PyObject_HEAD_INIT(type) size,
-#endif
-
-#ifndef Py_TYPE
-#define Py_TYPE(ob) (((PyObject*)(ob))->ob_type)
-#endif
-
-#ifndef Py_TPFLAGS_CHECKTYPES
-// Always on in Python 3
-#define Py_TPFLAGS_CHECKTYPES 0
-#endif
-
-#if PY_MAJOR_VERSION >= 3
-// For writing code that will compile in both versions.
-#define nb_nonzero nb_bool
-#define nb_divide nb_true_divide
-#define nb_inplace_divide nb_inplace_true_divide
-
-#define PyLongOrInt_Check(x) PyLong_Check(x)
-#define PyLongOrInt_AS_LONG PyLong_AS_LONG
-#define PyInt_Check PyLong_Check
-#define PyInt_AsLong PyLong_AsLong
-#define PyInt_AS_LONG PyLong_AS_LONG
-#define PyLongOrInt_AsSize_t PyLong_AsSize_t
-#else
-#define PyLongOrInt_Check(x) (PyInt_Check(x) || PyLong_Check(x))
-// PyInt_FromSize_t automatically picks the right type.
-#define PyLongOrInt_AS_LONG PyInt_AsLong
-
-EXPCL_INTERROGATEDB size_t PyLongOrInt_AsSize_t(PyObject *);
-
-// For more portably defining hash functions.
-typedef long Py_hash_t;
-#endif
-
-#if PY_MAJOR_VERSION >= 3
-// Python 3 versions before 3.3.3 defined this incorrectly.
-#undef _PyErr_OCCURRED
-#define _PyErr_OCCURRED() (PyThreadState_GET()->curexc_type)
-
-// Python versions before 3.3 did not define this.
-#if PY_VERSION_HEX < 0x03030000
-#define PyUnicode_AsUTF8 _PyUnicode_AsString
-#define PyUnicode_AsUTF8AndSize _PyUnicode_AsStringAndSize
-#endif
-#endif
-
-// Which character to use in PyArg_ParseTuple et al for a byte string.
-#if PY_MAJOR_VERSION >= 3
-#define FMTCHAR_BYTES "y"
-#else
-#define FMTCHAR_BYTES "s"
-#endif
-
 using namespace std;
 using namespace std;
 
 
 // this is tempory .. untill this is glued better into the panda build system
 // this is tempory .. untill this is glued better into the panda build system
@@ -236,7 +127,7 @@ static void Dtool_FreeInstance_##CLASS_NAME(PyObject *self) {\
 #else // NDEBUG
 #else // NDEBUG
 #define Define_Dtool_FreeInstance_Private(CLASS_NAME,CNAME)\
 #define Define_Dtool_FreeInstance_Private(CLASS_NAME,CNAME)\
 static void Dtool_FreeInstance_##CLASS_NAME(PyObject *self) {\
 static void Dtool_FreeInstance_##CLASS_NAME(PyObject *self) {\
-  if (((Dtool_PyInstDef *)self)->_ptr_to_object != NULL) {\
+  if (DtoolInstance_VOID_PTR(self) != nullptr) {\
     if (((Dtool_PyInstDef *)self)->_memory_rules) {\
     if (((Dtool_PyInstDef *)self)->_memory_rules) {\
       cerr << "Detected leak for " << #CLASS_NAME \
       cerr << "Detected leak for " << #CLASS_NAME \
            << " which interrogate cannot delete.\n"; \
            << " which interrogate cannot delete.\n"; \
@@ -248,9 +139,9 @@ static void Dtool_FreeInstance_##CLASS_NAME(PyObject *self) {\
 
 
 #define Define_Dtool_FreeInstance(CLASS_NAME,CNAME)\
 #define Define_Dtool_FreeInstance(CLASS_NAME,CNAME)\
 static void Dtool_FreeInstance_##CLASS_NAME(PyObject *self) {\
 static void Dtool_FreeInstance_##CLASS_NAME(PyObject *self) {\
-  if (((Dtool_PyInstDef *)self)->_ptr_to_object != NULL) {\
+  if (DtoolInstance_VOID_PTR(self) != nullptr) {\
     if (((Dtool_PyInstDef *)self)->_memory_rules) {\
     if (((Dtool_PyInstDef *)self)->_memory_rules) {\
-      delete ((CNAME *)((Dtool_PyInstDef *)self)->_ptr_to_object);\
+      delete (CNAME *)DtoolInstance_VOID_PTR(self);\
     }\
     }\
   }\
   }\
   Py_TYPE(self)->tp_free(self);\
   Py_TYPE(self)->tp_free(self);\
@@ -258,9 +149,9 @@ static void Dtool_FreeInstance_##CLASS_NAME(PyObject *self) {\
 
 
 #define Define_Dtool_FreeInstanceRef(CLASS_NAME,CNAME)\
 #define Define_Dtool_FreeInstanceRef(CLASS_NAME,CNAME)\
 static void Dtool_FreeInstance_##CLASS_NAME(PyObject *self) {\
 static void Dtool_FreeInstance_##CLASS_NAME(PyObject *self) {\
-  if (((Dtool_PyInstDef *)self)->_ptr_to_object != NULL) {\
+  if (DtoolInstance_VOID_PTR(self) != nullptr) {\
     if (((Dtool_PyInstDef *)self)->_memory_rules) {\
     if (((Dtool_PyInstDef *)self)->_memory_rules) {\
-      unref_delete((CNAME *)((Dtool_PyInstDef *)self)->_ptr_to_object);\
+      unref_delete((CNAME *)DtoolInstance_VOID_PTR(self));\
     }\
     }\
   }\
   }\
   Py_TYPE(self)->tp_free(self);\
   Py_TYPE(self)->tp_free(self);\
@@ -272,8 +163,17 @@ static void Dtool_FreeInstance_##CLASS_NAME(PyObject *self) {\
   Py_TYPE(self)->tp_free(self);\
   Py_TYPE(self)->tp_free(self);\
 }
 }
 
 
-// Simple Recognition Functions..
-EXPCL_INTERROGATEDB bool DtoolCanThisBeAPandaInstance(PyObject *self);
+// Use DtoolInstance_Check to check whether a PyObject* is a DtoolInstance.
+#define DtoolInstance_Check(obj) \
+  (Py_TYPE(obj)->tp_basicsize >= (int)sizeof(Dtool_PyInstDef) && \
+   ((Dtool_PyInstDef *)obj)->_signature == PY_PANDA_SIGNATURE)
+
+// These macros access the DtoolInstance without error checking.
+#define DtoolInstance_TYPE(obj) (((Dtool_PyInstDef *)obj)->_My_Type)
+#define DtoolInstance_IS_CONST(obj) (((Dtool_PyInstDef *)obj)->_is_const)
+#define DtoolInstance_VOID_PTR(obj) (((Dtool_PyInstDef *)obj)->_ptr_to_object)
+#define DtoolInstance_INIT_PTR(obj, ptr) { ((Dtool_PyInstDef *)obj)->_ptr_to_object = (void*)(ptr); }
+#define DtoolInstance_UPCAST(obj, type) (((Dtool_PyInstDef *)(obj))->_My_Type->_Dtool_UpcastInterface((obj), &(type)))
 
 
 // ** HACK ** allert.. Need to keep a runtime type dictionary ... that is
 // ** HACK ** allert.. Need to keep a runtime type dictionary ... that is
 // forward declared of typed object.  We rely on the fact that typed objects
 // forward declared of typed object.  We rely on the fact that typed objects
@@ -301,22 +201,16 @@ EXPCL_INTERROGATEDB bool Dtool_Call_ExtractThisPointer(PyObject *self, Dtool_PyT
 EXPCL_INTERROGATEDB bool Dtool_Call_ExtractThisPointer_NonConst(PyObject *self, Dtool_PyTypedObject &classdef,
 EXPCL_INTERROGATEDB bool Dtool_Call_ExtractThisPointer_NonConst(PyObject *self, Dtool_PyTypedObject &classdef,
                                                               void **answer, const char *method_name);
                                                               void **answer, const char *method_name);
 
 
-template<class T> INLINE bool DTOOL_Call_ExtractThisPointer(PyObject *self, T *&into);
+template<class T> INLINE bool DtoolInstance_GetPointer(PyObject *self, T *&into);
+template<class T> INLINE bool DtoolInstance_GetPointer(PyObject *self, T *&into, Dtool_PyTypedObject &classdef);
 
 
 // Functions related to error reporting.
 // Functions related to error reporting.
 EXPCL_INTERROGATEDB bool _Dtool_CheckErrorOccurred();
 EXPCL_INTERROGATEDB bool _Dtool_CheckErrorOccurred();
 
 
-// _PyErr_OCCURRED is an undocumented macro version of PyErr_Occurred.
-// Some implementations of the CPython API (e.g. PyPy's cpyext) do not define
-// it, so in these cases we just silently fall back to PyErr_Occurred.
-#ifndef _PyErr_OCCURRED
-#define _PyErr_OCCURRED() PyErr_Occurred()
-#endif
-
 #ifdef NDEBUG
 #ifdef NDEBUG
-#define Dtool_CheckErrorOccurred() (_PyErr_OCCURRED() != NULL)
+#define Dtool_CheckErrorOccurred() (UNLIKELY(_PyErr_OCCURRED() != nullptr))
 #else
 #else
-#define Dtool_CheckErrorOccurred() _Dtool_CheckErrorOccurred()
+#define Dtool_CheckErrorOccurred() (UNLIKELY(_Dtool_CheckErrorOccurred()))
 #endif
 #endif
 
 
 EXPCL_INTERROGATEDB PyObject *Dtool_Raise_AssertionError();
 EXPCL_INTERROGATEDB PyObject *Dtool_Raise_AssertionError();
@@ -333,13 +227,16 @@ 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);
 
 
 #ifdef NDEBUG
 #ifdef NDEBUG
-#define Dtool_Return_None() (_PyErr_OCCURRED() != NULL ? NULL : (Py_INCREF(Py_None), Py_None))
-#define Dtool_Return(value) (_PyErr_OCCURRED() != NULL ? NULL : value)
+#define Dtool_Return_None() (LIKELY(_PyErr_OCCURRED() == nullptr) ? (Py_INCREF(Py_None), Py_None) : nullptr)
+#define Dtool_Return(value) (LIKELY(_PyErr_OCCURRED() == nullptr) ? value : nullptr)
 #else
 #else
 #define Dtool_Return_None() _Dtool_Return_None()
 #define Dtool_Return_None() _Dtool_Return_None()
 #define Dtool_Return(value) _Dtool_Return(value)
 #define Dtool_Return(value) _Dtool_Return(value)
@@ -458,20 +355,6 @@ copy_from_copy_constructor(PyObject *self, PyObject *noargs);
 EXPCL_INTERROGATEDB PyObject *
 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.
- */
-struct Dtool_SequenceWrapper {
-  PyObject_HEAD
-  PyObject *_base;
-  lenfunc _len_func;
-  ssizeargfunc _getitem_func;
-  ssizeobjargproc _setitem_func;
-};
-
-EXPCL_INTERROGATEDB extern PyTypeObject Dtool_SequenceWrapper_Type;
-
 /**
 /**
  * These functions check whether the arguments passed to a function conform to
  * These functions check whether the arguments passed to a function conform to
  * certain expectations.
  * certain expectations.
@@ -511,7 +394,7 @@ ALWAYS_INLINE PyObject *Dtool_WrapValue(char value);
 ALWAYS_INLINE PyObject *Dtool_WrapValue(wchar_t value);
 ALWAYS_INLINE PyObject *Dtool_WrapValue(wchar_t value);
 ALWAYS_INLINE PyObject *Dtool_WrapValue(nullptr_t);
 ALWAYS_INLINE PyObject *Dtool_WrapValue(nullptr_t);
 ALWAYS_INLINE PyObject *Dtool_WrapValue(PyObject *value);
 ALWAYS_INLINE PyObject *Dtool_WrapValue(PyObject *value);
-ALWAYS_INLINE PyObject *Dtool_WrapValue(const std::vector<unsigned char> &value);
+ALWAYS_INLINE PyObject *Dtool_WrapValue(const vector_uchar &value);
 
 
 #if PY_MAJOR_VERSION >= 0x02060000
 #if PY_MAJOR_VERSION >= 0x02060000
 ALWAYS_INLINE PyObject *Dtool_WrapValue(Py_buffer *value);
 ALWAYS_INLINE PyObject *Dtool_WrapValue(Py_buffer *value);
@@ -527,6 +410,8 @@ EXPCL_INTERROGATEDB extern void Dtool_PyModuleClassInit_DTOOL_SUPER_BASE(PyObjec
 
 
 #include "py_panda.I"
 #include "py_panda.I"
 
 
+#include "py_wrappers.h"
+
 #endif  // HAVE_PYTHON && !CPPPARSER
 #endif  // HAVE_PYTHON && !CPPPARSER
 
 
 #endif // PY_PANDA_H_
 #endif // PY_PANDA_H_

+ 1675 - 0
dtool/src/interrogatedb/py_wrappers.cxx

@@ -0,0 +1,1675 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file py_wrappers.cxx
+ * @author rdb
+ * @date 2017-11-26
+ */
+
+#include "py_wrappers.h"
+
+#ifdef HAVE_PYTHON
+
+#if PY_VERSION_HEX >= 0x03040000
+#define _COLLECTIONS_ABC "_collections_abc"
+#elif PY_VERSION_HEX >= 0x03030000
+#define _COLLECTIONS_ABC "collections.abc"
+#else
+#define _COLLECTIONS_ABC "_abcoll"
+#endif
+
+static void _register_collection(PyTypeObject *type, const char *abc) {
+  PyObject *sys_modules = PyImport_GetModuleDict();
+  if (sys_modules != nullptr) {
+    PyObject *module = PyDict_GetItemString(sys_modules, _COLLECTIONS_ABC);
+    if (module != nullptr) {
+      PyObject *dict = PyModule_GetDict(module);
+      if (module != nullptr) {
+#if PY_MAJOR_VERSION >= 3
+        static PyObject *register_str = PyUnicode_InternFromString("register");
+#else
+        static PyObject *register_str = PyString_InternFromString("register");
+#endif
+        PyObject *sequence = PyDict_GetItemString(dict, abc);
+        if (sequence != nullptr) {
+          if (PyObject_CallMethodObjArgs(sequence, register_str, (PyObject *)type, nullptr) == nullptr) {
+            PyErr_Print();
+          }
+        }
+      }
+    }
+  }
+}
+
+/**
+ * These classes are returned from properties that require a subscript
+ * interface, ie. something.children[i] = 3.
+ */
+static void Dtool_WrapperBase_dealloc(PyObject *self) {
+  Dtool_WrapperBase *wrap = (Dtool_WrapperBase *)self;
+  nassertv(wrap);
+  Py_XDECREF(wrap->_self);
+}
+
+static PyObject *Dtool_WrapperBase_repr(PyObject *self) {
+  Dtool_WrapperBase *wrap = (Dtool_WrapperBase *)self;
+  nassertr(wrap, nullptr);
+
+  PyObject *repr = PyObject_Repr(wrap->_self);
+  PyObject *result;
+#if PY_MAJOR_VERSION >= 3
+  result = PyUnicode_FromFormat("<%s[] of %s>", wrap->_name, PyUnicode_AsUTF8(repr));
+#else
+  result = PyString_FromFormat("<%s[] of %s>", wrap->_name, PyString_AS_STRING(repr));
+#endif
+  Py_DECREF(repr);
+  return result;
+}
+
+static PyObject *Dtool_SequenceWrapper_repr(PyObject *self) {
+  Dtool_SequenceWrapper *wrap = (Dtool_SequenceWrapper *)self;
+  nassertr(wrap, nullptr);
+
+  Py_ssize_t len = -1;
+  if (wrap->_len_func != nullptr) {
+    len = wrap->_len_func(wrap->_base._self);
+  }
+
+  if (len < 0) {
+    PyErr_Restore(nullptr, nullptr, nullptr);
+    return Dtool_WrapperBase_repr(self);
+  }
+
+  PyObject *repr = PyObject_Repr(wrap->_base._self);
+  PyObject *result;
+#if PY_MAJOR_VERSION >= 3
+  result = PyUnicode_FromFormat("<%s[%zd] of %s>", wrap->_base._name, len, PyUnicode_AsUTF8(repr));
+#else
+  result = PyString_FromFormat("<%s[%zd] of %s>", wrap->_base._name, len, PyString_AS_STRING(repr));
+#endif
+  Py_DECREF(repr);
+  return result;
+}
+
+static Py_ssize_t Dtool_SequenceWrapper_length(PyObject *self) {
+  Dtool_SequenceWrapper *wrap = (Dtool_SequenceWrapper *)self;
+  nassertr(wrap, -1);
+  if (wrap->_len_func != nullptr) {
+    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) {
+  Dtool_SequenceWrapper *wrap = (Dtool_SequenceWrapper *)self;
+  nassertr(wrap, nullptr);
+  nassertr(wrap->_getitem_func, nullptr);
+  return wrap->_getitem_func(wrap->_base._self, index);
+}
+
+/**
+ * Implementation of (x in property)
+ */
+static int Dtool_SequenceWrapper_contains(PyObject *self, PyObject *value) {
+  Dtool_SequenceWrapper *wrap = (Dtool_SequenceWrapper *)self;
+  nassertr(wrap, -1);
+  nassertr(wrap->_len_func, -1);
+  nassertr(wrap->_getitem_func, -1);
+
+  Py_ssize_t length = wrap->_len_func(wrap->_base._self);
+
+  // Iterate through the items, invoking the equality function for each, until
+  // we have found the matching one.
+  for (Py_ssize_t index = 0; index < length; ++index) {
+    PyObject *item = wrap->_getitem_func(wrap->_base._self, index);
+    if (item != nullptr) {
+      int cmp = PyObject_RichCompareBool(item, value, Py_EQ);
+      if (cmp > 0) {
+        return 1;
+      }
+      if (cmp < 0) {
+        return -1;
+      }
+    } else {
+      return -1;
+    }
+  }
+  return 0;
+}
+
+/**
+ * Implementation of property.index(x) which returns the index of the first
+ * occurrence of x in the sequence, or raises a ValueError if it isn't found.
+ */
+static PyObject *Dtool_SequenceWrapper_index(PyObject *self, PyObject *value) {
+  Dtool_SequenceWrapper *wrap = (Dtool_SequenceWrapper *)self;
+  nassertr(wrap, nullptr);
+  nassertr(wrap->_len_func, nullptr);
+  nassertr(wrap->_getitem_func, nullptr);
+
+  Py_ssize_t length = wrap->_len_func(wrap->_base._self);
+
+  // Iterate through the items, invoking the equality function for each, until
+  // we have found the right one.
+  for (Py_ssize_t index = 0; index < length; ++index) {
+    PyObject *item = wrap->_getitem_func(wrap->_base._self, index);
+    if (item != nullptr) {
+      int cmp = PyObject_RichCompareBool(item, value, Py_EQ);
+      if (cmp > 0) {
+        return Dtool_WrapValue(index);
+      }
+      if (cmp < 0) {
+        return nullptr;
+      }
+    } else {
+      return nullptr;
+    }
+  }
+  // Not found, raise ValueError.
+  return PyErr_Format(PyExc_ValueError, "%s.index() did not find value", wrap->_base._name);
+}
+
+/**
+ * Implementation of property.count(x) which returns the number of occurrences
+ * of x in the sequence.
+ */
+static PyObject *Dtool_SequenceWrapper_count(PyObject *self, PyObject *value) {
+  Dtool_SequenceWrapper *wrap = (Dtool_SequenceWrapper *)self;
+  nassertr(wrap, nullptr);
+  Py_ssize_t index = 0;
+  if (wrap->_len_func != nullptr) {
+    index = wrap->_len_func(wrap->_base._self);
+  } else {
+    return Dtool_Raise_TypeError("property does not support count()");
+  }
+  // Iterate through the items, invoking the == operator for each.
+  long count = 0;
+  nassertr(wrap->_getitem_func, nullptr);
+  while (index > 0) {
+    --index;
+    PyObject *item = wrap->_getitem_func(wrap->_base._self, index);
+    if (item == nullptr) {
+      return nullptr;
+    }
+    int cmp = PyObject_RichCompareBool(item, value, Py_EQ);
+    if (cmp > 0) {
+      ++count;
+    }
+    if (cmp < 0) {
+      return nullptr;
+    }
+  }
+#if PY_MAJOR_VERSION >= 3
+  return PyLong_FromLong(count);
+#else
+  return PyInt_FromLong(count);
+#endif
+}
+
+/**
+ * Implementation of `property[i] = x`
+ */
+static int Dtool_MutableSequenceWrapper_setitem(PyObject *self, Py_ssize_t index, PyObject *value) {
+  Dtool_MutableSequenceWrapper *wrap = (Dtool_MutableSequenceWrapper *)self;
+  nassertr(wrap, -1);
+  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;
+  }
+}
+
+/**
+ * Implementation of property.clear() which removes all elements in the
+ * sequence, starting with the last.
+ */
+static PyObject *Dtool_MutableSequenceWrapper_clear(PyObject *self, PyObject *) {
+  Dtool_MutableSequenceWrapper *wrap = (Dtool_MutableSequenceWrapper *)self;
+  nassertr(wrap, nullptr);
+  Py_ssize_t index = 0;
+  if (wrap->_len_func != nullptr && wrap->_setitem_func != nullptr) {
+    index = wrap->_len_func(wrap->_base._self);
+  } else {
+    return Dtool_Raise_TypeError("property does not support clear()");
+  }
+
+  // Iterate through the items, invoking the delete function for each.  We do
+  // this in reverse order, which may be more efficient.
+  while (index > 0) {
+    --index;
+    if (wrap->_setitem_func(wrap->_base._self, index, nullptr) != 0) {
+      return nullptr;
+    }
+  }
+  Py_INCREF(Py_None);
+  return Py_None;
+}
+
+/**
+ * Implementation of property.remove(x) which removes the first occurrence of
+ * x in the sequence, or raises a ValueError if it isn't found.
+ */
+static PyObject *Dtool_MutableSequenceWrapper_remove(PyObject *self, PyObject *value) {
+  Dtool_MutableSequenceWrapper *wrap = (Dtool_MutableSequenceWrapper *)self;
+  nassertr(wrap, nullptr);
+  Py_ssize_t length = 0;
+  if (wrap->_len_func != nullptr && wrap->_setitem_func != nullptr) {
+    length = wrap->_len_func(wrap->_base._self);
+  } else {
+    return Dtool_Raise_TypeError("property does not support remove()");
+  }
+
+  // Iterate through the items, invoking the equality function for each, until
+  // we have found the right one.
+  nassertr(wrap->_getitem_func, nullptr);
+  for (Py_ssize_t index = 0; index < length; ++index) {
+    PyObject *item = wrap->_getitem_func(wrap->_base._self, index);
+    if (item != nullptr) {
+      int cmp = PyObject_RichCompareBool(item, value, Py_EQ);
+      if (cmp > 0) {
+        if (wrap->_setitem_func(wrap->_base._self, index, nullptr) == 0) {
+          Py_INCREF(Py_None);
+          return Py_None;
+        } else {
+          return nullptr;
+        }
+      }
+      if (cmp < 0) {
+        return nullptr;
+      }
+    } else {
+      return nullptr;
+    }
+  }
+  // Not found, raise ValueError.
+  return PyErr_Format(PyExc_ValueError, "%s.remove() did not find value", wrap->_base._name);
+}
+
+/**
+ * Implementation of property.pop([i=-1]) which returns and removes the
+ * element at the indicated index in the sequence.  If no index is provided,
+ * it removes from the end of the list.
+ */
+static PyObject *Dtool_MutableSequenceWrapper_pop(PyObject *self, PyObject *args) {
+  Dtool_MutableSequenceWrapper *wrap = (Dtool_MutableSequenceWrapper *)self;
+  nassertr(wrap, nullptr);
+  if (wrap->_getitem_func == nullptr || wrap->_setitem_func == nullptr ||
+      wrap->_len_func == nullptr) {
+    return Dtool_Raise_TypeError("property does not support pop()");
+  }
+
+  Py_ssize_t length = wrap->_len_func(wrap->_base._self);
+  Py_ssize_t index;
+  switch (PyTuple_GET_SIZE(args)) {
+  case 0:
+    index = length - 1;
+    break;
+  case 1:
+    index = PyNumber_AsSsize_t(PyTuple_GET_ITEM(args, 0), PyExc_IndexError);
+    if (index == -1 && _PyErr_OCCURRED()) {
+      return nullptr;
+    }
+    if (index < 0) {
+      index += length;
+    }
+    break;
+  default:
+    return Dtool_Raise_TypeError("pop([i=-1]) takes 0 or 1 arguments");
+  }
+
+  if (length <= 0) {
+    return PyErr_Format(PyExc_IndexError, "%s.pop() from empty sequence", wrap->_base._name);
+  }
+
+  // Index error will be caught by getitem_func.
+  PyObject *value = wrap->_getitem_func(wrap->_base._self, index);
+  if (value != nullptr) {
+    if (wrap->_setitem_func(wrap->_base._self, index, nullptr) != 0) {
+      return nullptr;
+    }
+    return value;
+  }
+  return nullptr;
+}
+
+/**
+ * Implementation of property.append(x) which is an alias for
+ * property.insert(len(property), x).
+ */
+static PyObject *Dtool_MutableSequenceWrapper_append(PyObject *self, PyObject *arg) {
+  Dtool_MutableSequenceWrapper *wrap = (Dtool_MutableSequenceWrapper *)self;
+  nassertr(wrap, nullptr);
+  if (wrap->_insert_func == nullptr) {
+    return Dtool_Raise_TypeError("property does not support append()");
+  }
+  return wrap->_insert_func(wrap->_base._self, (size_t)-1, arg);
+}
+
+/**
+ * Implementation of property.insert(i, x) which inserts the given item at the
+ * given position.
+ */
+static PyObject *Dtool_MutableSequenceWrapper_insert(PyObject *self, PyObject *args) {
+  Dtool_MutableSequenceWrapper *wrap = (Dtool_MutableSequenceWrapper *)self;
+  nassertr(wrap, nullptr);
+  if (wrap->_insert_func == nullptr) {
+    return Dtool_Raise_TypeError("property does not support insert()");
+  }
+  if (PyTuple_GET_SIZE(args) != 2) {
+    return Dtool_Raise_TypeError("insert() takes exactly 2 arguments");
+  }
+  Py_ssize_t index = PyNumber_AsSsize_t(PyTuple_GET_ITEM(args, 0), PyExc_IndexError);
+  if (index == -1 && _PyErr_OCCURRED()) {
+    return nullptr;
+  }
+  if (index < 0) {
+    if (wrap->_len_func != nullptr) {
+      index += wrap->_len_func(wrap->_base._self);
+    } else {
+      return PyErr_Format(PyExc_TypeError, "%s.insert() does not support negative indices", wrap->_base._name);
+    }
+  }
+  return wrap->_insert_func(wrap->_base._self, (ssize_t)max(index, (Py_ssize_t)0), PyTuple_GET_ITEM(args, 1));
+}
+
+/**
+ * Implementation of property.extend(seq) which is equivalent to:
+ * @code
+ * for x in seq:
+ *   property.append(seq)
+ * @endcode
+ */
+static PyObject *Dtool_MutableSequenceWrapper_extend(PyObject *self, PyObject *arg) {
+  Dtool_MutableSequenceWrapper *wrap = (Dtool_MutableSequenceWrapper *)self;
+  nassertr(wrap, nullptr);
+  if (wrap->_insert_func == nullptr) {
+    return Dtool_Raise_TypeError("property does not support extend()");
+  }
+  PyObject *iter = PyObject_GetIter(arg);
+  if (iter == nullptr) {
+    return nullptr;
+  }
+  PyObject *next = PyIter_Next(iter);
+  PyObject *retval = nullptr;
+  while (next != nullptr) {
+    retval = wrap->_insert_func(wrap->_base._self, (size_t)-1, next);
+    Py_DECREF(next);
+    if (retval == nullptr) {
+      Py_DECREF(iter);
+      return nullptr;
+    }
+    Py_DECREF(retval);
+    next = PyIter_Next(iter);
+  }
+
+  Py_DECREF(iter);
+  Py_INCREF(Py_None);
+  return Py_None;
+}
+
+/**
+ * Implementation of `x in mapping`.
+ */
+static int Dtool_MappingWrapper_contains(PyObject *self, PyObject *key) {
+  Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)self;
+  nassertr(wrap, -1);
+  nassertr(wrap->_getitem_func, -1);
+  PyObject *value = wrap->_getitem_func(wrap->_base._self, key);
+  if (value != nullptr) {
+    Py_DECREF(value);
+    return 1;
+  } else if (_PyErr_OCCURRED() == PyExc_KeyError ||
+             _PyErr_OCCURRED() == PyExc_TypeError) {
+    PyErr_Restore(nullptr, nullptr, nullptr);
+    return 0;
+  } else {
+    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);
+}
+
+/**
+ * Implementation of iter(property) that returns an iterable over all the
+ * keys.
+ */
+static PyObject *Dtool_MappingWrapper_iter(PyObject *self) {
+  Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)self;
+  nassertr(wrap, nullptr);
+
+  if (wrap->_keys._len_func == nullptr || wrap->_keys._getitem_func == nullptr) {
+    return PyErr_Format(PyExc_TypeError, "%s is not iterable", wrap->_base._name);
+  }
+
+  Dtool_SequenceWrapper *keys = Dtool_NewSequenceWrapper(wrap->_base._self, wrap->_base._name);
+  if (keys != nullptr) {
+    keys->_len_func = wrap->_keys._len_func;
+    keys->_getitem_func = wrap->_keys._getitem_func;
+    return PySeqIter_New((PyObject *)keys);
+  } else {
+    return nullptr;
+  }
+}
+
+/**
+ * Implementation of property.get(key[,def=None]) which returns the value with
+ * the given key in the mapping, or the given default value (which defaults to
+ * None) if the key isn't found in the mapping.
+ */
+static PyObject *Dtool_MappingWrapper_get(PyObject *self, PyObject *args) {
+  Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)self;
+  nassertr(wrap, nullptr);
+  nassertr(wrap->_getitem_func, nullptr);
+  Py_ssize_t size = PyTuple_GET_SIZE(args);
+  if (size != 1 && size != 2) {
+    return PyErr_Format(PyExc_TypeError, "%s.get() takes 1 or 2 arguments", wrap->_base._name);
+  }
+  PyObject *defvalue = Py_None;
+  if (size >= 2) {
+    defvalue = PyTuple_GET_ITEM(args, 1);
+  }
+  PyObject *key = PyTuple_GET_ITEM(args, 0);
+  PyObject *value = wrap->_getitem_func(wrap->_base._self, key);
+  if (value != nullptr) {
+    return value;
+  } else if (_PyErr_OCCURRED() == PyExc_KeyError) {
+    PyErr_Restore(nullptr, nullptr, nullptr);
+    Py_INCREF(defvalue);
+    return defvalue;
+  } else {
+    return nullptr;
+  }
+}
+
+/**
+ * Implementation of property.keys(...) that returns a view of all the keys.
+ */
+static PyObject *Dtool_MappingWrapper_keys(PyObject *self, PyObject *) {
+  Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)self;
+  nassertr(wrap, nullptr);
+
+  if (wrap->_keys._len_func == nullptr || wrap->_keys._getitem_func == nullptr) {
+    return Dtool_Raise_TypeError("property does not support keys()");
+  }
+
+  Dtool_MappingWrapper *keys = (Dtool_MappingWrapper *)PyObject_MALLOC(sizeof(Dtool_MappingWrapper));
+  if (keys == nullptr) {
+    return PyErr_NoMemory();
+  }
+
+  // If the collections.abc module is loaded, register this as a subclass.
+  static bool registered = false;
+  if (!registered) {
+    registered = true;
+    _register_collection((PyTypeObject *)&Dtool_MappingWrapper_Keys_Type, "MappingView");
+  }
+
+  PyObject_INIT(keys, &Dtool_MappingWrapper_Keys_Type);
+  Py_XINCREF(wrap->_base._self);
+  keys->_base._self = wrap->_base._self;
+  keys->_base._name = wrap->_base._name;
+  keys->_keys._len_func = wrap->_keys._len_func;
+  keys->_keys._getitem_func = wrap->_keys._getitem_func;
+  keys->_getitem_func = wrap->_getitem_func;
+  keys->_setitem_func = nullptr;
+  return (PyObject *)keys;
+}
+
+/**
+ * Implementation of property.values(...) that returns a view of the values.
+ */
+static PyObject *Dtool_MappingWrapper_values(PyObject *self, PyObject *) {
+  Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)self;
+  nassertr(wrap, nullptr);
+  nassertr(wrap->_getitem_func, nullptr);
+
+  if (wrap->_keys._len_func == nullptr || wrap->_keys._getitem_func == nullptr) {
+    return Dtool_Raise_TypeError("property does not support values()");
+  }
+
+  Dtool_MappingWrapper *values = (Dtool_MappingWrapper *)PyObject_MALLOC(sizeof(Dtool_MappingWrapper));
+  if (values == nullptr) {
+    return PyErr_NoMemory();
+  }
+
+  // If the collections.abc module is loaded, register this as a subclass.
+  static bool registered = false;
+  if (!registered) {
+    registered = true;
+    _register_collection((PyTypeObject *)&Dtool_MappingWrapper_Values_Type, "ValuesView");
+  }
+
+  PyObject_INIT(values, &Dtool_MappingWrapper_Values_Type);
+  Py_XINCREF(wrap->_base._self);
+  values->_base._self = wrap->_base._self;
+  values->_base._name = wrap->_base._name;
+  values->_keys._len_func = wrap->_keys._len_func;
+  values->_keys._getitem_func = wrap->_keys._getitem_func;
+  values->_getitem_func = wrap->_getitem_func;
+  values->_setitem_func = nullptr;
+  return (PyObject *)values;
+}
+
+/**
+ * Implementation of property.items(...) that returns an iterable yielding a
+ * `(key, value)` tuple for every item.
+ */
+static PyObject *Dtool_MappingWrapper_items(PyObject *self, PyObject *) {
+  Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)self;
+  nassertr(wrap, nullptr);
+  nassertr(wrap->_getitem_func, nullptr);
+
+  if (wrap->_keys._len_func == nullptr || wrap->_keys._getitem_func == nullptr) {
+    return Dtool_Raise_TypeError("property does not support items()");
+  }
+
+  Dtool_MappingWrapper *items = (Dtool_MappingWrapper *)PyObject_MALLOC(sizeof(Dtool_MappingWrapper));
+  if (items == nullptr) {
+    return PyErr_NoMemory();
+  }
+
+  // If the collections.abc module is loaded, register this as a subclass.
+  static bool registered = false;
+  if (!registered) {
+    registered = true;
+    _register_collection((PyTypeObject *)&Dtool_MappingWrapper_Items_Type, "MappingView");
+  }
+
+  PyObject_INIT(items, &Dtool_MappingWrapper_Items_Type);
+  Py_XINCREF(wrap->_base._self);
+  items->_base._self = wrap->_base._self;
+  items->_base._name = wrap->_base._name;
+  items->_keys._len_func = wrap->_keys._len_func;
+  items->_keys._getitem_func = wrap->_keys._getitem_func;
+  items->_getitem_func = wrap->_getitem_func;
+  items->_setitem_func = nullptr;
+  return (PyObject *)items;
+}
+
+/**
+ * Implementation of `property[key] = value`
+ */
+static int Dtool_MutableMappingWrapper_setitem(PyObject *self, PyObject *key, PyObject *value) {
+  Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)self;
+  nassertr(wrap->_setitem_func != nullptr, -1);
+  return wrap->_setitem_func(wrap->_base._self, key, value);
+}
+
+/**
+ * Implementation of property.pop(key[,def=None]) which is the same as get()
+ * except that it also removes the element from the mapping.
+ */
+static PyObject *Dtool_MutableMappingWrapper_pop(PyObject *self, PyObject *args) {
+  Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)self;
+  nassertr(wrap, nullptr);
+  if (wrap->_getitem_func == nullptr || wrap->_setitem_func == nullptr) {
+    return Dtool_Raise_TypeError("property does not support pop()");
+  }
+
+  Py_ssize_t size = PyTuple_GET_SIZE(args);
+  if (size != 1 && size != 2) {
+    return PyErr_Format(PyExc_TypeError, "%s.pop() takes 1 or 2 arguments", wrap->_base._name);
+  }
+  PyObject *defvalue = Py_None;
+  if (size >= 2) {
+    defvalue = PyTuple_GET_ITEM(args, 1);
+  }
+
+  PyObject *key = PyTuple_GET_ITEM(args, 0);
+  PyObject *value = wrap->_getitem_func(wrap->_base._self, key);
+  if (value != nullptr) {
+    // OK, now set unset this value.
+    if (wrap->_setitem_func(wrap->_base._self, key, nullptr) == 0) {
+      return value;
+    } else {
+      Py_DECREF(value);
+      return nullptr;
+    }
+  } else if (_PyErr_OCCURRED() == PyExc_KeyError) {
+    PyErr_Restore(nullptr, nullptr, nullptr);
+    Py_INCREF(defvalue);
+    return defvalue;
+  } else {
+    return nullptr;
+  }
+}
+
+/**
+ * Implementation of property.popitem() which returns and removes an arbitrary
+ * (key, value) pair from the mapping.  Useful for destructive iteration.
+ */
+static PyObject *Dtool_MutableMappingWrapper_popitem(PyObject *self, PyObject *) {
+  Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)self;
+  nassertr(wrap, nullptr);
+  if (wrap->_getitem_func == nullptr || wrap->_setitem_func == nullptr ||
+      wrap->_keys._len_func == nullptr || wrap->_keys._getitem_func == nullptr) {
+    return Dtool_Raise_TypeError("property does not support popitem()");
+  }
+
+  Py_ssize_t length = wrap->_keys._len_func(wrap->_base._self);
+  if (length < 1) {
+    return PyErr_Format(PyExc_KeyError, "%s is empty", wrap->_base._name);
+  }
+
+  PyObject *key = wrap->_keys._getitem_func(wrap->_base._self, length - 1);
+  if (key != nullptr) {
+    PyObject *value = wrap->_getitem_func(wrap->_base._self, key);
+    if (value != nullptr) {
+      // OK, now set unset this value.
+      if (wrap->_setitem_func(wrap->_base._self, key, nullptr) == 0) {
+        PyObject *item = PyTuple_New(2);
+        PyTuple_SET_ITEM(item, 0, key);
+        PyTuple_SET_ITEM(item, 1, value);
+        return item;
+      }
+      Py_DECREF(value);
+    }
+  }
+  return nullptr;
+}
+
+/*
+ * Implementation of property.clear() which removes all elements in the
+ * mapping.
+ */
+static PyObject *Dtool_MutableMappingWrapper_clear(PyObject *self, PyObject *) {
+  Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)self;
+  nassertr(wrap, nullptr);
+  Py_ssize_t index = 0;
+  if (wrap->_keys._len_func != nullptr && wrap->_keys._getitem_func != nullptr &&
+      wrap->_setitem_func != nullptr) {
+    index = wrap->_keys._len_func(wrap->_base._self);
+  } else {
+    return Dtool_Raise_TypeError("property does not support clear()");
+  }
+
+  // Iterate through the items, invoking the delete function for each.  We do
+  // this in reverse order, which may be more efficient.
+  while (index > 0) {
+    --index;
+    PyObject *key = wrap->_keys._getitem_func(wrap->_base._self, index);
+    if (key != nullptr) {
+      int result = wrap->_setitem_func(wrap->_base._self, key, nullptr);
+      Py_DECREF(key);
+      if (result != 0) {
+        return nullptr;
+      }
+    }
+  }
+  Py_INCREF(Py_None);
+  return Py_None;
+}
+
+/**
+ * Implementation of property.setdefault(key[,def=None]) which is the same as
+ * get() except that it also writes the default value back to the mapping if
+ * the key was not found is missing.
+ */
+static PyObject *Dtool_MutableMappingWrapper_setdefault(PyObject *self, PyObject *args) {
+  Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)self;
+  nassertr(wrap, nullptr);
+
+  if (wrap->_getitem_func == nullptr || wrap->_setitem_func == nullptr) {
+    return Dtool_Raise_TypeError("property does not support setdefault()");
+  }
+
+  Py_ssize_t size = PyTuple_GET_SIZE(args);
+  if (size != 1 && size != 2) {
+    return PyErr_Format(PyExc_TypeError, "%s.setdefault() takes 1 or 2 arguments", wrap->_base._name);
+  }
+  PyObject *defvalue = Py_None;
+  if (size >= 2) {
+    defvalue = PyTuple_GET_ITEM(args, 1);
+  }
+  PyObject *key = PyTuple_GET_ITEM(args, 0);
+  PyObject *value = wrap->_getitem_func(wrap->_base._self, key);
+  if (value != nullptr) {
+    return value;
+  } else if (_PyErr_OCCURRED() == PyExc_KeyError) {
+    PyErr_Restore(nullptr, nullptr, nullptr);
+    if (wrap->_setitem_func(wrap->_base._self, key, defvalue) == 0) {
+      Py_INCREF(defvalue);
+      return defvalue;
+    }
+  }
+  return nullptr;
+}
+
+/**
+ * Implementation of property.update(...) which sets multiple values in one
+ * go.  It accepts either a single dictionary or keyword arguments, not both.
+ */
+static PyObject *Dtool_MutableMappingWrapper_update(PyObject *self, PyObject *args, PyObject *kwargs) {
+  Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)self;
+  nassertr(wrap, nullptr);
+
+  if (wrap->_getitem_func == nullptr || wrap->_setitem_func == nullptr) {
+    return Dtool_Raise_TypeError("property does not support update()");
+  }
+
+  // We accept either a dict argument or keyword arguments, but not both.
+  PyObject *dict;
+  switch (PyTuple_GET_SIZE(args)) {
+  case 0:
+    if (kwargs == nullptr) {
+      // This is legal.
+      Py_INCREF(Py_None);
+      return Py_None;
+    }
+    dict = kwargs;
+    break;
+  case 1:
+    if (PyDict_Check(PyTuple_GET_ITEM(args, 0)) && (kwargs == nullptr || Py_SIZE(kwargs) == 0)) {
+      dict = PyTuple_GET_ITEM(args, 0);
+      break;
+    }
+    // Fall through
+  default:
+    return PyErr_Format(PyExc_TypeError, "%s.update() takes either a dict argument or keyword arguments", wrap->_base._name);
+  }
+
+  PyObject *key, *value;
+  Py_ssize_t pos = 0;
+  while (PyDict_Next(dict, &pos, &key, &value)) {
+    if (wrap->_setitem_func(wrap->_base._self, key, value) != 0) {
+      return nullptr;
+    }
+  }
+  Py_INCREF(Py_None);
+  return Py_None;
+}
+
+/**
+ * This variant defines only a sequence interface.
+ */
+static PySequenceMethods Dtool_SequenceWrapper_SequenceMethods = {
+  Dtool_SequenceWrapper_length,
+  0, // sq_concat
+  0, // sq_repeat
+  Dtool_SequenceWrapper_getitem,
+  0, // sq_slice
+  0, // sq_ass_item
+  0, // sq_ass_slice
+  Dtool_SequenceWrapper_contains,
+  0, // sq_inplace_concat
+  0, // sq_inplace_repeat
+};
+
+static PyMethodDef Dtool_SequenceWrapper_Methods[] = {
+  {"index", &Dtool_SequenceWrapper_index, METH_O, nullptr},
+  {"count", &Dtool_SequenceWrapper_count, METH_O, nullptr},
+  {nullptr, nullptr, 0, nullptr}
+};
+
+PyTypeObject Dtool_SequenceWrapper_Type = {
+  PyVarObject_HEAD_INIT(nullptr, 0)
+  "sequence wrapper",
+  sizeof(Dtool_SequenceWrapper),
+  0, // tp_itemsize
+  Dtool_WrapperBase_dealloc,
+  0, // tp_print
+  0, // tp_getattr
+  0, // tp_setattr
+  0, // tp_compare
+  Dtool_SequenceWrapper_repr,
+  0, // tp_as_number
+  &Dtool_SequenceWrapper_SequenceMethods,
+  0, // tp_as_mapping
+  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
+  PySeqIter_New,
+  0, // tp_iternext
+  Dtool_SequenceWrapper_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
+};
+
+/**
+ * This is a variant on SequenceWrapper that also has an insert() method.
+ */
+static PySequenceMethods Dtool_MutableSequenceWrapper_SequenceMethods = {
+  Dtool_SequenceWrapper_length,
+  0, // sq_concat
+  0, // sq_repeat
+  Dtool_SequenceWrapper_getitem,
+  0, // sq_slice
+  Dtool_MutableSequenceWrapper_setitem,
+  0, // sq_ass_slice
+  Dtool_SequenceWrapper_contains,
+  Dtool_MutableSequenceWrapper_extend,
+  0, // sq_inplace_repeat
+};
+
+static PyMethodDef Dtool_MutableSequenceWrapper_Methods[] = {
+  {"index", &Dtool_SequenceWrapper_index, METH_O, nullptr},
+  {"count", &Dtool_SequenceWrapper_count, METH_O, nullptr},
+  {"clear", &Dtool_MutableSequenceWrapper_clear, METH_NOARGS, nullptr},
+  {"pop", &Dtool_MutableSequenceWrapper_pop, METH_VARARGS, nullptr},
+  {"remove", &Dtool_MutableSequenceWrapper_remove, METH_O, nullptr},
+  {"append", &Dtool_MutableSequenceWrapper_append, METH_O, nullptr},
+  {"insert", &Dtool_MutableSequenceWrapper_insert, METH_VARARGS, nullptr},
+  {"extend", &Dtool_MutableSequenceWrapper_extend, METH_O, nullptr},
+  {nullptr, nullptr, 0, nullptr}
+};
+
+PyTypeObject Dtool_MutableSequenceWrapper_Type = {
+  PyVarObject_HEAD_INIT(nullptr, 0)
+  "sequence wrapper",
+  sizeof(Dtool_MutableSequenceWrapper),
+  0, // tp_itemsize
+  Dtool_WrapperBase_dealloc,
+  0, // tp_print
+  0, // tp_getattr
+  0, // tp_setattr
+  0, // tp_compare
+  Dtool_SequenceWrapper_repr,
+  0, // tp_as_number
+  &Dtool_MutableSequenceWrapper_SequenceMethods,
+  0, // tp_as_mapping
+  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
+  PySeqIter_New,
+  0, // tp_iternext
+  Dtool_MutableSequenceWrapper_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
+};
+
+/**
+ * This variant defines only a mapping interface.
+ */
+static PySequenceMethods Dtool_MappingWrapper_SequenceMethods = {
+  Dtool_SequenceWrapper_length,
+  0, // sq_concat
+  0, // sq_repeat
+  0, // sq_item
+  0, // sq_slice
+  0, // sq_ass_item
+  0, // sq_ass_slice
+  Dtool_MappingWrapper_contains,
+  0, // sq_inplace_concat
+  0, // sq_inplace_repeat
+};
+
+static PyMappingMethods Dtool_MappingWrapper_MappingMethods = {
+  Dtool_SequenceWrapper_length,
+  Dtool_MappingWrapper_getitem,
+  0, // mp_ass_subscript
+};
+
+static PyMethodDef Dtool_MappingWrapper_Methods[] = {
+  {"get", &Dtool_MappingWrapper_get, METH_VARARGS, nullptr},
+  {"keys", &Dtool_MappingWrapper_keys, METH_NOARGS, nullptr},
+  {"values", &Dtool_MappingWrapper_values, METH_NOARGS, nullptr},
+  {"items", &Dtool_MappingWrapper_items, METH_NOARGS, nullptr},
+  {nullptr, nullptr, 0, nullptr}
+};
+
+PyTypeObject Dtool_MappingWrapper_Type = {
+  PyVarObject_HEAD_INIT(nullptr, 0)
+  "mapping wrapper",
+  sizeof(Dtool_MappingWrapper),
+  0, // tp_itemsize
+  Dtool_WrapperBase_dealloc,
+  0, // tp_print
+  0, // tp_getattr
+  0, // tp_setattr
+  0, // tp_compare
+  Dtool_WrapperBase_repr,
+  0, // tp_as_number
+  &Dtool_MappingWrapper_SequenceMethods,
+  &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
+  Dtool_MappingWrapper_iter,
+  0, // tp_iternext
+  Dtool_MappingWrapper_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
+};
+
+/**
+ * This variant defines only a mutable mapping interface.
+ */
+static PyMappingMethods Dtool_MutableMappingWrapper_MappingMethods = {
+  Dtool_SequenceWrapper_length,
+  Dtool_MappingWrapper_getitem,
+  Dtool_MutableMappingWrapper_setitem,
+};
+
+static PyMethodDef Dtool_MutableMappingWrapper_Methods[] = {
+  {"get", &Dtool_MappingWrapper_get, METH_VARARGS, nullptr},
+  {"pop", &Dtool_MutableMappingWrapper_pop, METH_VARARGS, nullptr},
+  {"popitem", &Dtool_MutableMappingWrapper_popitem, METH_NOARGS, nullptr},
+  {"clear", &Dtool_MutableMappingWrapper_clear, METH_VARARGS, nullptr},
+  {"setdefault", &Dtool_MutableMappingWrapper_setdefault, METH_VARARGS, nullptr},
+  {"update", (PyCFunction) &Dtool_MutableMappingWrapper_update, METH_VARARGS | METH_KEYWORDS, nullptr},
+  {"keys", &Dtool_MappingWrapper_keys, METH_NOARGS, nullptr},
+  {"values", &Dtool_MappingWrapper_values, METH_NOARGS, nullptr},
+  {"items", &Dtool_MappingWrapper_items, METH_NOARGS, nullptr},
+  {nullptr, nullptr, 0, nullptr}
+};
+
+PyTypeObject Dtool_MutableMappingWrapper_Type = {
+  PyVarObject_HEAD_INIT(nullptr, 0)
+  "mapping wrapper",
+  sizeof(Dtool_MappingWrapper),
+  0, // tp_itemsize
+  Dtool_WrapperBase_dealloc,
+  0, // tp_print
+  0, // tp_getattr
+  0, // tp_setattr
+  0, // tp_compare
+  Dtool_WrapperBase_repr,
+  0, // tp_as_number
+  &Dtool_MappingWrapper_SequenceMethods,
+  &Dtool_MutableMappingWrapper_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
+  Dtool_MappingWrapper_iter,
+  0, // tp_iternext
+  Dtool_MutableMappingWrapper_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
+};
+
+/**
+ * This is returned by mapping.items().
+ */
+static PyObject *Dtool_MappingWrapper_Items_repr(PyObject *self) {
+  Dtool_WrapperBase *wrap = (Dtool_WrapperBase *)self;
+  nassertr(wrap, nullptr);
+
+  PyObject *repr = PyObject_Repr(wrap->_self);
+  PyObject *result;
+#if PY_MAJOR_VERSION >= 3
+  result = PyUnicode_FromFormat("<%s.items() of %s>", wrap->_name, PyUnicode_AsUTF8(repr));
+#else
+  result = PyString_FromFormat("<%s.items() of %s>", wrap->_name, PyString_AS_STRING(repr));
+#endif
+  Py_DECREF(repr);
+  return result;
+}
+
+static PyObject *Dtool_MappingWrapper_Items_getitem(PyObject *self, Py_ssize_t index) {
+  Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)self;
+  nassertr(wrap, nullptr);
+  nassertr(wrap->_keys._getitem_func, nullptr);
+
+  PyObject *key = wrap->_keys._getitem_func(wrap->_base._self, index);
+  if (key != nullptr) {
+    PyObject *value = wrap->_getitem_func(wrap->_base._self, key);
+    if (value != nullptr) {
+      // PyTuple_SET_ITEM steals the reference.
+      PyObject *item = PyTuple_New(2);
+      PyTuple_SET_ITEM(item, 0, key);
+      PyTuple_SET_ITEM(item, 1, value);
+      return item;
+    } else {
+      Py_DECREF(key);
+    }
+  }
+  return nullptr;
+}
+
+static PySequenceMethods Dtool_MappingWrapper_Items_SequenceMethods = {
+  Dtool_SequenceWrapper_length,
+  0, // sq_concat
+  0, // sq_repeat
+  Dtool_MappingWrapper_Items_getitem,
+  0, // sq_slice
+  0, // sq_ass_item
+  0, // sq_ass_slice
+  Dtool_MappingWrapper_contains,
+  0, // sq_inplace_concat
+  0, // sq_inplace_repeat
+};
+
+PyTypeObject Dtool_MappingWrapper_Items_Type = {
+  PyVarObject_HEAD_INIT(nullptr, 0)
+  "sequence wrapper",
+  sizeof(Dtool_MappingWrapper),
+  0, // tp_itemsize
+  Dtool_WrapperBase_dealloc,
+  0, // tp_print
+  0, // tp_getattr
+  0, // tp_setattr
+  0, // tp_compare
+  Dtool_MappingWrapper_Items_repr,
+  0, // tp_as_number
+  &Dtool_MappingWrapper_Items_SequenceMethods,
+  0, // tp_as_mapping
+  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
+  PySeqIter_New,
+  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
+};
+
+/**
+ * This is returned by mapping.keys().
+ */
+static PyObject *Dtool_MappingWrapper_Keys_repr(PyObject *self) {
+  Dtool_WrapperBase *wrap = (Dtool_WrapperBase *)self;
+  nassertr(wrap, nullptr);
+
+  PyObject *repr = PyObject_Repr(wrap->_self);
+  PyObject *result;
+#if PY_MAJOR_VERSION >= 3
+  result = PyUnicode_FromFormat("<%s.keys() of %s>", wrap->_name, PyUnicode_AsUTF8(repr));
+#else
+  result = PyString_FromFormat("<%s.keys() of %s>", wrap->_name, PyString_AS_STRING(repr));
+#endif
+  Py_DECREF(repr);
+  return result;
+}
+
+static PySequenceMethods Dtool_MappingWrapper_Keys_SequenceMethods = {
+  Dtool_SequenceWrapper_length,
+  0, // sq_concat
+  0, // sq_repeat
+  Dtool_MappingWrapper_Items_getitem,
+  0, // sq_slice
+  0, // sq_ass_item
+  0, // sq_ass_slice
+  Dtool_MappingWrapper_contains,
+  0, // sq_inplace_concat
+  0, // sq_inplace_repeat
+};
+
+PyTypeObject Dtool_MappingWrapper_Keys_Type = {
+  PyVarObject_HEAD_INIT(nullptr, 0)
+  "sequence wrapper",
+  sizeof(Dtool_SequenceWrapper),
+  0, // tp_itemsize
+  Dtool_WrapperBase_dealloc,
+  0, // tp_print
+  0, // tp_getattr
+  0, // tp_setattr
+  0, // tp_compare
+  Dtool_MappingWrapper_Keys_repr,
+  0, // tp_as_number
+  &Dtool_SequenceWrapper_SequenceMethods,
+  0, // tp_as_mapping
+  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
+  PySeqIter_New,
+  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
+};
+
+/**
+ * This is returned by mapping.values().
+ */
+static PyObject *Dtool_MappingWrapper_Values_repr(PyObject *self) {
+  Dtool_WrapperBase *wrap = (Dtool_WrapperBase *)self;
+  nassertr(wrap, nullptr);
+
+  PyObject *repr = PyObject_Repr(wrap->_self);
+  PyObject *result;
+#if PY_MAJOR_VERSION >= 3
+  result = PyUnicode_FromFormat("<%s.values() of %s>", wrap->_name, PyUnicode_AsUTF8(repr));
+#else
+  result = PyString_FromFormat("<%s.values() of %s>", wrap->_name, PyString_AS_STRING(repr));
+#endif
+  Py_DECREF(repr);
+  return result;
+}
+
+static PyObject *Dtool_MappingWrapper_Values_getitem(PyObject *self, Py_ssize_t index) {
+  Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)self;
+  nassertr(wrap, nullptr);
+  nassertr(wrap->_keys._getitem_func, nullptr);
+
+  PyObject *key = wrap->_keys._getitem_func(wrap->_base._self, index);
+  if (key != nullptr) {
+    PyObject *value = wrap->_getitem_func(wrap->_base._self, key);
+    Py_DECREF(key);
+    return value;
+  }
+  return nullptr;
+}
+
+static PySequenceMethods Dtool_MappingWrapper_Values_SequenceMethods = {
+  Dtool_SequenceWrapper_length,
+  0, // sq_concat
+  0, // sq_repeat
+  Dtool_MappingWrapper_Values_getitem,
+  0, // sq_slice
+  0, // sq_ass_item
+  0, // sq_ass_slice
+  Dtool_MappingWrapper_contains,
+  0, // sq_inplace_concat
+  0, // sq_inplace_repeat
+};
+
+PyTypeObject Dtool_MappingWrapper_Values_Type = {
+  PyVarObject_HEAD_INIT(nullptr, 0)
+  "sequence wrapper",
+  sizeof(Dtool_MappingWrapper),
+  0, // tp_itemsize
+  Dtool_WrapperBase_dealloc,
+  0, // tp_print
+  0, // tp_getattr
+  0, // tp_setattr
+  0, // tp_compare
+  Dtool_MappingWrapper_Values_repr,
+  0, // tp_as_number
+  &Dtool_MappingWrapper_Values_SequenceMethods,
+  0, // tp_as_mapping
+  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
+  PySeqIter_New,
+  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
+};
+
+/**
+ * This variant defines only a generator interface.
+ */
+static PyObject *Dtool_GeneratorWrapper_iternext(PyObject *self) {
+  Dtool_GeneratorWrapper *wrap = (Dtool_GeneratorWrapper *)self;
+  nassertr(wrap, nullptr);
+  nassertr(wrap->_iternext_func, nullptr);
+  return wrap->_iternext_func(wrap->_base._self);
+}
+
+PyTypeObject Dtool_GeneratorWrapper_Type = {
+  PyVarObject_HEAD_INIT(nullptr, 0)
+  "generator wrapper",
+  sizeof(Dtool_GeneratorWrapper),
+  0, // tp_itemsize
+  Dtool_WrapperBase_dealloc,
+  0, // tp_print
+  0, // tp_getattr
+  0, // tp_setattr
+  0, // tp_compare
+  0, // tp_repr
+  0, // tp_as_number
+  0, // tp_as_sequence
+  0, // tp_as_mapping
+  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
+  PyObject_SelfIter,
+  Dtool_GeneratorWrapper_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
+};
+
+/**
+ * This is a variant of the Python getset mechanism that permits static
+ * properties.
+ */
+static void
+Dtool_StaticProperty_dealloc(PyDescrObject *descr) {
+  _PyObject_GC_UNTRACK(descr);
+  Py_XDECREF(descr->d_type);
+  Py_XDECREF(descr->d_name);
+//#if PY_MAJOR_VERSION >= 3
+//  Py_XDECREF(descr->d_qualname);
+//#endif
+  PyObject_GC_Del(descr);
+}
+
+static PyObject *
+Dtool_StaticProperty_repr(PyDescrObject *descr, const char *format) {
+#if PY_MAJOR_VERSION >= 3
+  return PyUnicode_FromFormat("<attribute '%s' of '%s'>",
+                              PyUnicode_AsUTF8(descr->d_name),
+                              descr->d_type->tp_name);
+#else
+  return PyString_FromFormat("<attribute '%s' of '%s'>",
+                             PyString_AS_STRING(descr->d_name),
+                             descr->d_type->tp_name);
+#endif
+}
+
+static int
+Dtool_StaticProperty_traverse(PyObject *self, visitproc visit, void *arg) {
+  PyDescrObject *descr = (PyDescrObject *)self;
+  Py_VISIT(descr->d_type);
+  return 0;
+}
+
+static PyObject *
+Dtool_StaticProperty_get(PyGetSetDescrObject *descr, PyObject *obj, PyObject *type) {
+  if (descr->d_getset->get != nullptr) {
+    return descr->d_getset->get(obj, descr->d_getset->closure);
+  } else {
+    return PyErr_Format(PyExc_AttributeError,
+                        "attribute '%s' of type '%.100s' is not readable",
+#if PY_MAJOR_VERSION >= 3
+                        PyUnicode_AsUTF8(((PyDescrObject *)descr)->d_name),
+#else
+                        PyString_AS_STRING(((PyDescrObject *)descr)->d_name),
+#endif
+                        ((PyDescrObject *)descr)->d_type->tp_name);
+  }
+}
+
+static int
+Dtool_StaticProperty_set(PyGetSetDescrObject *descr, PyObject *obj, PyObject *value) {
+  if (descr->d_getset->set != nullptr) {
+    return descr->d_getset->set(obj, value, descr->d_getset->closure);
+  } else {
+    PyErr_Format(PyExc_AttributeError,
+                 "attribute '%s' of type '%.100s' is not writable",
+#if PY_MAJOR_VERSION >= 3
+                 PyUnicode_AsUTF8(((PyDescrObject *)descr)->d_name),
+#else
+                 PyString_AS_STRING(((PyDescrObject *)descr)->d_name),
+#endif
+                 ((PyDescrObject *)descr)->d_type->tp_name);
+    return -1;
+  }
+}
+
+PyTypeObject Dtool_StaticProperty_Type = {
+  PyVarObject_HEAD_INIT(&PyType_Type, 0)
+  "getset_descriptor",
+  sizeof(PyGetSetDescrObject),
+  0, // tp_itemsize
+  (destructor)Dtool_StaticProperty_dealloc,
+  0, // tp_print
+  0, // tp_getattr
+  0, // tp_setattr
+  0, // tp_reserved
+  (reprfunc)Dtool_StaticProperty_repr,
+  0, // tp_as_number
+  0, // tp_as_sequence
+  0, // tp_as_mapping
+  0, // tp_hash
+  0, // tp_call
+  0, // tp_str
+  PyObject_GenericGetAttr,
+  0, // tp_setattro
+  0, // tp_as_buffer
+  Py_TPFLAGS_DEFAULT,
+  0, // tp_doc
+  Dtool_StaticProperty_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
+  (descrgetfunc)Dtool_StaticProperty_get,
+  (descrsetfunc)Dtool_StaticProperty_set,
+  0, // tp_dictoffset
+  0, // tp_init
+  0, // tp_alloc
+  0, // tp_new
+  0, // tp_del
+  0, // tp_is_gc
+  0, // tp_bases
+  0, // tp_mro
+  0, // tp_cache
+  0, // tp_subclasses
+  0, // tp_weaklist
+  0, // tp_del
+};
+
+/**
+ * This wraps around a property that exposes a sequence interface.
+ */
+Dtool_SequenceWrapper *Dtool_NewSequenceWrapper(PyObject *self, const char *name) {
+  Dtool_SequenceWrapper *wrap = (Dtool_SequenceWrapper *)PyObject_MALLOC(sizeof(Dtool_SequenceWrapper));
+  if (wrap == nullptr) {
+    return (Dtool_SequenceWrapper *)PyErr_NoMemory();
+  }
+
+  // If the collections.abc module is loaded, register this as a subclass.
+  static bool registered = false;
+  if (!registered) {
+    registered = true;
+    _register_collection((PyTypeObject *)&Dtool_MutableSequenceWrapper_Type, "Sequence");
+  }
+
+  PyObject_INIT(wrap, &Dtool_SequenceWrapper_Type);
+  Py_XINCREF(self);
+  wrap->_base._self = self;
+  wrap->_base._name = name;
+  wrap->_len_func = nullptr;
+  wrap->_getitem_func = nullptr;
+  return wrap;
+}
+
+/**
+ * This wraps around a property that exposes a mutable sequence interface.
+ */
+Dtool_MutableSequenceWrapper *Dtool_NewMutableSequenceWrapper(PyObject *self, const char *name) {
+  Dtool_MutableSequenceWrapper *wrap = (Dtool_MutableSequenceWrapper *)PyObject_MALLOC(sizeof(Dtool_MutableSequenceWrapper));
+  if (wrap == nullptr) {
+    return (Dtool_MutableSequenceWrapper *)PyErr_NoMemory();
+  }
+
+  // If the collections.abc module is loaded, register this as a subclass.
+  static bool registered = false;
+  if (!registered) {
+    registered = true;
+    _register_collection((PyTypeObject *)&Dtool_MutableSequenceWrapper_Type, "MutableSequence");
+  }
+
+  PyObject_INIT(wrap, &Dtool_MutableSequenceWrapper_Type);
+  Py_XINCREF(self);
+  wrap->_base._self = self;
+  wrap->_base._name = name;
+  wrap->_len_func = nullptr;
+  wrap->_getitem_func = nullptr;
+  wrap->_setitem_func = nullptr;
+  wrap->_insert_func = nullptr;
+  return wrap;
+}
+
+/**
+ * This wraps around a mapping interface, with getitem function.
+ */
+Dtool_MappingWrapper *Dtool_NewMappingWrapper(PyObject *self, const char *name) {
+  Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)PyObject_MALLOC(sizeof(Dtool_MappingWrapper));
+  if (wrap == nullptr) {
+    return (Dtool_MappingWrapper *)PyErr_NoMemory();
+  }
+
+  // If the collections.abc module is loaded, register this as a subclass.
+  static bool registered = false;
+  if (!registered) {
+    registered = true;
+    _register_collection((PyTypeObject *)&Dtool_MappingWrapper_Type, "Mapping");
+  }
+
+  PyObject_INIT(wrap, &Dtool_MappingWrapper_Type);
+  Py_XINCREF(self);
+  wrap->_base._self = self;
+  wrap->_base._name = name;
+  wrap->_keys._len_func = nullptr;
+  wrap->_keys._getitem_func = nullptr;
+  wrap->_getitem_func = nullptr;
+  wrap->_setitem_func = nullptr;
+  return wrap;
+}
+
+/**
+ * This wraps around a mapping interface, with getitem/setitem functions.
+ */
+Dtool_MappingWrapper *Dtool_NewMutableMappingWrapper(PyObject *self, const char *name) {
+  Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)PyObject_MALLOC(sizeof(Dtool_MappingWrapper));
+  if (wrap == nullptr) {
+    return (Dtool_MappingWrapper *)PyErr_NoMemory();
+  }
+
+  // If the collections.abc module is loaded, register this as a subclass.
+  static bool registered = false;
+  if (!registered) {
+    registered = true;
+    _register_collection((PyTypeObject *)&Dtool_MutableMappingWrapper_Type, "MutableMapping");
+  }
+
+  PyObject_INIT(wrap, &Dtool_MutableMappingWrapper_Type);
+  Py_XINCREF(self);
+  wrap->_base._self = self;
+  wrap->_base._name = name;
+  wrap->_keys._len_func = nullptr;
+  wrap->_keys._getitem_func = nullptr;
+  wrap->_getitem_func = nullptr;
+  wrap->_setitem_func = nullptr;
+  return wrap;
+}
+
+/**
+ * This is a variant of the Python getset mechanism that permits static
+ * properties.
+ */
+PyObject *
+Dtool_NewStaticProperty(PyTypeObject *type, const PyGetSetDef *getset) {
+  PyGetSetDescrObject *descr;
+  descr = (PyGetSetDescrObject *)PyType_GenericAlloc(&Dtool_StaticProperty_Type, 0);
+  if (descr != nullptr) {
+    Py_XINCREF(type);
+    descr->d_getset = (PyGetSetDef *)getset;
+#if PY_MAJOR_VERSION >= 3
+    descr->d_common.d_type = type;
+    descr->d_common.d_name = PyUnicode_InternFromString(getset->name);
+#if PY_VERSION_HEX >= 0x03030000
+    descr->d_common.d_qualname = nullptr;
+#endif
+#else
+    descr->d_type = type;
+    descr->d_name = PyString_InternFromString(getset->name);
+#endif
+  }
+  return (PyObject *)descr;
+}
+
+#endif  // HAVE_PYTHON

+ 78 - 0
dtool/src/interrogatedb/py_wrappers.h

@@ -0,0 +1,78 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file py_wrappers.h
+ * @author rdb
+ * @date 2017-11-26
+ */
+
+#ifndef PY_WRAPPERS_H
+#define PY_WRAPPERS_H
+
+#include "py_panda.h"
+
+#ifdef HAVE_PYTHON
+
+/**
+ * These classes are returned from properties that require a subscript
+ * interface, ie. something.children[i] = 3.
+ */
+struct Dtool_WrapperBase {
+  PyObject_HEAD;
+  PyObject *_self;
+  const char *_name;
+};
+
+struct Dtool_SequenceWrapper {
+  Dtool_WrapperBase _base;
+  lenfunc _len_func;
+  ssizeargfunc _getitem_func;
+};
+
+struct Dtool_MutableSequenceWrapper {
+  Dtool_WrapperBase _base;
+  lenfunc _len_func;
+  ssizeargfunc _getitem_func;
+  ssizeobjargproc _setitem_func;
+  PyObject *(*_insert_func)(PyObject *, size_t, PyObject *);
+};
+
+struct Dtool_MappingWrapper {
+  union {
+    Dtool_WrapperBase _base;
+    Dtool_SequenceWrapper _keys;
+  };
+  binaryfunc _getitem_func;
+  objobjargproc _setitem_func;
+};
+
+struct Dtool_GeneratorWrapper {
+  Dtool_WrapperBase _base;
+  iternextfunc _iternext_func;
+};
+
+EXPCL_INTERROGATEDB extern PyTypeObject Dtool_SequenceWrapper_Type;
+EXPCL_INTERROGATEDB extern PyTypeObject Dtool_MutableSequenceWrapper_Type;
+EXPCL_INTERROGATEDB extern PyTypeObject Dtool_MappingWrapper_Type;
+EXPCL_INTERROGATEDB extern PyTypeObject Dtool_MutableMappingWrapper_Type;
+EXPCL_INTERROGATEDB extern PyTypeObject Dtool_MappingWrapper_Items_Type;
+EXPCL_INTERROGATEDB extern PyTypeObject Dtool_MappingWrapper_Keys_Type;
+EXPCL_INTERROGATEDB extern PyTypeObject Dtool_MappingWrapper_Values_Type;
+EXPCL_INTERROGATEDB extern PyTypeObject Dtool_GeneratorWrapper_Type;
+EXPCL_INTERROGATEDB extern PyTypeObject Dtool_StaticProperty_Type;
+
+EXPCL_INTERROGATEDB Dtool_SequenceWrapper *Dtool_NewSequenceWrapper(PyObject *self, const char *name);
+EXPCL_INTERROGATEDB Dtool_MutableSequenceWrapper *Dtool_NewMutableSequenceWrapper(PyObject *self, const char *name);
+EXPCL_INTERROGATEDB Dtool_MappingWrapper *Dtool_NewMappingWrapper(PyObject *self, const char *name);
+EXPCL_INTERROGATEDB Dtool_MappingWrapper *Dtool_NewMutableMappingWrapper(PyObject *self, const char *name);
+EXPCL_INTERROGATEDB PyObject *Dtool_NewGenerator(PyObject *self, const char *name, iternextfunc func);
+EXPCL_INTERROGATEDB PyObject *Dtool_NewStaticProperty(PyTypeObject *obj, const PyGetSetDef *getset);
+
+#endif  // HAVE_PYTHON
+
+#endif  // PY_WRAPPERS_H

+ 9 - 0
dtool/src/parser-inc/iostream

@@ -88,6 +88,9 @@ __published:
   streampos tellp();
   streampos tellp();
   void seekp(streampos pos);
   void seekp(streampos pos);
   void seekp(streamoff off, ios_base::seekdir dir);
   void seekp(streamoff off, ios_base::seekdir dir);
+
+protected:
+  ostream(ostream &&);
 };
 };
 class istream : virtual public ios {
 class istream : virtual public ios {
 __published:
 __published:
@@ -97,12 +100,18 @@ __published:
   streampos tellg();
   streampos tellg();
   void seekg(streampos pos);
   void seekg(streampos pos);
   void seekg(streamoff off, ios_base::seekdir dir);
   void seekg(streamoff off, ios_base::seekdir dir);
+
+protected:
+  istream(istream &&);
 };
 };
 class iostream : public istream, public ostream {
 class iostream : public istream, public ostream {
 __published:
 __published:
   iostream(const iostream&) = delete;
   iostream(const iostream&) = delete;
 
 
   void flush();
   void flush();
+
+protected:
+  iostream(iostream &&);
 };
 };
 
 
 class ofstream : public ostream {
 class ofstream : public ostream {

+ 1 - 1
dtool/src/prc/configVariable.h

@@ -35,7 +35,7 @@ protected:
                         const string &description, int flags);
                         const string &description, int flags);
 
 
 PUBLISHED:
 PUBLISHED:
-  INLINE ConfigVariable(const string &name);
+  INLINE explicit ConfigVariable(const string &name);
   INLINE ~ConfigVariable();
   INLINE ~ConfigVariable();
 
 
   INLINE const string &get_string_value() const;
   INLINE const string &get_string_value() const;

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