diff libinterp/octave-value/cdef-class.cc @ 26769:2f847e3e8d6b

split classdef into multiple smaller source files * cdef-class.cc, cdef-class.h, cdef-manager.cc, cdef-manager.h, cdef-object.cc, cdef-object.h, cdef-utils.cc, cdef-utils.h: New files with contents split from ov-classdef.h and ov-classdef.cc. * libinterp/octave-value/module.mk: Update. * ov-classdef.h and ov-classdef.cc (ocave_classdef_superclass_ref, octave_classdef_meta): Move class declarations to header file. * interpreter-private.cc, interpreter.h, mex.cc, pt-eval.cc, Array-tc.cc: Adjust include file lists. * oop.txi: Add @DOCSTRING tag for properties.
author John W. Eaton <jwe@octave.org>
date Fri, 22 Feb 2019 07:34:47 +0000
parents
children d1419ac09564
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libinterp/octave-value/cdef-class.cc	Fri Feb 22 07:34:47 2019 +0000
@@ -0,0 +1,1732 @@
+/*
+
+Copyright (C) 2012-2019 Michael Goffioul
+
+This file is part of Octave.
+
+Octave is free software: you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Octave is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Octave; see the file COPYING.  If not, see
+<https://www.gnu.org/licenses/>.
+
+*/
+
+#if defined (HAVE_CONFIG_H)
+#  include "config.h"
+#endif
+
+#include <algorithm>
+#include <iomanip>
+
+#include "cdef-class.h"
+#include "cdef-manager.h"
+#include "cdef-utils.h"
+#include "errwarn.h"
+#include "interpreter-private.h"
+#include "interpreter.h"
+#include "load-path.h"
+#include "ov-builtin.h"
+#include "ov-classdef.h"
+#include "ov-fcn-handle.h"
+#include "ov-usr-fcn.h"
+#include "parse.h"
+#include "pt-assign.h"
+#include "pt-classdef.h"
+#include "pt-eval.h"
+#include "pt-idx.h"
+#include "pt-misc.h"
+#include "pt-stmt.h"
+#include "pt-walk.h"
+
+// Define to 1 to enable debugging statements.
+#define DEBUG_TRACE 0
+
+OCTAVE_NORETURN static
+void
+err_method_access (const std::string& from, const cdef_method& meth)
+{
+  octave_value acc = meth.get ("Access");
+  std::string acc_s;
+
+  if (acc.is_string ())
+    acc_s = acc.string_value ();
+  else
+    acc_s = "class-restricted";
+
+  error ("%s: method `%s' has %s access and cannot be run in this context",
+         from.c_str (), meth.get_name ().c_str (), acc_s.c_str ());
+}
+
+OCTAVE_NORETURN static
+void
+err_property_access (const std::string& from, const cdef_property& prop,
+                     bool is_set = false)
+{
+  octave_value acc = (prop.get (is_set ? "SetAccess" : "GetAccess"));
+  std::string acc_s;
+
+  if (acc.is_string ())
+    acc_s = acc.string_value ();
+  else
+    acc_s = "class-restricted";
+
+  if (is_set)
+    error ("%s: property `%s' has %s access and cannot be set in this context",
+           from.c_str (), prop.get_name ().c_str (), acc_s.c_str ());
+  else
+    error ("%s: property `%s' has %s access and cannot be obtained in this context",
+           from.c_str (), prop.get_name ().c_str (), acc_s.c_str ());
+}
+
+static octave_value
+make_fcn_handle (const octave_value& fcn, const std::string& nm)
+{
+  octave_value retval;
+
+  if (fcn.is_defined ())
+    retval = octave_value (new octave_fcn_handle (fcn, nm));
+
+  return retval;
+}
+
+static bool
+check_access (const cdef_class& cls, const octave_value& acc,
+              const std::string& meth_name = "",
+              const std::string& prop_name = "",
+              bool is_prop_set = false)
+{
+  if (acc.is_string ())
+    {
+      std::string acc_s = acc.string_value ();
+
+      if (acc_s == "public")
+        return true;
+
+      cdef_class ctx = get_class_context ();
+
+      // The access is private or protected, this requires a
+      // valid class context.
+
+      if (ctx.ok ())
+        {
+          if (acc_s == "private")
+            return (ctx == cls);
+          else if (acc_s == "protected")
+            {
+              if (is_superclass (cls, ctx))
+                // Calling a protected method in a superclass.
+                return true;
+              else if (is_strict_superclass (ctx, cls))
+                {
+                  // Calling a protected method or property in a derived class.
+                  // This is only allowed if the context class knows about it
+                  // and has access to it.
+
+                  if (! meth_name.empty ())
+                    {
+                      cdef_method m = ctx.find_method (meth_name);
+
+                      if (m.ok ())
+                        return check_access (ctx, m.get ("Access"), meth_name);
+
+                      return false;
+                    }
+                  else if (! prop_name.empty ())
+                    {
+                      cdef_property p = ctx.find_property (prop_name);
+
+                      if (p.ok ())
+                        {
+                          octave_value p_access = p.get (is_prop_set ?
+                                                         "SetAccess" :
+                                                         "GetAccess");
+
+                          return check_access (ctx, p_access, meth_name,
+                                               prop_name, is_prop_set);
+                        }
+
+                      return false;
+                    }
+                  else
+                    panic_impossible ();
+                }
+
+              return false;
+            }
+          else
+            panic_impossible ();
+        }
+    }
+  else if (acc.isobject ())
+    {
+      cdef_class ctx = get_class_context ();
+
+      // At this point, a class context is always required.
+      if (ctx.ok ())
+        {
+          if (ctx == cls)
+            return true;
+
+          cdef_class acc_cls (to_cdef (acc));
+
+          if (is_superclass (acc_cls, ctx))
+            return true;
+        }
+    }
+  else if (acc.iscell ())
+    {
+      Cell acc_c = acc.cell_value ();
+
+      cdef_class ctx = get_class_context ();
+
+      // At this point, a class context is always required.
+
+      if (ctx.ok ())
+        {
+          if (ctx == cls)
+            return true;
+
+          for (int i = 0; i < acc.numel (); i++)
+            {
+              cdef_class acc_cls (to_cdef (acc_c(i)));
+
+              if (is_superclass (acc_cls, ctx))
+                return true;
+            }
+        }
+    }
+  else
+    error ("invalid property/method access in class `%s'",
+           cls.get_name ().c_str ());
+
+  return false;
+}
+
+static bool
+is_method_executing (const octave_value& ov, const cdef_object& obj)
+{
+  octave::tree_evaluator& tw
+    = octave::__get_evaluator__ ("is_method_executing");
+
+  octave::call_stack& cs = octave::__get_call_stack__ ("is_method_executing");
+
+  octave_function *stack_fcn = cs.current ();
+
+  octave_function *method_fcn = ov.function_value (true);
+
+  // Does the top of the call stack match our target function?
+
+  if (stack_fcn && stack_fcn == method_fcn)
+    {
+      octave_user_function *uf = method_fcn->user_function_value (true);
+
+      // We can only check the context object for user-function (not builtin),
+      // where we have access to the parameters (arguments and return values).
+      // That's ok as there's no need to call this function for builtin
+      // methods.
+
+      if (uf)
+        {
+          // At this point, the method is executing, but we still need to
+          // check the context object for which the method is executing.  For
+          // methods, it's the first argument of the function; for ctors, it
+          // is the first return value.
+
+          octave::tree_parameter_list *pl = uf->is_classdef_constructor ()
+            ? uf->return_list () : uf->parameter_list ();
+
+          if (pl && pl->size () > 0)
+            {
+              octave::tree_decl_elt *elt = pl->front ();
+
+              octave_value arg0 = tw.evaluate (elt);
+
+              if (arg0.is_defined () && arg0.type_name () == "object")
+                {
+                  cdef_object arg0_obj = to_cdef (arg0);
+
+                  return obj.is (arg0_obj);
+                }
+            }
+        }
+    }
+
+  return false;
+}
+
+cdef_class::cdef_class_rep::cdef_class_rep (const std::list<cdef_class>& superclasses)
+  : cdef_meta_object_rep (), member_count (0), handle_class (false),
+    object_count (0), meta (false)
+{
+  put ("SuperClasses", to_ov (superclasses));
+  implicit_ctor_list = superclasses;
+}
+
+cdef_method
+cdef_class::cdef_class_rep::find_method (const std::string& nm, bool local)
+{
+  auto it = method_map.find (nm);
+
+  if (it == method_map.end ())
+    {
+      // FIXME: look into class directory
+    }
+  else
+    {
+      cdef_method& meth = it->second;
+
+      // FIXME: check if method reload needed
+
+      if (meth.ok ())
+        return meth;
+    }
+
+  if (! local)
+    {
+      // Look into superclasses
+
+      Cell super_classes = get ("SuperClasses").cell_value ();
+
+      for (int i = 0; i < super_classes.numel (); i++)
+        {
+          cdef_class cls = lookup_class (super_classes(i));
+
+          cdef_method meth = cls.find_method (nm);
+
+          if (meth.ok ())
+            return meth;
+        }
+    }
+
+  return cdef_method ();
+}
+
+class ctor_analyzer : public octave::tree_walker
+{
+public:
+  ctor_analyzer (void) = delete;
+
+  ctor_analyzer (const std::string& ctor, const std::string& obj)
+    : octave::tree_walker (), who (ctor), obj_name (obj) { }
+
+  ctor_analyzer (const ctor_analyzer&) = delete;
+
+  ctor_analyzer& operator = (const ctor_analyzer&) = delete;
+
+  ~ctor_analyzer (void) = default;
+
+  void visit_statement_list (octave::tree_statement_list& t)
+  {
+    for (const auto& stmt_p : t)
+      stmt_p->accept (*this);
+  }
+
+  void visit_statement (octave::tree_statement& t)
+  {
+    if (t.is_expression ())
+      t.expression ()->accept (*this);
+  }
+
+  void visit_simple_assignment (octave::tree_simple_assignment& t)
+  {
+    t.right_hand_side ()->accept (*this);
+  }
+
+  void visit_multi_assignment (octave::tree_multi_assignment& t)
+  {
+    t.right_hand_side ()->accept (*this);
+  }
+
+  void visit_index_expression (octave::tree_index_expression& t)
+  {
+    t.expression ()->accept (*this);
+  }
+
+  std::list<cdef_class> get_constructor_list (void) const
+  { return ctor_list; }
+
+  // NO-OP
+  void visit_anon_fcn_handle (octave::tree_anon_fcn_handle&) { }
+  void visit_argument_list (octave::tree_argument_list&) { }
+  void visit_binary_expression (octave::tree_binary_expression&) { }
+  void visit_break_command (octave::tree_break_command&) { }
+  void visit_colon_expression (octave::tree_colon_expression&) { }
+  void visit_continue_command (octave::tree_continue_command&) { }
+  void visit_decl_command (octave::tree_decl_command&) { }
+  void visit_decl_init_list (octave::tree_decl_init_list&) { }
+  void visit_decl_elt (octave::tree_decl_elt&) { }
+  void visit_simple_for_command (octave::tree_simple_for_command&) { }
+  void visit_complex_for_command (octave::tree_complex_for_command&) { }
+  void visit_octave_user_script (octave_user_script&) { }
+  void visit_octave_user_function (octave_user_function&) { }
+  void visit_function_def (octave::tree_function_def&) { }
+  void visit_identifier (octave::tree_identifier&) { }
+  void visit_if_clause (octave::tree_if_clause&) { }
+  void visit_if_command (octave::tree_if_command&) { }
+  void visit_if_command_list (octave::tree_if_command_list&) { }
+  void visit_switch_case (octave::tree_switch_case&) { }
+  void visit_switch_case_list (octave::tree_switch_case_list&) { }
+  void visit_switch_command (octave::tree_switch_command&) { }
+  void visit_matrix (octave::tree_matrix&) { }
+  void visit_cell (octave::tree_cell&) { }
+  void visit_no_op_command (octave::tree_no_op_command&) { }
+  void visit_constant (octave::tree_constant&) { }
+  void visit_fcn_handle (octave::tree_fcn_handle&) { }
+  void visit_parameter_list (octave::tree_parameter_list&) { }
+  void visit_postfix_expression (octave::tree_postfix_expression&) { }
+  void visit_prefix_expression (octave::tree_prefix_expression&) { }
+  void visit_return_command (octave::tree_return_command&) { }
+  void visit_return_list (octave::tree_return_list&) { }
+  void visit_try_catch_command (octave::tree_try_catch_command&) { }
+  void visit_unwind_protect_command (octave::tree_unwind_protect_command&) { }
+  void visit_while_command (octave::tree_while_command&) { }
+  void visit_do_until_command (octave::tree_do_until_command&) { }
+
+  void visit_superclass_ref (octave::tree_superclass_ref& t)
+  {
+    if (t.method_name () == obj_name)
+      {
+        std::string class_name = t.class_name ();
+
+        cdef_class cls = lookup_class (class_name, false);
+
+        if (cls.ok ())
+          ctor_list.push_back (cls);
+      }
+  }
+
+private:
+  // The name of the constructor being analyzed.
+  std::string who;
+
+  // The name of the first output argument of the constructor.
+  std::string obj_name;
+
+  // The list of superclass constructors that are explicitly called.
+  std::list<cdef_class> ctor_list;
+};
+
+void
+cdef_class::cdef_class_rep::install_method (const cdef_method& meth)
+{
+  method_map[meth.get_name ()] = meth;
+
+  member_count++;
+
+  if (meth.is_constructor ())
+    {
+      // Analyze the constructor code to determine what superclass
+      // constructors are called explicitly.
+
+      octave_function *of = meth.get_function ().function_value (true);
+
+      if (of)
+        {
+          octave_user_function *uf = of->user_function_value (true);
+
+          if (uf)
+            {
+              octave::tree_parameter_list *ret_list = uf->return_list ();
+              octave::tree_statement_list *body = uf->body ();
+
+              if (! ret_list || ret_list->size () != 1)
+                error ("%s: invalid constructor output arguments",
+                       meth.get_name ().c_str ());
+
+              std::string obj_name = ret_list->front ()->name ();
+              ctor_analyzer a (meth.get_name (), obj_name);
+
+              body->accept (a);
+
+              std::list<cdef_class> explicit_ctor_list
+                = a.get_constructor_list ();
+
+              for (const auto& cdef_cls : explicit_ctor_list)
+                {
+#if DEBUG_TRACE
+                  std::cerr << "explicit superclass constructor: "
+                            << cdef_cls.get_name () << std::endl;
+#endif
+
+                  implicit_ctor_list.remove (cdef_cls);
+                }
+            }
+        }
+    }
+}
+
+void
+cdef_class::cdef_class_rep::load_all_methods (void)
+{
+  // FIXME: re-scan class directory
+}
+
+Cell
+cdef_class::cdef_class_rep::get_methods (void)
+{
+  std::map<std::string,cdef_method> meths;
+
+  find_methods (meths, false);
+
+  Cell c (meths.size (), 1);
+
+  int idx = 0;
+
+  for (const auto& nm_mthd : meths)
+    c(idx++, 0) = to_ov (nm_mthd.second);
+
+  return c;
+}
+
+std::map<std::string, cdef_method>
+cdef_class::cdef_class_rep::get_method_map (bool only_inherited)
+{
+  std::map<std::string, cdef_method> methods;
+
+  find_methods (methods, only_inherited);
+
+  return methods;
+}
+
+void
+cdef_class::cdef_class_rep::find_methods (std::map<std::string,
+                                          cdef_method>& meths,
+                                          bool only_inherited)
+{
+  load_all_methods ();
+
+  method_const_iterator it;
+
+  for (it = method_map.begin (); it != method_map.end (); ++it)
+    {
+      if (! it->second.is_constructor ())
+        {
+          std::string nm = it->second.get_name ();
+
+          if (meths.find (nm) == meths.end ())
+            {
+              if (only_inherited)
+                {
+                  octave_value acc = it->second.get ("Access");
+
+                  if (! acc.is_string ()
+                      || acc.string_value () == "private")
+                    continue;
+                }
+
+              meths[nm] = it->second;
+            }
+        }
+    }
+
+  // Look into superclasses
+
+  Cell super_classes = get ("SuperClasses").cell_value ();
+
+  for (int i = 0; i < super_classes.numel (); i++)
+    {
+      cdef_class cls = lookup_class (super_classes(i));
+
+      cls.get_rep ()->find_methods (meths, true);
+    }
+}
+
+cdef_property
+cdef_class::cdef_class_rep::find_property (const std::string& nm)
+{
+  auto it = property_map.find (nm);
+
+  if (it != property_map.end ())
+    {
+      cdef_property& prop = it->second;
+
+      if (prop.ok ())
+        return prop;
+    }
+
+  // Look into superclasses
+
+  Cell super_classes = get ("SuperClasses").cell_value ();
+
+  for (int i = 0; i < super_classes.numel (); i++)
+    {
+      cdef_class cls = lookup_class (super_classes(i));
+
+      cdef_property prop = cls.find_property (nm);
+
+      if (prop.ok ())
+        return prop;
+    }
+
+  return cdef_property ();
+}
+
+void
+cdef_class::cdef_class_rep::install_property (const cdef_property& prop)
+{
+  property_map[prop.get_name ()] = prop;
+
+  member_count++;
+}
+
+Cell
+cdef_class::cdef_class_rep::get_properties (int mode)
+{
+  std::map<std::string,cdef_property> props;
+
+  props = get_property_map (mode);
+
+  Cell c (props.size (), 1);
+
+  int idx = 0;
+
+  for (const auto& pname_prop : props)
+    c(idx++, 0) = to_ov (pname_prop.second);
+
+  return c;
+}
+
+std::map<std::string, cdef_property>
+cdef_class::cdef_class_rep::get_property_map (int mode)
+{
+  std::map<std::string,cdef_property> props;
+
+  find_properties (props, mode);
+
+  return props;
+}
+
+void
+cdef_class::cdef_class_rep::find_properties (std::map<std::string,
+                                             cdef_property>& props,
+                                             int mode)
+{
+  property_const_iterator it;
+
+  for (it = property_map.begin (); it != property_map.end (); ++it)
+    {
+      std::string nm = it->second.get_name ();
+
+      if (props.find (nm) == props.end ())
+        {
+          if (mode == property_inherited)
+            {
+              octave_value acc = it->second.get ("GetAccess");
+
+              if (! acc.is_string ()
+                  || acc.string_value () == "private")
+                continue;
+            }
+
+          props[nm] = it->second;
+        }
+    }
+
+  // Look into superclasses
+
+  Cell super_classes = get ("SuperClasses").cell_value ();
+
+  for (int i = 0; i < super_classes.numel (); i++)
+    {
+      cdef_class cls = lookup_class (super_classes(i));
+
+      cls.get_rep ()->find_properties (props,
+                                       (mode == property_all
+                                        ? property_all
+                                        : property_inherited));
+    }
+}
+
+void
+cdef_class::cdef_class_rep::find_names (std::set<std::string>& names,
+                                        bool all)
+{
+  load_all_methods ();
+
+  for (const auto& cls_fnmap : method_map)
+    {
+      if (! cls_fnmap.second.is_constructor ())
+        {
+          std::string nm = cls_fnmap.second.get_name ();
+
+          if (! all)
+            {
+              octave_value acc = cls_fnmap.second.get ("Access");
+
+              if (! acc.is_string()
+                  || acc.string_value () != "public")
+                continue;
+            }
+
+          names.insert (nm);
+        }
+    }
+
+  for (const auto& pname_prop : property_map)
+    {
+      std::string nm = pname_prop.second.get_name ();
+
+      if (! all)
+        {
+          octave_value acc = pname_prop.second.get ("GetAccess");
+
+          if (! acc.is_string()
+              || acc.string_value () != "public")
+            continue;
+        }
+
+      names.insert (nm);
+    }
+
+  // Look into superclasses
+
+  Cell super_classes = get ("SuperClasses").cell_value ();
+
+  for (int i = 0; i < super_classes.numel (); i++)
+    {
+      cdef_class cls = lookup_class (super_classes(i));
+
+      cls.get_rep ()->find_names (names, all);
+    }
+}
+
+string_vector
+cdef_class::cdef_class_rep::get_names (void)
+{
+  std::set<std::string> names;
+
+  find_names (names, false);
+
+  string_vector v (names);
+
+  return v.sort (true);
+}
+
+void
+cdef_class::cdef_class_rep::delete_object (const cdef_object& obj)
+{
+  cdef_method dtor = find_method ("delete");
+
+  if (dtor.ok ())
+    dtor.execute (obj, octave_value_list (), 0, true, "destructor");
+
+  // FIXME: should we destroy corresponding properties here?
+
+  // Call "delete" in super classes
+
+  Cell super_classes = get ("SuperClasses").cell_value ();
+
+  for (int i = 0; i < super_classes.numel (); i++)
+    {
+      cdef_class cls = lookup_class (super_classes(i));
+
+      if (cls.get_name () != "handle")
+        cls.delete_object (obj);
+    }
+}
+
+octave_value_list
+cdef_class::cdef_class_rep::meta_subsref (const std::string& type,
+                                          const std::list<octave_value_list>& idx,
+                                          int nargout)
+{
+  size_t skip = 1;
+
+  octave_value_list retval;
+
+  switch (type[0])
+    {
+    case '(':
+      // Constructor call
+
+#if DEBUG_TRACE
+      std::cerr << "constructor" << std::endl;
+#endif
+
+      retval(0) = construct (idx.front ());
+      break;
+
+    case '.':
+      {
+        // Static method, constant (or property?)
+
+#if DEBUG_TRACE
+        std::cerr << "static method/property" << std::endl;
+#endif
+
+        if (idx.front ().length () != 1)
+          error ("invalid meta.class indexing");
+
+        std::string nm = idx.front ()(0).xstring_value ("invalid meta.class indexing, expected a method or property name");
+
+        cdef_method meth = find_method (nm);
+
+        if (meth.ok ())
+          {
+            if (! meth.is_static ())
+              error ("method `%s' is not static", nm.c_str ());
+
+            octave_value_list args;
+
+            if (type.length () > 1 && idx.size () > 1 && type[1] == '(')
+              {
+                args = *(++(idx.begin ()));
+                skip++;
+              }
+
+            retval = meth.execute (args, (type.length () > skip
+                                          ? 1 : nargout), true,
+                                   "meta.class");
+          }
+        else
+          {
+            cdef_property prop = find_property (nm);
+
+            if (! prop.ok ())
+              error ("no such method or property `%s'", nm.c_str ());
+
+            if (! prop.is_constant ())
+              error ("property `%s' is not constant", nm.c_str ());
+
+            retval(0) = prop.get_value (true, "meta.class");
+          }
+      }
+      break;
+
+    default:
+      error ("invalid meta.class indexing");
+      break;
+    }
+
+  if (type.length () > skip && idx.size () > skip && ! retval.empty ())
+    retval = retval(0).next_subsref (nargout, type, idx, skip);
+
+  return retval;
+}
+
+void
+cdef_class::cdef_class_rep::meta_release (void)
+{
+  cdef_manager& cdm
+    = octave::__get_cdef_manager__ ("cdef_class::cdef_class_rep::meta_release");
+
+  cdm.unregister_class (wrap ());
+}
+
+void
+cdef_class::cdef_class_rep::initialize_object (cdef_object& obj)
+{
+  // Populate the object with default property values
+
+  std::list<cdef_class> super_classes = lookup_classes (
+                                          get ("SuperClasses").cell_value ());
+
+  for (auto& cls : super_classes)
+    cls.initialize_object (obj);
+
+  for (const auto& pname_prop : property_map)
+    {
+      if (! pname_prop.second.get ("Dependent").bool_value ())
+        {
+          octave_value pvalue = pname_prop.second.get ("DefaultValue");
+
+          if (pvalue.is_defined ())
+            obj.put (pname_prop.first, pvalue);
+          else
+            obj.put (pname_prop.first, octave_value (Matrix ()));
+        }
+    }
+
+  refcount++;
+  obj.mark_for_construction (cdef_class (this));
+}
+
+void
+cdef_class::cdef_class_rep::run_constructor (cdef_object& obj,
+                                             const octave_value_list& args)
+{
+  octave_value_list empty_args;
+
+  for (const auto& cls : implicit_ctor_list)
+    {
+      cdef_class supcls = lookup_class (cls);
+
+      supcls.run_constructor (obj, empty_args);
+    }
+
+  std::string cls_name = get_name ();
+  std::string ctor_name = get_base_name (cls_name);
+
+  cdef_method ctor = find_method (ctor_name);
+
+  if (ctor.ok ())
+    {
+      octave_value_list ctor_args (args);
+      octave_value_list ctor_retval;
+
+      ctor_args.prepend (to_ov (obj));
+      ctor_retval = ctor.execute (ctor_args, 1, true, "constructor");
+
+      if (ctor_retval.length () != 1)
+        error ("%s: invalid number of output arguments for classdef constructor",
+               ctor_name.c_str ());
+
+      obj = to_cdef (ctor_retval(0));
+    }
+
+  obj.mark_as_constructed (wrap ());
+}
+
+octave_value
+cdef_class::cdef_class_rep::construct (const octave_value_list& args)
+{
+  cdef_object obj = construct_object (args);
+
+  if (obj.ok ())
+    return to_ov (obj);
+
+  return octave_value ();
+}
+
+cdef_object
+cdef_class::cdef_class_rep::construct_object (const octave_value_list& args)
+{
+  if (is_abstract ())
+    error ("cannot instantiate object for abstract class `%s'",
+           get_name ().c_str ());
+
+  cdef_object obj;
+
+  if (is_meta_class ())
+    {
+      // This code path is only used to create empty meta objects
+      // as filler for the empty values within a meta object array.
+
+      cdef_class this_cls = wrap ();
+
+      static cdef_object empty_class;
+
+      cdef_manager& cdm
+        = octave::__get_cdef_manager__ ("cdef_class::cdef_class_rep::construct_object");
+
+      if (this_cls == cdm.meta_class ())
+        {
+          if (! empty_class.ok ())
+            empty_class = cdm.make_class ("", std::list<cdef_class> ());
+          obj = empty_class;
+        }
+      else if (this_cls == cdm.meta_property ())
+        {
+          static cdef_property empty_property;
+
+          if (! empty_class.ok ())
+            empty_class = cdm.make_class ("", std::list<cdef_class> ());
+          if (! empty_property.ok ())
+            empty_property = cdm.make_property (empty_class, "");
+          obj = empty_property;
+        }
+      else if (this_cls == cdm.meta_method ())
+        {
+          static cdef_method empty_method;
+
+          if (! empty_class.ok ())
+            empty_class = cdm.make_class ("", std::list<cdef_class> ());
+          if (! empty_method.ok ())
+            empty_method = cdm.make_method (empty_class, "", octave_value ());
+          obj = empty_method;
+        }
+      else if (this_cls == cdm.meta_package ())
+        {
+          static cdef_package empty_package;
+
+          if (! empty_package.ok ())
+            empty_package = cdm.make_package ("");
+          obj = empty_package;
+        }
+      else
+        panic_impossible ();
+
+      return obj;
+    }
+  else
+    {
+      if (is_handle_class ())
+        obj = cdef_object (new handle_cdef_object ());
+      else
+        obj = cdef_object (new value_cdef_object ());
+      obj.set_class (wrap ());
+
+      initialize_object (obj);
+
+      run_constructor (obj, args);
+
+      return obj;
+    }
+
+  return cdef_object ();
+}
+
+static octave_value
+compute_attribute_value (octave::tree_evaluator& tw,
+                         octave::tree_classdef_attribute *t)
+{
+  octave::tree_expression *expr = t->expression ();
+
+  if (expr)
+    {
+      if (expr->is_identifier ())
+        {
+          std::string s = expr->name ();
+
+          if (s == "public")
+            return std::string ("public");
+          else if (s == "protected")
+            return std::string ("protected");
+          else if (s == "private")
+            return std::string ("private");
+        }
+
+      return tw.evaluate (expr);
+    }
+  else
+    return octave_value (true);
+}
+
+template <typename T>
+static std::string
+attribute_value_to_string (T *t, octave_value v)
+{
+  if (v.is_string ())
+    return v.string_value ();
+  else if (t->expression ())
+    return t->expression ()->original_text ();
+  else
+    return "true";
+}
+
+cdef_class
+cdef_class::make_meta_class (octave::interpreter& interp,
+                             octave::tree_classdef *t, bool is_at_folder)
+{
+  cdef_class retval;
+  std::string class_name, full_class_name;
+
+  // Class creation
+
+  class_name = full_class_name = t->ident ()->name ();
+  if (! t->package_name ().empty ())
+    full_class_name = t->package_name () + '.' + full_class_name;
+
+#if DEBUG_TRACE
+  std::cerr << "class: " << full_class_name << std::endl;
+#endif
+
+  std::list<cdef_class> slist;
+
+  if (t->superclass_list ())
+    {
+      for (auto& scls : (*t->superclass_list ()))
+        {
+          std::string sclass_name = (scls)->class_name ();
+
+#if DEBUG_TRACE
+          std::cerr << "superclass: " << sclass_name << std::endl;
+#endif
+
+          cdef_class sclass = lookup_class (sclass_name);
+
+          if (sclass.get ("Sealed").bool_value ())
+            error ("`%s' cannot inherit from `%s', because it is sealed",
+                   full_class_name.c_str (), sclass_name.c_str ());
+
+          slist.push_back (sclass);
+        }
+    }
+
+  cdef_manager& cdm
+    = octave::__get_cdef_manager__ ("cdef_class::make_meta_class");
+
+  retval = cdm.make_class (full_class_name, slist);
+
+  // Package owning this class
+
+  if (! t->package_name ().empty ())
+    {
+      cdef_package pack = cdm.find_package (t->package_name ());
+
+      if (pack.ok ())
+        retval.put ("ContainingPackage", to_ov (pack));
+    }
+
+  // Class attributes
+
+  octave::tree_evaluator& tw = interp.get_evaluator ();
+
+  if (t->attribute_list ())
+    {
+      for (const auto& attr : (*t->attribute_list ()))
+        {
+          std::string aname = attr->ident ()->name ();
+          octave_value avalue = compute_attribute_value (tw, attr);
+
+#if DEBUG_TRACE
+          std::cerr << "class attribute: " << aname << " = "
+                    << attribute_value_to_string (attr, avalue) << std::endl;
+#endif
+
+          retval.put (aname, avalue);
+        }
+    }
+
+  octave::tree_classdef_body *b = t->body ();
+
+  if (b)
+    {
+      // Keep track of the get/set accessor methods.  They will be used
+      // later on when creating properties.
+
+      std::map<std::string, octave_value> get_methods;
+      std::map<std::string, octave_value> set_methods;
+
+      // Method blocks
+
+      std::list<octave::tree_classdef_methods_block *> mb_list = b->methods_list ();
+
+      octave::load_path& lp = interp.get_load_path ();
+
+      for (auto& mb_p : mb_list)
+        {
+          std::map<std::string, octave_value> amap;
+
+#if DEBUG_TRACE
+          std::cerr << "method block" << std::endl;
+#endif
+
+          // Method attributes
+
+          if (mb_p->attribute_list ())
+            {
+              for (auto& attr_p : *mb_p->attribute_list ())
+                {
+                  std::string aname = attr_p->ident ()->name ();
+                  octave_value avalue = compute_attribute_value (tw, attr_p);
+
+#if DEBUG_TRACE
+                  std::cerr << "method attribute: " << aname << " = "
+                            << attribute_value_to_string (attr_p, avalue)
+                            << std::endl;
+#endif
+
+                  amap[aname] = avalue;
+                }
+            }
+
+          // Methods
+
+          if (mb_p->element_list ())
+            {
+              for (auto& mtd : *mb_p->element_list ())
+                {
+                  std::string mname = mtd.function_value ()->name ();
+                  std::string mprefix = mname.substr (0, 4);
+
+                  if (mprefix == "get.")
+                    get_methods[mname.substr (4)] =
+                      make_fcn_handle (mtd, full_class_name + '>' + mname);
+                  else if (mprefix == "set.")
+                    set_methods[mname.substr (4)] =
+                      make_fcn_handle (mtd, full_class_name + '>' + mname);
+                  else
+                    {
+                      cdef_method meth = cdm.make_method (retval, mname, mtd);
+
+#if DEBUG_TRACE
+                      std::cerr << (mname == class_name ? "constructor"
+                                                        : "method")
+                                << ": " << mname << std::endl;
+#endif
+
+                      for (auto& attrnm_val : amap)
+                        meth.put (attrnm_val.first, attrnm_val.second);
+
+                      retval.install_method (meth);
+                    }
+                }
+            }
+        }
+
+      if (is_at_folder)
+        {
+          // Look for all external methods visible on octave path at the
+          // time of loading of the class.
+          //
+          // FIXME: This is an "extension" to Matlab behavior, which only looks
+          // in the @-folder containing the original classdef file.  However,
+          // this is easier to implement it that way at the moment.
+
+          std::list<std::string> external_methods
+            = lp.methods (full_class_name);
+
+          for (const auto& mtdnm : external_methods)
+            {
+              // FIXME: should we issue a warning if the method is already
+              // defined in the classdef file?
+
+              if (mtdnm != class_name
+                  && ! retval.find_method (mtdnm, true).ok ())
+                {
+                  // Create a dummy method that is used until the actual
+                  // method is loaded.
+                  octave_user_function *fcn = new octave_user_function ();
+
+                  fcn->stash_function_name (mtdnm);
+
+                  cdef_method meth
+                    = cdm.make_method (retval, mtdnm, octave_value (fcn));
+
+                  retval.install_method (meth);
+                }
+            }
+        }
+
+      // Property blocks
+
+      // FIXME: default property expression should be able to call static
+      //        methods of the class being constructed.  A restricted CLASSNAME
+      //        symbol should be added to the scope before evaluating default
+      //        value expressions.
+
+      std::list<octave::tree_classdef_properties_block *> pb_list
+        = b->properties_list ();
+
+      for (auto& pb_p : pb_list)
+        {
+          std::map<std::string, octave_value> amap;
+
+#if DEBUG_TRACE
+          std::cerr << "property block" << std::endl;
+#endif
+
+          // Property attributes
+
+          if (pb_p->attribute_list ())
+            {
+              for (auto& attr_p : *pb_p->attribute_list ())
+                {
+                  std::string aname = attr_p->ident ()->name ();
+                  octave_value avalue = compute_attribute_value (tw, attr_p);
+
+#if DEBUG_TRACE
+                  std::cerr << "property attribute: " << aname << " = "
+                            << attribute_value_to_string (attr_p, avalue)
+                            << std::endl;
+#endif
+
+                  if (aname == "Access")
+                    {
+                      amap["GetAccess"] = avalue;
+                      amap["SetAccess"] = avalue;
+                    }
+                  else
+                    amap[aname] = avalue;
+                }
+            }
+
+          // Properties
+
+          if (pb_p->element_list ())
+            {
+              for (auto& prop_p : *pb_p->element_list ())
+                {
+                  std::string prop_name = prop_p->ident ()->name ();
+
+                  cdef_property prop = cdm.make_property (retval, prop_name);
+
+#if DEBUG_TRACE
+                  std::cerr << "property: " << prop_p->ident ()->name ()
+                            << std::endl;
+#endif
+
+                  octave::tree_expression *expr = prop_p->expression ();
+                  if (expr)
+                    {
+                      octave_value pvalue = tw.evaluate (expr);
+
+#if DEBUG_TRACE
+                      std::cerr << "property default: "
+                                << attribute_value_to_string (*pit, pvalue)
+                                << std::endl;
+#endif
+
+                      prop.put ("DefaultValue", pvalue);
+                    }
+
+                  // Install property attributes.  This is done before assigning
+                  // the property accessors so we can do validation by using
+                  // cdef_property methods.
+
+                  for (auto& attrnm_val : amap)
+                    prop.put (attrnm_val.first, attrnm_val.second);
+
+                  // Install property access methods, if any.  Remove the
+                  // accessor methods from the temporary storage map, so we can
+                  // detect which ones are invalid and do not correspond to a
+                  // defined property.
+
+                  auto git = get_methods.find (prop_name);
+
+                  if (git != get_methods.end ())
+                    {
+                      make_function_of_class (retval, git->second);
+                      prop.put ("GetMethod", git->second);
+                      get_methods.erase (git);
+                    }
+
+                  auto sit = set_methods.find (prop_name);
+
+                  if (sit != set_methods.end ())
+                    {
+                      make_function_of_class (retval, sit->second);
+                      prop.put ("SetMethod", sit->second);
+                      set_methods.erase (sit);
+                    }
+
+                  retval.install_property (prop);
+                }
+            }
+        }
+    }
+
+  return retval;
+}
+
+octave_function*
+cdef_class::get_method_function (const std::string& /* nm */)
+{
+  octave_classdef_meta *p = new octave_classdef_meta (*this);
+
+  return p;
+}
+
+octave_value
+cdef_property::cdef_property_rep::get_value (const cdef_object& obj,
+                                             bool do_check_access,
+                                             const std::string& who)
+{
+  octave_value retval;
+
+  if (do_check_access && ! check_get_access ())
+    err_property_access (who, wrap (), false);
+
+  if (! obj.is_constructed ())
+    {
+      cdef_class cls (to_cdef (get ("DefiningClass")));
+
+      if (! obj.is_partially_constructed_for (cls))
+        error ("cannot reference properties of class `%s' for non-constructed object",
+               cls.get_name ().c_str ());
+    }
+
+  octave_value get_fcn = get ("GetMethod");
+
+  // FIXME: should check whether we're already in get accessor method
+
+  if (get_fcn.isempty () || is_method_executing (get_fcn, obj))
+    retval = obj.get (get ("Name").string_value ());
+  else
+    {
+      octave_value_list args;
+
+      args(0) = to_ov (obj);
+
+      args = octave::feval (get_fcn, args, 1);
+
+      retval = args(0);
+    }
+
+  return retval;
+}
+
+octave_value
+cdef_property::cdef_property_rep::get_value (bool do_check_access,
+                                             const std::string& who)
+{
+  if (do_check_access && ! check_get_access ())
+    err_property_access (who, wrap (), false);
+
+  return get ("DefaultValue");
+}
+
+bool
+cdef_property::cdef_property_rep::is_recursive_set (const cdef_object& /* obj */) const
+{
+  // FIXME: implement
+  return false;
+}
+
+void
+cdef_property::cdef_property_rep::set_value (cdef_object& obj,
+                                             const octave_value& val,
+                                             bool do_check_access,
+                                             const std::string& who)
+{
+  if (do_check_access && ! check_set_access ())
+    err_property_access (who, wrap (), true);
+
+  if (! obj.is_constructed ())
+    {
+      cdef_class cls (to_cdef (get ("DefiningClass")));
+
+      if (! obj.is_partially_constructed_for (cls))
+        error ("cannot reference properties of class `%s' for non-constructed object",
+               cls.get_name ().c_str ());
+    }
+
+  octave_value set_fcn = get ("SetMethod");
+
+  if (set_fcn.isempty () || is_method_executing (set_fcn, obj))
+    obj.put (get ("Name").string_value (), val);
+  else
+    {
+      octave_value_list args;
+
+      args(0) = to_ov (obj);
+      args(1) = val;
+
+      args = octave::feval (set_fcn, args, 1);
+
+      if (args.length () > 0 && args(0).is_defined ())
+        {
+          if (args (0).is_classdef_object ())
+            {
+              cdef_object new_obj = to_cdef (args(0));
+
+              obj = new_obj;
+            }
+          else
+            ::warning ("set-method of property `%s' returned a non-classdef object",
+                       get_name ().c_str ());
+        }
+    }
+}
+
+bool
+cdef_property::cdef_property_rep::check_get_access (void) const
+{
+  cdef_class cls (to_cdef (get ("DefiningClass")));
+
+  return ::check_access (cls, get ("GetAccess"), "",
+                         get_name (), false);
+
+  return false;
+}
+
+bool
+cdef_property::cdef_property_rep::check_set_access (void) const
+{
+  cdef_class cls (to_cdef (get ("DefiningClass")));
+
+  return ::check_access (cls, get ("SetAccess"), "",
+                         get_name (), true);
+
+  return false;
+}
+
+void
+cdef_method::cdef_method_rep::check_method (void)
+{
+  if (is_external ())
+    {
+      if (is_dummy_method (function))
+        {
+          octave::load_path& lp
+            = octave::__get_load_path__ ("cdef_method::cdef_method_rep::check_method");
+
+          std::string name = get_name ();
+          std::string cls_name = dispatch_type;
+          std::string pack_name;
+
+          size_t pos = cls_name.rfind ('.');
+
+          if (pos != std::string::npos)
+            {
+              pack_name = cls_name.substr (0, pos);
+              cls_name = cls_name.substr (pos + 1);
+            }
+
+          std::string dir_name;
+          std::string file_name = lp.find_method (cls_name, name,
+                                                  dir_name, pack_name);
+
+          if (! file_name.empty ())
+            {
+              octave_value ov_fcn
+                = octave::load_fcn_from_file (file_name, dir_name,
+                                              dispatch_type, pack_name);
+
+              if (ov_fcn.is_defined ())
+                {
+                  function = ov_fcn;
+
+                  make_function_of_class (dispatch_type, function);
+                }
+            }
+        }
+      else
+        {
+          // FIXME: check out-of-date status
+        }
+
+      if (is_dummy_method (function))
+        error ("no definition found for method `%s' of class `%s'",
+               get_name ().c_str (), dispatch_type.c_str ());
+    }
+}
+
+octave_value_list
+cdef_method::cdef_method_rep::execute (const octave_value_list& args,
+                                       int nargout, bool do_check_access,
+                                       const std::string& who)
+{
+  octave_value_list retval;
+
+  if (do_check_access && ! check_access ())
+    err_method_access (who, wrap ());
+
+  if (get ("Abstract").bool_value ())
+    error ("%s: cannot execute abstract method",
+           get ("Name").string_value ().c_str ());
+
+  check_method ();
+
+  if (function.is_defined ())
+    retval = octave::feval (function, args, nargout);
+
+  return retval;
+}
+
+octave_value_list
+cdef_method::cdef_method_rep::execute (const cdef_object& obj,
+                                       const octave_value_list& args,
+                                       int nargout, bool do_check_access,
+                                       const std::string& who)
+{
+  octave_value_list retval;
+
+  if (do_check_access && ! check_access ())
+    err_method_access (who, wrap ());
+
+  if (get ("Abstract").bool_value ())
+    error ("%s: cannot execute abstract method",
+           get ("Name").string_value ().c_str ());
+
+  check_method ();
+
+  if (function.is_defined ())
+    {
+      octave_value_list new_args;
+
+      new_args.resize (args.length () + 1);
+
+      new_args(0) = to_ov (obj);
+      for (int i = 0; i < args.length (); i++)
+        new_args(i+1) = args(i);
+
+      retval = octave::feval (function, new_args, nargout);
+    }
+
+  return retval;
+}
+
+bool
+cdef_method::cdef_method_rep::is_constructor (void) const
+{
+  if (function.is_function())
+    return function.function_value ()->is_classdef_constructor ();
+
+  return false;
+}
+
+bool
+cdef_method::cdef_method_rep::check_access (void) const
+{
+  cdef_class cls (to_cdef (get ("DefiningClass")));
+
+  return ::check_access (cls, get ("Access"), get_name ());
+}
+
+octave_value_list
+cdef_method::cdef_method_rep::meta_subsref
+  (const std::string& type, const std::list<octave_value_list>& idx,
+   int nargout)
+{
+  octave_value_list retval;
+
+  switch (type[0])
+    {
+    case '(':
+      retval = (execute (idx.front (), type.length () > 1 ? 1 : nargout, true));
+      break;
+
+    default:
+      error ("invalid meta.method indexing");
+      break;
+    }
+
+  if (type.length () > 1 && idx.size () > 1 && ! retval.empty ())
+    retval = retval(0).next_subsref (nargout, type, idx, 1);
+
+  return retval;
+}
+
+void
+cdef_package::cdef_package_rep::install_class (const cdef_class& cls,
+                                               const std::string& nm)
+{
+  class_map[nm] = cls;
+
+  member_count++;
+}
+
+void
+cdef_package::cdef_package_rep::install_function (const octave_value& fcn,
+                                                  const std::string& nm)
+{
+  function_map[nm] = fcn;
+}
+
+void
+cdef_package::cdef_package_rep::install_package (const cdef_package& pack,
+                                                 const std::string& nm)
+{
+  package_map[nm] = pack;
+
+  member_count++;
+}
+
+template <typename T1, typename T2>
+Cell
+map2Cell (const std::map<T1, T2>& m)
+{
+  Cell retval (1, m.size ());
+  int i = 0;
+
+  for (auto it = m.begin (); it != m.end (); ++it, ++i)
+    retval(i) = to_ov (it->second);
+
+  return retval;
+}
+
+Cell
+cdef_package::cdef_package_rep::get_classes (void) const
+{ return map2Cell (class_map); }
+
+Cell
+cdef_package::cdef_package_rep::get_functions (void) const
+{ return map2Cell (function_map); }
+
+Cell
+cdef_package::cdef_package_rep::get_packages (void) const
+{ return map2Cell (package_map); }
+
+octave_value
+cdef_package::cdef_package_rep::find (const std::string& nm)
+{
+  std::string symbol_name = get_name () + '.' + nm;
+
+  octave::interpreter& interp
+    = octave::__get_interpreter__ ("cdef_package::cdef_package_rep::find");
+
+  return interp.find (symbol_name);
+}
+
+octave_value_list
+cdef_package::cdef_package_rep::meta_subsref
+  (const std::string& type, const std::list<octave_value_list>& idx,
+   int nargout)
+{
+  octave_value_list retval;
+
+  switch (type[0])
+    {
+    case '.':
+      {
+        if (idx.front ().length () != 1)
+          error ("invalid meta.package indexing");
+
+        std::string nm = idx.front ()(0).xstring_value ("invalid meta.package indexing, expected a symbol name");
+
+#if DEBUG_TRACE
+        std::cerr << "meta.package query: " << nm << std::endl;
+#endif
+
+        octave_value o = find (nm);
+
+        if (! o.is_defined ())
+          error ("member `%s' in package `%s' does not exist",
+                 nm.c_str (), get_name ().c_str ());
+
+        if (o.is_function ())
+          {
+            octave_function *fcn = o.function_value ();
+
+            // NOTE: the case where the package query is the last
+            // part of this subsref index is handled in the parse
+            // tree, because there is some logic to handle magic
+            // "end" that makes it impossible to execute the
+            // function call at this stage.
+
+            if (type.size () > 1
+                && ! fcn->accepts_postfix_index (type[1]))
+              {
+                octave_value_list tmp_args;
+
+                retval = octave::feval (o, tmp_args, nargout);
+              }
+            else
+              retval(0) = o;
+
+            if (type.size () > 1 && idx.size () > 1)
+              retval = retval(0).next_subsref (nargout, type,
+                                               idx, 1);
+          }
+        else if (type.size () > 1 && idx.size () > 1)
+          retval = o.next_subsref (nargout, type, idx, 1);
+        else
+          retval(0) = o;
+      }
+      break;
+
+    default:
+      error ("invalid meta.package indexing");
+      break;
+    }
+
+  return retval;
+}
+
+void
+cdef_package::cdef_package_rep::meta_release (void)
+{
+  // FIXME: Do we really want to unregister the package, as it
+  //        could still be referenced by classes or sub-packages?
+  //        If the package object is recreated later on, it won't
+  //        match the one already referenced by those classes or
+  //        sub-packages.
+
+  cdef_manager& cdm
+    = octave::__get_cdef_manager__ ("cdef_package::cdef_package_rep::meta_release");
+
+  // Don't delete the "meta" package.
+  if (this != cdm.meta ().get_rep ())
+    cdm.unregister_package (wrap ());
+}