view libinterp/octave-value/cdef-class.cc @ 31605:e88a07dec498 stable

maint: Use macros to begin/end C++ namespaces. * oct-conf-post-public.in.h: Define two macros (OCTAVE_BEGIN_NAMESPACE, OCTAVE_END_NAMESPACE) that can be used to start/end a namespace. * mk-opts.pl, build-env.h, build-env.in.cc, __betainc__.cc, __contourc__.cc, __dsearchn__.cc, __eigs__.cc, __expint__.cc, __ftp__.cc, __gammainc__.cc, __ichol__.cc, __ilu__.cc, __isprimelarge__.cc, __lin_interpn__.cc, __magick_read__.cc, __pchip_deriv__.cc, __qp__.cc, amd.cc, auto-shlib.cc, auto-shlib.h, balance.cc, base-text-renderer.cc, base-text-renderer.h, besselj.cc, bitfcns.cc, bsxfun.cc, c-file-ptr-stream.cc, c-file-ptr-stream.h, call-stack.cc, call-stack.h, ccolamd.cc, cellfun.cc, chol.cc, colamd.cc, colloc.cc, conv2.cc, daspk.cc, dasrt.cc, dassl.cc, data.cc, data.h, debug.cc, defaults.cc, defaults.h, defun-int.h, defun.cc, det.cc, dirfns.cc, display.cc, display.h, dlmread.cc, dmperm.cc, dot.cc, dynamic-ld.cc, dynamic-ld.h, eig.cc, ellipj.cc, environment.cc, environment.h, error.cc, error.h, errwarn.h, event-manager.cc, event-manager.h, event-queue.cc, event-queue.h, fcn-info.cc, fcn-info.h, fft.cc, fft2.cc, fftn.cc, file-io.cc, filter.cc, find.cc, ft-text-renderer.cc, ft-text-renderer.h, gcd.cc, getgrent.cc, getpwent.cc, getrusage.cc, givens.cc, gl-render.cc, gl-render.h, gl2ps-print.cc, gl2ps-print.h, graphics-toolkit.cc, graphics-toolkit.h, graphics.cc, graphics.in.h, gsvd.cc, gtk-manager.cc, gtk-manager.h, hash.cc, help.cc, help.h, hess.cc, hex2num.cc, hook-fcn.cc, hook-fcn.h, input.cc, input.h, interpreter-private.cc, interpreter-private.h, interpreter.cc, interpreter.h, inv.cc, jsondecode.cc, jsonencode.cc, kron.cc, latex-text-renderer.cc, latex-text-renderer.h, load-path.cc, load-path.h, load-save.cc, load-save.h, lookup.cc, ls-ascii-helper.cc, ls-ascii-helper.h, ls-oct-text.cc, ls-utils.cc, ls-utils.h, lsode.cc, lu.cc, mappers.cc, matrix_type.cc, max.cc, mex-private.h, mex.cc, mgorth.cc, nproc.cc, oct-fstrm.cc, oct-fstrm.h, oct-hdf5-types.cc, oct-hdf5-types.h, oct-hist.cc, oct-hist.h, oct-iostrm.cc, oct-iostrm.h, oct-opengl.h, oct-prcstrm.cc, oct-prcstrm.h, oct-procbuf.cc, oct-procbuf.h, oct-process.cc, oct-process.h, oct-stdstrm.h, oct-stream.cc, oct-stream.h, oct-strstrm.cc, oct-strstrm.h, oct-tex-lexer.in.ll, oct-tex-parser.yy, ordqz.cc, ordschur.cc, pager.cc, pager.h, pinv.cc, pow2.cc, pr-flt-fmt.cc, pr-output.cc, procstream.cc, procstream.h, psi.cc, qr.cc, quad.cc, quadcc.cc, qz.cc, rand.cc, rcond.cc, regexp.cc, schur.cc, settings.cc, settings.h, sighandlers.cc, sighandlers.h, sparse-xdiv.cc, sparse-xdiv.h, sparse-xpow.cc, sparse-xpow.h, sparse.cc, spparms.cc, sqrtm.cc, stack-frame.cc, stack-frame.h, stream-euler.cc, strfind.cc, strfns.cc, sub2ind.cc, svd.cc, sylvester.cc, symbfact.cc, syminfo.cc, syminfo.h, symrcm.cc, symrec.cc, symrec.h, symscope.cc, symscope.h, symtab.cc, symtab.h, syscalls.cc, sysdep.cc, sysdep.h, text-engine.cc, text-engine.h, text-renderer.cc, text-renderer.h, time.cc, toplev.cc, tril.cc, tsearch.cc, typecast.cc, url-handle-manager.cc, url-handle-manager.h, urlwrite.cc, utils.cc, utils.h, variables.cc, variables.h, xdiv.cc, xdiv.h, xnorm.cc, xnorm.h, xpow.cc, xpow.h, __delaunayn__.cc, __fltk_uigetfile__.cc, __glpk__.cc, __init_fltk__.cc, __init_gnuplot__.cc, __ode15__.cc, __voronoi__.cc, audiodevinfo.cc, audioread.cc, convhulln.cc, fftw.cc, gzip.cc, mk-build-env-features.sh, mk-builtins.pl, cdef-class.cc, cdef-class.h, cdef-fwd.h, cdef-manager.cc, cdef-manager.h, cdef-method.cc, cdef-method.h, cdef-object.cc, cdef-object.h, cdef-package.cc, cdef-package.h, cdef-property.cc, cdef-property.h, cdef-utils.cc, cdef-utils.h, ov-base.cc, ov-base.h, ov-bool-mat.cc, ov-builtin.h, ov-cell.cc, ov-class.cc, ov-class.h, ov-classdef.cc, ov-classdef.h, ov-complex.cc, ov-fcn-handle.cc, ov-fcn-handle.h, ov-fcn.h, ov-java.cc, ov-java.h, ov-mex-fcn.h, ov-null-mat.cc, ov-oncleanup.cc, ov-struct.cc, ov-typeinfo.cc, ov-typeinfo.h, ov-usr-fcn.cc, ov-usr-fcn.h, ov.cc, ov.h, octave.cc, octave.h, mk-ops.sh, op-b-b.cc, op-b-bm.cc, op-b-sbm.cc, op-bm-b.cc, op-bm-bm.cc, op-bm-sbm.cc, op-cdm-cdm.cc, op-cell.cc, op-chm.cc, op-class.cc, op-cm-cm.cc, op-cm-cs.cc, op-cm-m.cc, op-cm-s.cc, op-cm-scm.cc, op-cm-sm.cc, op-cs-cm.cc, op-cs-cs.cc, op-cs-m.cc, op-cs-s.cc, op-cs-scm.cc, op-cs-sm.cc, op-dm-dm.cc, op-dm-scm.cc, op-dm-sm.cc, op-dm-template.cc, op-dms-template.cc, op-fcdm-fcdm.cc, op-fcm-fcm.cc, op-fcm-fcs.cc, op-fcm-fm.cc, op-fcm-fs.cc, op-fcn.cc, op-fcs-fcm.cc, op-fcs-fcs.cc, op-fcs-fm.cc, op-fcs-fs.cc, op-fdm-fdm.cc, op-fm-fcm.cc, op-fm-fcs.cc, op-fm-fm.cc, op-fm-fs.cc, op-fs-fcm.cc, op-fs-fcs.cc, op-fs-fm.cc, op-fs-fs.cc, op-i16-i16.cc, op-i32-i32.cc, op-i64-i64.cc, op-i8-i8.cc, op-int-concat.cc, op-m-cm.cc, op-m-cs.cc, op-m-m.cc, op-m-s.cc, op-m-scm.cc, op-m-sm.cc, op-mi.cc, op-pm-pm.cc, op-pm-scm.cc, op-pm-sm.cc, op-pm-template.cc, op-range.cc, op-s-cm.cc, op-s-cs.cc, op-s-m.cc, op-s-s.cc, op-s-scm.cc, op-s-sm.cc, op-sbm-b.cc, op-sbm-bm.cc, op-sbm-sbm.cc, op-scm-cm.cc, op-scm-cs.cc, op-scm-m.cc, op-scm-s.cc, op-scm-scm.cc, op-scm-sm.cc, op-sm-cm.cc, op-sm-cs.cc, op-sm-m.cc, op-sm-s.cc, op-sm-scm.cc, op-sm-sm.cc, op-str-m.cc, op-str-s.cc, op-str-str.cc, op-struct.cc, op-ui16-ui16.cc, op-ui32-ui32.cc, op-ui64-ui64.cc, op-ui8-ui8.cc, ops.h, anon-fcn-validator.cc, anon-fcn-validator.h, bp-table.cc, bp-table.h, comment-list.cc, comment-list.h, filepos.h, lex.h, lex.ll, oct-lvalue.cc, oct-lvalue.h, oct-parse.yy, parse.h, profiler.cc, profiler.h, pt-anon-scopes.cc, pt-anon-scopes.h, pt-arg-list.cc, pt-arg-list.h, pt-args-block.cc, pt-args-block.h, pt-array-list.cc, pt-array-list.h, pt-assign.cc, pt-assign.h, pt-binop.cc, pt-binop.h, pt-bp.cc, pt-bp.h, pt-cbinop.cc, pt-cbinop.h, pt-cell.cc, pt-cell.h, pt-check.cc, pt-check.h, pt-classdef.cc, pt-classdef.h, pt-cmd.h, pt-colon.cc, pt-colon.h, pt-const.cc, pt-const.h, pt-decl.cc, pt-decl.h, pt-eval.cc, pt-eval.h, pt-except.cc, pt-except.h, pt-exp.cc, pt-exp.h, pt-fcn-handle.cc, pt-fcn-handle.h, pt-id.cc, pt-id.h, pt-idx.cc, pt-idx.h, pt-jump.h, pt-loop.cc, pt-loop.h, pt-mat.cc, pt-mat.h, pt-misc.cc, pt-misc.h, pt-pr-code.cc, pt-pr-code.h, pt-select.cc, pt-select.h, pt-spmd.cc, pt-spmd.h, pt-stmt.cc, pt-stmt.h, pt-tm-const.cc, pt-tm-const.h, pt-unop.cc, pt-unop.h, pt-vm-eval.cc, pt-walk.cc, pt-walk.h, pt.cc, pt.h, token.cc, token.h, Range.cc, Range.h, idx-vector.cc, idx-vector.h, range-fwd.h, CollocWt.cc, CollocWt.h, aepbalance.cc, aepbalance.h, chol.cc, chol.h, gepbalance.cc, gepbalance.h, gsvd.cc, gsvd.h, hess.cc, hess.h, lo-mappers.cc, lo-mappers.h, lo-specfun.cc, lo-specfun.h, lu.cc, lu.h, oct-convn.cc, oct-convn.h, oct-fftw.cc, oct-fftw.h, oct-norm.cc, oct-norm.h, oct-rand.cc, oct-rand.h, oct-spparms.cc, oct-spparms.h, qr.cc, qr.h, qrp.cc, qrp.h, randgamma.cc, randgamma.h, randmtzig.cc, randmtzig.h, randpoisson.cc, randpoisson.h, schur.cc, schur.h, sparse-chol.cc, sparse-chol.h, sparse-lu.cc, sparse-lu.h, sparse-qr.cc, sparse-qr.h, svd.cc, svd.h, child-list.cc, child-list.h, dir-ops.cc, dir-ops.h, file-ops.cc, file-ops.h, file-stat.cc, file-stat.h, lo-sysdep.cc, lo-sysdep.h, lo-sysinfo.cc, lo-sysinfo.h, mach-info.cc, mach-info.h, oct-env.cc, oct-env.h, oct-group.cc, oct-group.h, oct-password.cc, oct-password.h, oct-syscalls.cc, oct-syscalls.h, oct-time.cc, oct-time.h, oct-uname.cc, oct-uname.h, action-container.cc, action-container.h, base-list.h, cmd-edit.cc, cmd-edit.h, cmd-hist.cc, cmd-hist.h, f77-fcn.h, file-info.cc, file-info.h, lo-array-errwarn.cc, lo-array-errwarn.h, lo-hash.cc, lo-hash.h, lo-ieee.h, lo-regexp.cc, lo-regexp.h, lo-utils.cc, lo-utils.h, oct-base64.cc, oct-base64.h, oct-glob.cc, oct-glob.h, oct-inttypes.h, oct-mutex.cc, oct-mutex.h, oct-refcount.h, oct-shlib.cc, oct-shlib.h, oct-sparse.cc, oct-sparse.h, oct-string.h, octave-preserve-stream-state.h, pathsearch.cc, pathsearch.h, quit.cc, quit.h, unwind-prot.cc, unwind-prot.h, url-transfer.cc, url-transfer.h : Use new macros to begin/end C++ namespaces.
author Rik <rik@octave.org>
date Thu, 01 Dec 2022 14:23:45 -0800
parents 670a0d878af1
children aac27ad79be6
line wrap: on
line source

////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2012-2022 The Octave Project Developers
//
// See the file COPYRIGHT.md in the top-level directory of this
// distribution or <https://octave.org/copyright/>.
//
// 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-method.h"
#include "cdef-package.h"
#include "cdef-property.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"
#include "unwind-prot.h"

// Define to 1 to enable debugging statements.
#define DEBUG_TRACE 0
#if DEBUG_TRACE
#  include <iostream>
#endif

OCTAVE_BEGIN_NAMESPACE(octave)

  static octave_value
  make_fcn_handle (const octave_value& fcn, const std::string& meth_name,
                   const std::string& class_name)
  {
    octave_value retval;

    if (fcn.is_defined ())
      {
        // FCN_HANDLE: METHOD
        octave_fcn_handle *fh
          = new octave_fcn_handle (fcn, class_name, meth_name);

        retval = octave_value (fh);
      }

    return retval;
  }

  cdef_class::cdef_class_rep::cdef_class_rep (const std::list<cdef_class>& superclasses)
    : cdef_meta_object_rep (), m_member_count (0), m_handle_class (false),
      m_meta (false)
  {
    put ("SuperClasses", to_ov (superclasses));
    m_implicit_ctor_list = superclasses;
  }

  cdef_method
  cdef_class::cdef_class_rep::find_method (const std::string& nm, bool local)
  {
    auto it = m_method_map.find (nm);

    if (it == m_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 tree_walker
  {
  public:

    ctor_analyzer (void) = delete;

    ctor_analyzer (const std::string& ctor, const std::string& obj)
      : tree_walker (), m_who (ctor), m_obj_name (obj) { }

    ctor_analyzer (const ctor_analyzer&) = delete;

    ctor_analyzer& operator = (const ctor_analyzer&) = delete;

    ~ctor_analyzer (void) = default;

    void visit_statement (tree_statement& t)
    {
      if (t.is_expression ())
        t.expression ()->accept (*this);
    }

    void visit_simple_assignment (tree_simple_assignment& t)
    {
      t.right_hand_side ()->accept (*this);
    }

    void visit_multi_assignment (tree_multi_assignment& t)
    {
      t.right_hand_side ()->accept (*this);
    }

    void visit_index_expression (tree_index_expression& t)
    {
      t.expression ()->accept (*this);
    }

    std::list<cdef_class> get_constructor_list (void) const
    { return m_ctor_list; }

    // NO-OP

    void visit_anon_fcn_handle (tree_anon_fcn_handle&) { }
    void visit_argument_list (tree_argument_list&) { }
    void visit_binary_expression (tree_binary_expression&) { }
    void visit_break_command (tree_break_command&) { }
    void visit_colon_expression (tree_colon_expression&) { }
    void visit_continue_command (tree_continue_command&) { }
    void visit_decl_command (tree_decl_command&) { }
    void visit_decl_init_list (tree_decl_init_list&) { }
    void visit_decl_elt (tree_decl_elt&) { }
    void visit_simple_for_command (tree_simple_for_command&) { }
    void visit_complex_for_command (tree_complex_for_command&) { }
    void visit_octave_user_script (octave_user_script&) { }
    void visit_octave_user_function (octave_user_function&) { }
    void visit_function_def (tree_function_def&) { }
    void visit_identifier (tree_identifier&) { }
    void visit_if_clause (tree_if_clause&) { }
    void visit_if_command (tree_if_command&) { }
    void visit_if_command_list (tree_if_command_list&) { }
    void visit_switch_case (tree_switch_case&) { }
    void visit_switch_case_list (tree_switch_case_list&) { }
    void visit_switch_command (tree_switch_command&) { }
    void visit_matrix (tree_matrix&) { }
    void visit_cell (tree_cell&) { }
    void visit_no_op_command (tree_no_op_command&) { }
    void visit_constant (tree_constant&) { }
    void visit_fcn_handle (tree_fcn_handle&) { }
    void visit_parameter_list (tree_parameter_list&) { }
    void visit_postfix_expression (tree_postfix_expression&) { }
    void visit_prefix_expression (tree_prefix_expression&) { }
    void visit_return_command (tree_return_command&) { }
    void visit_try_catch_command (tree_try_catch_command&) { }
    void visit_unwind_protect_command (tree_unwind_protect_command&) { }
    void visit_while_command (tree_while_command&) { }
    void visit_do_until_command (tree_do_until_command&) { }

    void visit_superclass_ref (tree_superclass_ref& t)
    {
      if (t.method_name () == m_obj_name)
        {
          std::string class_name = t.class_name ();

          cdef_class cls = lookup_class (class_name, false);

          if (cls.ok ())
            m_ctor_list.push_back (cls);
        }
    }

  private:

    // The name of the constructor being analyzed.
    std::string m_who;

    // The name of the first output argument of the constructor.
    std::string m_obj_name;

    // The list of superclass constructors that are explicitly called.
    std::list<cdef_class> m_ctor_list;
  };

  void
  cdef_class::cdef_class_rep::install_method (const cdef_method& meth)
  {
    m_method_map[meth.get_name ()] = meth;

    m_member_count++;

    if (meth.is_constructor ())
      {
        // Analyze the constructor code to determine what superclass
        // constructors are called explicitly.

        octave_value ov_fcn = meth.get_function ();

        if (ov_fcn.is_defined ())
          {
            octave_user_function *uf = ov_fcn.user_function_value (true);

            if (uf)
              {
                tree_parameter_list *ret_list = uf->return_list ();
                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 m_obj_name = ret_list->front ()->name ();
                ctor_analyzer a (meth.get_name (), m_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

                    m_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 (bool include_ctor)
  {
    std::map<std::string, cdef_method> meths;

    find_methods (meths, false, include_ctor);

    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,
                                              bool include_ctor)
  {
    std::map<std::string, cdef_method> methods;

    find_methods (methods, only_inherited, include_ctor);

    return methods;
  }

  void
  cdef_class::cdef_class_rep::find_methods (std::map<std::string,
                                            cdef_method>& meths,
                                            bool only_inherited,
                                            bool include_ctor)
  {
    load_all_methods ();

    method_const_iterator it;

    for (it = m_method_map.begin (); it != m_method_map.end (); ++it)
      {
        if (include_ctor || ! 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, false);
      }
  }

  cdef_property
  cdef_class::cdef_class_rep::find_property (const std::string& nm)
  {
    auto it = m_property_map.find (nm);

    if (it != m_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)
  {
    m_property_map[prop.get_name ()] = prop;

    m_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 = m_property_map.begin (); it != m_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 : m_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 : m_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");

    // FIXME: would it be better to tell find_method above to not find
    // overloaded functions?

    if (dtor.ok () && dtor.is_defined_in_class (get_name ()))
      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)
  {
    std::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 = __get_cdef_manager__ ();

    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 : m_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 ()));
          }
      }

    m_count++;
    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 : m_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::get_method (const std::string& name) const
  {
    auto p = m_method_map.find (name);

    if (p == m_method_map.end ())
      return octave_value ();

    return p->second.get_function ();
  }


  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 = __get_cdef_manager__ ();

        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 (tree_evaluator& tw,
                           tree_classdef_attribute *t)
  {
    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 expr->evaluate (tw);
      }
    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 (interpreter& interp,
                               tree_classdef *t, bool is_at_folder)
  {
    cdef_class retval;

    // Class creation

    std::string class_name = t->ident ()->name ();
    std::string full_class_name = class_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

    // Push a dummy scope frame on the call stack that corresponds to
    // the scope that was used when parsing classdef object.  Without
    // this, we may pick up stray values from the current scope when
    // evaluating expressions found in things like attribute lists.

    tree_evaluator& tw = interp.get_evaluator ();

    tw.push_dummy_scope (full_class_name);
    unwind_action pop_scope (&tree_evaluator::pop_scope, &tw);

    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 = __get_cdef_manager__ ();

    retval = cdm.make_class (full_class_name, slist);

    retval.doc_string (t->doc_string ());
    retval.file_name (t->file_name ());

    // 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));
      }

    // FIXME: instead of attaching attributes here, pass them to
    // cdef_manager::make_method.  The classdef manager contains a meta
    // object with a list of all valid properties that can be used to
    // validate the attribute list (see bug #60593).

    // Class attributes

    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);
          }
      }

    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<tree_classdef_methods_block *> mb_list = b->methods_list ();

        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, mname, full_class_name);
                    else if (mprefix == "set.")
                      set_methods[mname.substr (4)]
                        = make_fcn_handle (mtd, mname, full_class_name);
                    else
                      {
                        cdef_method meth = cdm.make_method (retval, mname, mtd);

#if DEBUG_TRACE
                        std::cerr << (mname == class_name ? "constructor"
                                      : "method")
                                  << ": " << mname << std::endl;
#endif

                        // FIXME: instead of attaching attributes here,
                        // pass them to cdef_manager::make_method.  The
                        // classdef manager contains a meta object with
                        // a list of all valid properties that can be
                        // used to validate the attribute list (see bug
                        // #60593).

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

                    tree_expression *expr = prop_p->expression ();
                    if (expr)
                      {
                        octave_value pvalue = expr->evaluate (tw);

#if DEBUG_TRACE
                        std::cerr << "property default: "
                                  << attribute_value_to_string (prop_p, pvalue)
                                  << std::endl;
#endif

                        prop.put ("DefaultValue", pvalue);
                      }

                    // FIXME: instead of attaching attributes here, pass
                    // them to cdef_manager::make_property.  The
                    // classdef manager contains a meta object with a
                    // list of all valid properties that can be used to
                    // validate the attribute list (see bug #60593).

                    // 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_value
  cdef_class::get_method_function (const std::string& /* nm */)
  {
    return octave_value (new octave_classdef_meta (*this));
  }

OCTAVE_END_NAMESPACE(octave)