GNU Octave  9.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
cdef-class.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 2012-2024 The Octave Project Developers
4 //
5 // See the file COPYRIGHT.md in the top-level directory of this
6 // distribution or <https://octave.org/copyright/>.
7 //
8 // This file is part of Octave.
9 //
10 // Octave is free software: you can redistribute it and/or modify it
11 // under the terms of the GNU General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // Octave is distributed in the hope that it will be useful, but
16 // WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU General Public License for more details.
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with Octave; see the file COPYING. If not, see
22 // <https://www.gnu.org/licenses/>.
23 //
24 ////////////////////////////////////////////////////////////////////////
25 
26 #if defined (HAVE_CONFIG_H)
27 # include "config.h"
28 #endif
29 
30 #include <algorithm>
31 #include <iomanip>
32 
33 #include "cdef-class.h"
34 #include "cdef-manager.h"
35 #include "cdef-method.h"
36 #include "cdef-package.h"
37 #include "cdef-property.h"
38 #include "cdef-utils.h"
39 #include "errwarn.h"
40 #include "interpreter-private.h"
41 #include "interpreter.h"
42 #include "load-path.h"
43 #include "ov-builtin.h"
44 #include "ov-classdef.h"
45 #include "ov-fcn-handle.h"
46 #include "ov-usr-fcn.h"
47 #include "parse.h"
48 #include "pt-assign.h"
49 #include "pt-classdef.h"
50 #include "pt-eval.h"
51 #include "pt-idx.h"
52 #include "pt-misc.h"
53 #include "pt-stmt.h"
54 #include "pt-walk.h"
55 #include "unwind-prot.h"
56 
57 // Define to 1 to enable debugging statements.
58 #define DEBUG_TRACE 0
59 #if DEBUG_TRACE
60 # include <iostream>
61 #endif
62 
64 
65 static octave_value
66 make_fcn_handle (const octave_value& fcn, const std::string& meth_name,
67  const std::string& class_name)
68 {
69  octave_value retval;
70 
71  if (fcn.is_defined ())
72  {
73  // FCN_HANDLE: METHOD
75  = new octave_fcn_handle (fcn, class_name, meth_name);
76 
77  retval = octave_value (fh);
78  }
79 
80  return retval;
81 }
82 
83 cdef_class::cdef_class_rep::cdef_class_rep (const std::list<cdef_class>& superclasses)
84  : cdef_meta_object_rep (), m_member_count (0), m_handle_class (false),
85  m_meta (false)
86 {
87  put ("SuperClasses", to_ov (superclasses));
88  m_implicit_ctor_list = superclasses;
89 }
90 
92 cdef_class::cdef_class_rep::find_method (const std::string& nm, bool local)
93 {
94  auto it = m_method_map.find (nm);
95 
96  if (it == m_method_map.end ())
97  {
98  // FIXME: look into class directory
99  }
100  else
101  {
102  cdef_method& meth = it->second;
103 
104  // FIXME: check if method reload needed
105 
106  if (meth.ok ())
107  return meth;
108  }
109 
110  if (! local)
111  {
112  // Look into superclasses
113 
114  Cell super_classes = get ("SuperClasses").cell_value ();
115 
116  for (int i = 0; i < super_classes.numel (); i++)
117  {
118  cdef_class cls = lookup_class (super_classes(i));
119 
120  cdef_method meth = cls.find_method (nm);
121 
122  if (meth.ok ())
123  return meth;
124  }
125  }
126 
127  return cdef_method ();
128 }
129 
130 class ctor_analyzer : public tree_walker
131 {
132 public:
133 
134  ctor_analyzer () = delete;
135 
136  ctor_analyzer (const std::string& ctor, const std::string& obj)
137  : tree_walker (), m_who (ctor), m_obj_name (obj) { }
138 
139  OCTAVE_DISABLE_COPY_MOVE (ctor_analyzer)
140 
141  ~ctor_analyzer () = default;
142 
144  {
145  if (t.is_expression ())
146  t.expression ()->accept (*this);
147  }
148 
150  {
151  t.right_hand_side ()->accept (*this);
152  }
153 
155  {
156  t.right_hand_side ()->accept (*this);
157  }
158 
160  {
161  t.expression ()->accept (*this);
162  }
163 
164  std::list<cdef_class> get_constructor_list () const
165  { return m_ctor_list; }
166 
167  // NO-OP
168 
177  void visit_decl_elt (tree_decl_elt&) { }
184  void visit_if_clause (tree_if_clause&) { }
190  void visit_matrix (tree_matrix&) { }
191  void visit_cell (tree_cell&) { }
193  void visit_constant (tree_constant&) { }
203 
205  {
206  if (t.method_name () == m_obj_name)
207  {
208  std::string class_name = t.class_name ();
209 
210  cdef_class cls = lookup_class (class_name, false);
211 
212  if (cls.ok ())
213  m_ctor_list.push_back (cls);
214  }
215  }
216 
217 private:
218 
219  // The name of the constructor being analyzed.
220  std::string m_who;
221 
222  // The name of the first output argument of the constructor.
223  std::string m_obj_name;
224 
225  // The list of superclass constructors that are explicitly called.
226  std::list<cdef_class> m_ctor_list;
227 };
228 
229 void
230 cdef_class::cdef_class_rep::install_method (const cdef_method& meth)
231 {
232  m_method_map[meth.get_name ()] = meth;
233 
234  m_member_count++;
235 
236  if (meth.is_constructor ())
237  {
238  // Analyze the constructor code to determine what superclass
239  // constructors are called explicitly.
240 
241  octave_value ov_fcn = meth.get_function ();
242 
243  if (ov_fcn.is_defined ())
244  {
245  octave_user_function *uf = ov_fcn.user_function_value (true);
246 
247  if (uf)
248  {
249  tree_parameter_list *ret_list = uf->return_list ();
250  tree_statement_list *body = uf->body ();
251 
252  if (! ret_list || ret_list->size () != 1)
253  error ("%s: invalid constructor output arguments",
254  meth.get_name ().c_str ());
255 
256  std::string m_obj_name = ret_list->front ()->name ();
257  ctor_analyzer a (meth.get_name (), m_obj_name);
258 
259  body->accept (a);
260 
261  std::list<cdef_class> explicit_ctor_list
262  = a.get_constructor_list ();
263 
264  for (const auto& cdef_cls : explicit_ctor_list)
265  {
266 #if DEBUG_TRACE
267  std::cerr << "explicit superclass constructor: "
268  << cdef_cls.get_name () << std::endl;
269 #endif
270 
271  m_implicit_ctor_list.remove (cdef_cls);
272  }
273  }
274  }
275  }
276 }
277 
278 void
279 cdef_class::cdef_class_rep::load_all_methods ()
280 {
281  // FIXME: re-scan class directory
282 }
283 
284 Cell
285 cdef_class::cdef_class_rep::get_methods (bool include_ctor)
286 {
287  std::map<std::string, cdef_method> meths;
288 
289  find_methods (meths, false, include_ctor);
290 
291  Cell c (meths.size (), 1);
292 
293  int idx = 0;
294 
295  for (const auto& nm_mthd : meths)
296  c(idx++, 0) = to_ov (nm_mthd.second);
297 
298  return c;
299 }
300 
301 std::map<std::string, cdef_method>
302 cdef_class::cdef_class_rep::get_method_map (bool only_inherited,
303  bool include_ctor)
304 {
305  std::map<std::string, cdef_method> methods;
306 
307  find_methods (methods, only_inherited, include_ctor);
308 
309  return methods;
310 }
311 
312 void
313 cdef_class::cdef_class_rep::find_methods (std::map<std::string,
314  cdef_method>& meths,
315  bool only_inherited,
316  bool include_ctor)
317 {
318  load_all_methods ();
319 
320  for (const auto& it : m_method_map)
321  {
322  if (include_ctor || ! it.second.is_constructor ())
323  {
324  std::string nm = it.second.get_name ();
325 
326  if (meths.find (nm) == meths.end ())
327  {
328  if (only_inherited)
329  {
330  octave_value acc = it.second.get ("Access");
331 
332  if (! acc.is_string ()
333  || acc.string_value () == "private")
334  continue;
335  }
336 
337  meths[nm] = it.second;
338  }
339  }
340  }
341 
342  // Look into superclasses
343 
344  Cell super_classes = get ("SuperClasses").cell_value ();
345 
346  for (int i = 0; i < super_classes.numel (); i++)
347  {
348  cdef_class cls = lookup_class (super_classes(i));
349 
350  cls.get_rep ()->find_methods (meths, true, false);
351  }
352 }
353 
355 cdef_class::cdef_class_rep::find_property (const std::string& nm)
356 {
357  auto it = m_property_map.find (nm);
358 
359  if (it != m_property_map.end ())
360  {
361  cdef_property& prop = it->second;
362 
363  if (prop.ok ())
364  return prop;
365  }
366 
367  // Look into superclasses
368 
369  Cell super_classes = get ("SuperClasses").cell_value ();
370 
371  for (int i = 0; i < super_classes.numel (); i++)
372  {
373  cdef_class cls = lookup_class (super_classes(i));
374 
375  cdef_property prop = cls.find_property (nm);
376 
377  if (prop.ok ())
378  return prop;
379  }
380 
381  return cdef_property ();
382 }
383 
384 void
385 cdef_class::cdef_class_rep::install_property (const cdef_property& prop)
386 {
387  m_property_map[prop.get_name ()] = prop;
388 
389  m_member_count++;
390 }
391 
392 Cell
393 cdef_class::cdef_class_rep::get_properties (int mode)
394 {
395  std::map<std::string, cdef_property> props;
396 
397  props = get_property_map (mode);
398 
399  Cell c (props.size (), 1);
400 
401  int idx = 0;
402 
403  for (const auto& pname_prop : props)
404  c(idx++, 0) = to_ov (pname_prop.second);
405 
406  return c;
407 }
408 
409 std::map<std::string, cdef_property>
410 cdef_class::cdef_class_rep::get_property_map (int mode)
411 {
412  std::map<std::string, cdef_property> props;
413 
414  find_properties (props, mode);
415 
416  return props;
417 }
418 
419 void
420 cdef_class::cdef_class_rep::find_properties (std::map<std::string,
421  cdef_property>& props,
422  int mode)
423 {
424  for (const auto& it : m_property_map)
425  {
426  std::string nm = it.second.get_name ();
427 
428  if (props.find (nm) == props.end ())
429  {
430  if (mode == property_inherited)
431  {
432  octave_value acc = it.second.get ("GetAccess");
433 
434  if (! acc.is_string ()
435  || acc.string_value () == "private")
436  continue;
437  }
438 
439  props[nm] = it.second;
440  }
441  }
442 
443  // Look into superclasses
444 
445  Cell super_classes = get ("SuperClasses").cell_value ();
446 
447  for (int i = 0; i < super_classes.numel (); i++)
448  {
449  cdef_class cls = lookup_class (super_classes(i));
450 
451  cls.get_rep ()->find_properties (props,
452  (mode == property_all
453  ? property_all
454  : property_inherited));
455  }
456 }
457 
458 void
459 cdef_class::cdef_class_rep::find_names (std::set<std::string>& names,
460  bool all)
461 {
462  load_all_methods ();
463 
464  for (const auto& cls_fnmap : m_method_map)
465  {
466  if (! cls_fnmap.second.is_constructor ())
467  {
468  std::string nm = cls_fnmap.second.get_name ();
469 
470  if (! all)
471  {
472  octave_value acc = cls_fnmap.second.get ("Access");
473 
474  if (! acc.is_string()
475  || acc.string_value () != "public")
476  continue;
477  }
478 
479  names.insert (nm);
480  }
481  }
482 
483  for (const auto& pname_prop : m_property_map)
484  {
485  std::string nm = pname_prop.second.get_name ();
486 
487  if (! all)
488  {
489  octave_value acc = pname_prop.second.get ("GetAccess");
490 
491  if (! acc.is_string()
492  || acc.string_value () != "public")
493  continue;
494  }
495 
496  names.insert (nm);
497  }
498 
499  // Look into superclasses
500 
501  Cell super_classes = get ("SuperClasses").cell_value ();
502 
503  for (int i = 0; i < super_classes.numel (); i++)
504  {
505  cdef_class cls = lookup_class (super_classes(i));
506 
507  cls.get_rep ()->find_names (names, all);
508  }
509 }
510 
512 cdef_class::cdef_class_rep::get_names ()
513 {
514  std::set<std::string> names;
515 
516  find_names (names, false);
517 
518  string_vector v (names);
519 
520  return v.sort (true);
521 }
522 
523 void
524 cdef_class::cdef_class_rep::delete_object (const cdef_object& obj)
525 {
526  cdef_method dtor = find_method ("delete");
527 
528  // FIXME: would it be better to tell find_method above to not find
529  // overloaded functions?
530 
531  if (dtor.ok () && dtor.is_defined_in_class (get_name ()))
532  dtor.execute (obj, octave_value_list (), 0, true, "destructor");
533 
534  // FIXME: should we destroy corresponding properties here?
535 
536  // Call "delete" in super classes
537 
538  Cell super_classes = get ("SuperClasses").cell_value ();
539 
540  for (int i = 0; i < super_classes.numel (); i++)
541  {
542  cdef_class cls = lookup_class (super_classes(i));
543 
544  if (cls.get_name () != "handle")
545  cls.delete_object (obj);
546  }
547 }
548 
550 cdef_class::cdef_class_rep::meta_subsref (const std::string& type,
551  const std::list<octave_value_list>& idx,
552  int nargout)
553 {
554  std::size_t skip = 1;
555 
556  octave_value_list retval;
557 
558  switch (type[0])
559  {
560  case '(':
561  // Constructor call
562 
563 #if DEBUG_TRACE
564  std::cerr << "constructor" << std::endl;
565 #endif
566 
567  retval(0) = construct (idx.front ());
568  break;
569 
570  case '.':
571  {
572  // Static method, constant (or property?)
573 
574 #if DEBUG_TRACE
575  std::cerr << "static method/property" << std::endl;
576 #endif
577 
578  if (idx.front ().length () != 1)
579  error ("invalid meta.class indexing");
580 
581  std::string nm = idx.front ()(0).xstring_value ("invalid meta.class indexing, expected a method or property name");
582 
583  cdef_method meth = find_method (nm);
584 
585  if (meth.ok ())
586  {
587  if (! meth.is_static ())
588  error ("method '%s' is not static", nm.c_str ());
589 
590  octave_value_list args;
591 
592  if (type.length () > 1 && idx.size () > 1 && type[1] == '(')
593  {
594  args = *(++(idx.begin ()));
595  skip++;
596  }
597 
598  retval = meth.execute (args, (type.length () > skip
599  ? 1 : nargout), true,
600  "meta.class");
601  }
602  else
603  {
604  cdef_property prop = find_property (nm);
605 
606  if (! prop.ok ())
607  error ("no such method or property '%s'", nm.c_str ());
608 
609  if (! prop.is_constant ())
610  error ("property '%s' is not constant", nm.c_str ());
611 
612  retval(0) = prop.get_value (true, "meta.class");
613  }
614  }
615  break;
616 
617  default:
618  error ("invalid meta.class indexing");
619  break;
620  }
621 
622  if (type.length () > skip && idx.size () > skip && ! retval.empty ())
623  retval = retval(0).next_subsref (nargout, type, idx, skip);
624 
625  return retval;
626 }
627 
628 void
629 cdef_class::cdef_class_rep::meta_release ()
630 {
632 
633  cdm.unregister_class (wrap ());
634 }
635 
636 void
637 cdef_class::cdef_class_rep::initialize_object (cdef_object& obj)
638 {
639  // Populate the object with default property values
640 
641  std::list<cdef_class> super_classes
642  = lookup_classes (get ("SuperClasses").cell_value ());
643 
644  for (auto& cls : super_classes)
645  cls.initialize_object (obj);
646 
647  for (const auto& pname_prop : m_property_map)
648  {
649  if (! pname_prop.second.get ("Dependent").bool_value ())
650  {
651  octave_value pvalue = pname_prop.second.get ("DefaultValue");
652 
653  if (pvalue.is_defined ())
654  obj.put (pname_prop.first, pvalue);
655  else
656  obj.put (pname_prop.first, octave_value (Matrix ()));
657  }
658  }
659 
660  m_count++;
661  obj.mark_for_construction (cdef_class (this));
662 }
663 
664 void
665 cdef_class::cdef_class_rep::run_constructor (cdef_object& obj,
666  const octave_value_list& args)
667 {
668  octave_value_list empty_args;
669 
670  for (const auto& cls : m_implicit_ctor_list)
671  {
672  cdef_class supcls = lookup_class (cls);
673 
674  supcls.run_constructor (obj, empty_args);
675  }
676 
677  std::string cls_name = get_name ();
678  std::string ctor_name = get_base_name (cls_name);
679 
680  cdef_method ctor = find_method (ctor_name);
681 
682  if (ctor.ok ())
683  {
684  octave_value_list ctor_args (args);
685  octave_value_list ctor_retval;
686 
687  ctor_args.prepend (to_ov (obj));
688  ctor_retval = ctor.execute (ctor_args, 1, true, "constructor");
689 
690  if (ctor_retval.length () != 1)
691  error ("%s: invalid number of output arguments for classdef constructor",
692  ctor_name.c_str ());
693 
694  obj = to_cdef (ctor_retval(0));
695  }
696 
697  obj.mark_as_constructed (wrap ());
698 }
699 
701 cdef_class::cdef_class_rep::get_method (const std::string& name) const
702 {
703  auto p = m_method_map.find (name);
704 
705  if (p == m_method_map.end ())
706  return octave_value ();
707 
708  return p->second.get_function ();
709 }
710 
712 cdef_class::cdef_class_rep::get_method (int line) const
713 {
714  octave_value closest_match;
715  int closest_match_end_line = std::numeric_limits<int>::max ();
716  // Since we have a dynamic cast, performance could be an issue if this is
717  // called from a critical path. If performance is an issue, we can cache
718  // an ordered version of the method map
719  for (auto i = m_method_map.cbegin (); i != m_method_map.cend (); ++i)
720  {
721  const octave_value& fcn = i->second.get_function ();
722  octave_user_code *user_code = fcn.user_code_value ();
723 
724  if (user_code == nullptr)
725  continue;
726 
728  = dynamic_cast<octave_user_function*> (user_code);
729 
730  if (pfcn == nullptr)
731  continue;
732 
733  const int e = pfcn->ending_line ();
734  if (line <= e && e <= closest_match_end_line && pfcn->is_defined ()
735  && pfcn->is_user_code ())
736  {
737  closest_match = fcn;
738  closest_match_end_line = e;
739  }
740  }
741 
742  return closest_match;
743 }
744 
746 cdef_class::cdef_class_rep::construct (const octave_value_list& args)
747 {
748  cdef_object obj = construct_object (args);
749 
750  if (obj.ok ())
751  return to_ov (obj);
752 
753  return octave_value ();
754 }
755 
757 cdef_class::cdef_class_rep::construct_object (const octave_value_list& args)
758 {
759  if (is_abstract ())
760  error ("cannot instantiate object for abstract class '%s'",
761  get_name ().c_str ());
762 
763  cdef_object obj;
764 
765  if (is_meta_class ())
766  {
767  // This code path is only used to create empty meta objects
768  // as filler for the empty values within a meta object array.
769 
770  cdef_class this_cls = wrap ();
771 
772  static cdef_object empty_class;
773 
775 
776  if (this_cls == cdm.meta_class ())
777  {
778  if (! empty_class.ok ())
779  empty_class = cdm.make_class ("", std::list<cdef_class> ());
780  obj = empty_class;
781  }
782  else if (this_cls == cdm.meta_property ())
783  {
784  static cdef_property empty_property;
785 
786  if (! empty_class.ok ())
787  empty_class = cdm.make_class ("", std::list<cdef_class> ());
788  if (! empty_property.ok ())
789  empty_property = cdm.make_property (empty_class, "");
790  obj = empty_property;
791  }
792  else if (this_cls == cdm.meta_method ())
793  {
794  static cdef_method empty_method;
795 
796  if (! empty_class.ok ())
797  empty_class = cdm.make_class ("", std::list<cdef_class> ());
798  if (! empty_method.ok ())
799  empty_method = cdm.make_method (empty_class, "", octave_value ());
800  obj = empty_method;
801  }
802  else if (this_cls == cdm.meta_package ())
803  {
804  static cdef_package empty_package;
805 
806  if (! empty_package.ok ())
807  empty_package = cdm.make_package ("");
808  obj = empty_package;
809  }
810  else
811  panic_impossible ();
812 
813  return obj;
814  }
815  else
816  {
817  if (is_handle_class ())
818  obj = cdef_object (new handle_cdef_object ());
819  else
820  obj = cdef_object (new value_cdef_object ());
821  obj.set_class (wrap ());
822 
823  initialize_object (obj);
824 
825  run_constructor (obj, args);
826 
827  return obj;
828  }
829 
830  return cdef_object ();
831 }
832 
833 static octave_value
834 compute_attribute_value (tree_evaluator& tw,
836 {
837  tree_expression *expr = t->expression ();
838 
839  if (expr)
840  {
841  if (expr->is_identifier ())
842  {
843  std::string s = expr->name ();
844 
845  if (s == "public")
846  return std::string ("public");
847  else if (s == "protected")
848  return std::string ("protected");
849  else if (s == "private")
850  return std::string ("private");
851  }
852 
853  return expr->evaluate (tw);
854  }
855  else
856  return octave_value (true);
857 }
858 
859 template <typename T>
860 static std::string
861 attribute_value_to_string (T *t, octave_value v)
862 {
863  if (v.is_string ())
864  return v.string_value ();
865  else if (t->expression ())
866  return t->expression ()->original_text ();
867  else
868  return "true";
869 }
870 
873  tree_classdef *t, bool is_at_folder)
874 {
875  cdef_class retval;
876 
877  // Class creation
878 
879  std::string class_name = t->ident ()->name ();
880  std::string full_class_name = class_name;
881  if (! t->package_name ().empty ())
882  full_class_name = t->package_name () + '.' + full_class_name;
883 
884 #if DEBUG_TRACE
885  std::cerr << "class: " << full_class_name << std::endl;
886 #endif
887 
888  // Push a dummy scope frame on the call stack that corresponds to
889  // the scope that was used when parsing classdef object. Without
890  // this, we may pick up stray values from the current scope when
891  // evaluating expressions found in things like attribute lists.
892 
893  tree_evaluator& tw = interp.get_evaluator ();
894 
895  tw.push_dummy_scope (full_class_name);
896  unwind_action pop_scope (&tree_evaluator::pop_scope, &tw);
897 
898  std::list<cdef_class> slist;
899 
900  if (t->superclass_list ())
901  {
902  for (auto& scls : (*t->superclass_list ()))
903  {
904  std::string sclass_name = (scls)->class_name ();
905 
906 #if DEBUG_TRACE
907  std::cerr << "superclass: " << sclass_name << std::endl;
908 #endif
909 
910  cdef_class sclass = lookup_class (sclass_name);
911 
912  if (sclass.get ("Sealed").bool_value ())
913  error ("'%s' cannot inherit from '%s', because it is sealed",
914  full_class_name.c_str (), sclass_name.c_str ());
915 
916  slist.push_back (sclass);
917  }
918  }
919 
921 
922  retval = cdm.make_class (full_class_name, slist);
923 
924  retval.doc_string (t->doc_string ());
925  retval.file_name (t->file_name ());
926 
927  // Package owning this class
928 
929  if (! t->package_name ().empty ())
930  {
931  cdef_package pack = cdm.find_package (t->package_name ());
932 
933  if (pack.ok ())
934  retval.put ("ContainingPackage", to_ov (pack));
935  }
936 
937  // FIXME: instead of attaching attributes here, pass them to
938  // cdef_manager::make_method. The classdef manager contains a meta
939  // object with a list of all valid properties that can be used to
940  // validate the attribute list (see bug #60593).
941 
942  // Class attributes
943 
944  if (t->attribute_list ())
945  {
946  for (const auto& attr : (*t->attribute_list ()))
947  {
948  std::string aname = attr->ident ()->name ();
949  octave_value avalue = compute_attribute_value (tw, attr);
950 
951 #if DEBUG_TRACE
952  std::cerr << "class attribute: " << aname << " = "
953  << attribute_value_to_string (attr, avalue) << std::endl;
954 #endif
955 
956  retval.put (aname, avalue);
957  }
958  }
959 
960  tree_classdef_body *b = t->body ();
961 
962  if (b)
963  {
964  // Keep track of the get/set accessor methods. They will be used
965  // later on when creating properties.
966 
967  std::map<std::string, octave_value> get_methods;
968  std::map<std::string, octave_value> set_methods;
969 
970  // Method blocks
971 
972  std::list<tree_classdef_methods_block *> mb_list = b->methods_list ();
973 
974  load_path& lp = interp.get_load_path ();
975 
976  for (auto& mb_p : mb_list)
977  {
978  std::map<std::string, octave_value> amap;
979 
980 #if DEBUG_TRACE
981  std::cerr << "method block" << std::endl;
982 #endif
983 
984  // Method attributes
985 
986  if (mb_p->attribute_list ())
987  {
988  for (auto& attr_p : *mb_p->attribute_list ())
989  {
990  std::string aname = attr_p->ident ()->name ();
991  octave_value avalue = compute_attribute_value (tw, attr_p);
992 
993 #if DEBUG_TRACE
994  std::cerr << "method attribute: " << aname << " = "
995  << attribute_value_to_string (attr_p, avalue)
996  << std::endl;
997 #endif
998 
999  amap[aname] = avalue;
1000  }
1001  }
1002 
1003  // Methods
1004 
1005  if (mb_p->element_list ())
1006  {
1007  for (auto& mtd : *mb_p->element_list ())
1008  {
1009  std::string mname = mtd.function_value ()->name ();
1010  std::string mprefix = mname.substr (0, 4);
1011 
1012  if (mprefix == "get.")
1013  get_methods[mname.substr (4)]
1014  = make_fcn_handle (mtd, mname, full_class_name);
1015  else if (mprefix == "set.")
1016  set_methods[mname.substr (4)]
1017  = make_fcn_handle (mtd, mname, full_class_name);
1018  else
1019  {
1020  cdef_method meth = cdm.make_method (retval, mname, mtd);
1021 
1022 #if DEBUG_TRACE
1023  std::cerr << (mname == class_name ? "constructor"
1024  : "method")
1025  << ": " << mname << std::endl;
1026 #endif
1027 
1028  // FIXME: instead of attaching attributes here,
1029  // pass them to cdef_manager::make_method. The
1030  // classdef manager contains a meta object with
1031  // a list of all valid properties that can be
1032  // used to validate the attribute list (see bug
1033  // #60593).
1034 
1035  for (auto& attrnm_val : amap)
1036  meth.put (attrnm_val.first, attrnm_val.second);
1037 
1038  retval.install_method (meth);
1039  }
1040  }
1041  }
1042  }
1043 
1044  if (is_at_folder)
1045  {
1046  // Look for all external methods visible on octave path at the
1047  // time of loading of the class.
1048  //
1049  // FIXME: This is an "extension" to Matlab behavior, which only
1050  // looks in the @-folder containing the original classdef file.
1051  // However, this is easier to implement it that way at the moment.
1052 
1053  std::list<std::string> external_methods
1054  = lp.methods (full_class_name);
1055 
1056  for (const auto& mtdnm : external_methods)
1057  {
1058  // FIXME: should we issue a warning if the method is already
1059  // defined in the classdef file?
1060 
1061  if (mtdnm != class_name
1062  && ! retval.find_method (mtdnm, true).ok ())
1063  {
1064  // Create a dummy method that is used until the actual
1065  // method is loaded.
1067 
1068  fcn->stash_function_name (mtdnm);
1069 
1070  cdef_method meth
1071  = cdm.make_method (retval, mtdnm, octave_value (fcn));
1072 
1073  retval.install_method (meth);
1074  }
1075  }
1076  }
1077 
1078  // Property blocks
1079 
1080  // FIXME: default property expression should be able to call static
1081  // methods of the class being constructed. A restricted
1082  // CLASSNAME symbol should be added to the scope before
1083  // evaluating default value expressions.
1084 
1085  std::list<tree_classdef_properties_block *> pb_list
1086  = b->properties_list ();
1087 
1088  for (auto& pb_p : pb_list)
1089  {
1090  std::map<std::string, octave_value> amap;
1091 
1092 #if DEBUG_TRACE
1093  std::cerr << "property block" << std::endl;
1094 #endif
1095 
1096  // Property attributes
1097 
1098  if (pb_p->attribute_list ())
1099  {
1100  for (auto& attr_p : *pb_p->attribute_list ())
1101  {
1102  std::string aname = attr_p->ident ()->name ();
1103  octave_value avalue = compute_attribute_value (tw, attr_p);
1104 
1105 #if DEBUG_TRACE
1106  std::cerr << "property attribute: " << aname << " = "
1107  << attribute_value_to_string (attr_p, avalue)
1108  << std::endl;
1109 #endif
1110 
1111  if (aname == "Access")
1112  {
1113  amap["GetAccess"] = avalue;
1114  amap["SetAccess"] = avalue;
1115  }
1116  else
1117  amap[aname] = avalue;
1118  }
1119  }
1120 
1121  // Properties
1122 
1123  if (pb_p->element_list ())
1124  {
1125  for (auto& prop_p : *pb_p->element_list ())
1126  {
1127  std::string prop_name = prop_p->ident ()->name ();
1128 
1129  cdef_property prop = cdm.make_property (retval, prop_name);
1130 
1131  prop.doc_string (prop_p->doc_string ());
1132 
1133 #if DEBUG_TRACE
1134  std::cerr << "property: " << prop_p->ident ()->name ()
1135  << std::endl;
1136 #endif
1137 
1138  tree_expression *expr = prop_p->expression ();
1139  if (expr)
1140  {
1141  octave_value pvalue = expr->evaluate (tw);
1142 
1143 #if DEBUG_TRACE
1144  std::cerr << "property default: "
1145  << attribute_value_to_string (prop_p, pvalue)
1146  << std::endl;
1147 #endif
1148 
1149  prop.put ("DefaultValue", pvalue);
1150  }
1151 
1152  // FIXME: instead of attaching attributes here, pass
1153  // them to cdef_manager::make_property. The
1154  // classdef manager contains a meta object with a
1155  // list of all valid properties that can be used to
1156  // validate the attribute list (see bug #60593).
1157 
1158  // Install property attributes. This is done before
1159  // assigning the property accessors so we can do validation
1160  // by using cdef_property methods.
1161 
1162  for (auto& attrnm_val : amap)
1163  prop.put (attrnm_val.first, attrnm_val.second);
1164 
1165  // Install property access methods, if any. Remove the
1166  // accessor methods from the temporary storage map, so we
1167  // can detect which ones are invalid and do not correspond
1168  // to a defined property.
1169 
1170  auto git = get_methods.find (prop_name);
1171 
1172  if (git != get_methods.end ())
1173  {
1174  make_function_of_class (retval, git->second);
1175  prop.put ("GetMethod", git->second);
1176  get_methods.erase (git);
1177  }
1178 
1179  auto sit = set_methods.find (prop_name);
1180 
1181  if (sit != set_methods.end ())
1182  {
1183  make_function_of_class (retval, sit->second);
1184  prop.put ("SetMethod", sit->second);
1185  set_methods.erase (sit);
1186  }
1187 
1188  retval.install_property (prop);
1189  }
1190  }
1191  }
1192  }
1193 
1194  return retval;
1195 }
1196 
1198 cdef_class::get_method_function (const std::string& /* nm */)
1199 {
1200  return octave_value (new octave_classdef_meta (*this));
1201 }
1202 
1203 OCTAVE_END_NAMESPACE(octave)
cdef_object to_cdef(const octave_value &val)
Definition: cdef-utils.cc:143
std::list< cdef_class > lookup_classes(const Cell &cls_list)
Definition: cdef-utils.cc:113
cdef_class lookup_class(const std::string &name, bool error_if_not_found, bool load_if_not_found)
Definition: cdef-utils.cc:80
std::string get_base_name(const std::string &nm)
Definition: cdef-utils.cc:44
octave_value to_ov(const cdef_object &obj)
Definition: cdef-utils.cc:128
void make_function_of_class(const std::string &class_name, const octave_value &fcn)
Definition: cdef-utils.cc:55
charNDArray max(char d, const charNDArray &m)
Definition: chNDArray.cc:230
Array< octave_idx_type > find(octave_idx_type n=-1, bool backward=false) const
Find indices of (at most n) nonzero elements.
Definition: Array-base.cc:2238
octave_idx_type numel() const
Number of elements in the array.
Definition: Array.h:414
Definition: Cell.h:43
Definition: dMatrix.h:42
elt_type & front()
Definition: base-list.h:79
std::size_t size() const
Definition: base-list.h:52
cdef_method find_method(const std::string &nm, bool local=false)
Definition: cdef-class.h:457
void install_property(const cdef_property &prop)
Definition: cdef-class.h:286
void install_method(const cdef_method &meth)
Definition: cdef-class.h:268
void run_constructor(cdef_object &obj, const octave_value_list &args)
Definition: cdef-class.h:383
std::string get_name() const
Definition: cdef-class.h:318
void file_name(const std::string &nm)
Definition: cdef-class.h:402
void initialize_object(cdef_object &obj)
Definition: cdef-class.h:378
void delete_object(const cdef_object &obj)
Definition: cdef-class.h:322
octave_value get_method_function(const std::string &nm)
Definition: cdef-class.cc:1198
cdef_property find_property(const std::string &nm)
Definition: cdef-class.h:463
Cell get_methods(bool include_ctor=false)
Definition: cdef-class.h:273
static cdef_class make_meta_class(interpreter &interp, tree_classdef *t, bool is_at_folder=false)
Analyze the tree_classdef tree and transform it to a cdef_class.
Definition: cdef-class.cc:872
const cdef_class & meta_method() const
Definition: cdef-manager.h:91
cdef_class make_class(const std::string &name, const std::list< cdef_class > &super_list=std::list< cdef_class >())
cdef_property make_property(const cdef_class &cls, const std::string &name, const octave_value &get_method=Matrix(), const std::string &get_access="public", const octave_value &set_method=Matrix(), const std::string &set_access="public")
const cdef_class & meta_property() const
Definition: cdef-manager.h:90
cdef_package make_package(const std::string &nm, const std::string &parent="")
cdef_package find_package(const std::string &name, bool error_if_not_found=true, bool load_if_not_found=true)
void unregister_class(const cdef_class &cls)
Definition: cdef-manager.h:74
const cdef_class & meta_class() const
Definition: cdef-manager.h:89
cdef_method make_method(const cdef_class &cls, const std::string &name, const octave_value &fcn, const std::string &m_access="public", bool is_static=false)
const cdef_class & meta_package() const
Definition: cdef-manager.h:92
void doc_string(const std::string &txt)
Definition: cdef-object.h:694
bool is_constructor() const
Definition: cdef-method.h:201
octave_value_list execute(const octave_value_list &args, int nargout, bool do_check_access=true, const std::string &who="")
Definition: cdef-method.h:164
bool is_static() const
Definition: cdef-method.h:184
bool is_defined_in_class(const std::string &cname) const
Definition: cdef-method.h:206
octave_value get_function() const
Definition: cdef-method.h:191
std::string get_name() const
Definition: cdef-method.h:182
void mark_for_construction(const cdef_class &cls)
Definition: cdef-object.h:314
void put(const std::string &pname, const octave_value &val)
Definition: cdef-object.h:263
bool ok() const
Definition: cdef-object.h:312
void mark_as_constructed()
Definition: cdef-object.h:331
void set_class(const cdef_class &cls)
Definition: cdef-object.h:232
std::string class_name() const
Definition: cdef-object.h:234
octave_value get(const std::string &pname) const
Definition: cdef-object.h:268
std::string get_name() const
bool is_constant() const
octave_value get_value(const cdef_object &obj, bool do_check_access=true, const std::string &who="") const
tree_evaluator & get_evaluator()
load_path & get_load_path()
Definition: interpreter.h:283
std::list< std::string > methods(const std::string &class_name, const std::string &pack_name="")
Definition: load-path.h:93
std::string name() const
Definition: ov-fcn.h:206
bool is_user_code() const
Definition: ov-usr-fcn.h:80
octave::tree_statement_list * body()
Definition: ov-usr-fcn.h:118
void stash_function_name(const std::string &s)
Definition: ov-usr-fcn.h:301
int ending_line() const
Definition: ov-usr-fcn.h:241
octave::tree_parameter_list * return_list()
Definition: ov-usr-fcn.h:386
bool empty() const
Definition: ovl.h:115
octave_idx_type length() const
Definition: ovl.h:113
octave_value_list & prepend(const octave_value &val)
Definition: ovl.cc:80
bool bool_value(bool warn=false) const
Definition: ov.h:885
octave_user_function * user_function_value(bool silent=false) const
bool is_string() const
Definition: ov.h:637
octave_user_code * user_code_value(bool silent=false) const
bool is_defined() const
Definition: ov.h:592
octave_function * function_value(bool silent=false) const
std::string string_value(bool force=false) const
Definition: ov.h:974
tree_expression * expression()
Definition: pt-classdef.h:152
std::list< tree_classdef_properties_block * > properties_list()
Definition: pt-classdef.h:630
std::list< tree_classdef_methods_block * > methods_list()
Definition: pt-classdef.h:635
tree_identifier * ident()
Definition: pt-classdef.h:711
std::string package_name() const
Definition: pt-classdef.h:721
tree_classdef_body * body()
Definition: pt-classdef.h:716
tree_classdef_superclass_list * superclass_list()
Definition: pt-classdef.h:714
tree_classdef_attribute_list * attribute_list()
Definition: pt-classdef.h:709
std::string doc_string() const
Definition: pt-classdef.h:728
std::string file_name() const
Definition: pt-classdef.h:723
std::string name() const
Definition: pt-decl.h:86
void push_dummy_scope(const std::string &name)
Definition: pt-eval.cc:2694
void pop_scope()
Definition: pt-eval.cc:2702
virtual octave_value evaluate(tree_evaluator &tw, int nargout=1)=0
virtual bool is_identifier() const
Definition: pt-exp.h:66
virtual std::string name() const
Definition: pt-exp.h:102
std::string name() const
Definition: pt-id.h:69
tree_expression * expression()
Definition: pt-idx.h:80
tree_expression * right_hand_side()
Definition: pt-assign.h:142
tree_expression * right_hand_side()
Definition: pt-assign.h:75
void accept(tree_walker &tw)
Definition: pt-stmt.h:191
bool is_expression() const
Definition: pt-stmt.h:76
tree_expression * expression()
Definition: pt-stmt.h:97
std::string method_name() const
Definition: pt-classdef.h:62
std::string class_name() const
Definition: pt-classdef.h:67
virtual void visit_unwind_protect_command(tree_unwind_protect_command &)
Definition: pt-walk.cc:612
virtual void visit_try_catch_command(tree_try_catch_command &)
Definition: pt-walk.cc:593
virtual void visit_if_command_list(tree_if_command_list &)
Definition: pt-walk.cc:345
virtual void visit_multi_assignment(tree_multi_assignment &)
Definition: pt-walk.cc:483
virtual void visit_return_command(tree_return_command &)
Definition: pt-walk.cc:547
virtual void visit_continue_command(tree_continue_command &)
Definition: pt-walk.cc:193
virtual void visit_switch_case_list(tree_switch_case_list &)
Definition: pt-walk.cc:373
virtual void visit_complex_for_command(tree_complex_for_command &)
Definition: pt-walk.cc:259
virtual void visit_break_command(tree_break_command &)
Definition: pt-walk.cc:168
virtual void visit_matrix(tree_matrix &)
Definition: pt-walk.cc:455
virtual void visit_fcn_handle(tree_fcn_handle &)
Definition: pt-walk.cc:509
virtual void visit_colon_expression(tree_colon_expression &)
Definition: pt-walk.cc:174
virtual void visit_do_until_command(tree_do_until_command &)
Definition: pt-walk.cc:640
virtual void visit_no_op_command(tree_no_op_command &)
Definition: pt-walk.cc:497
virtual void visit_binary_expression(tree_binary_expression &)
Definition: pt-walk.cc:142
virtual void visit_simple_for_command(tree_simple_for_command &)
Definition: pt-walk.cc:235
virtual void visit_postfix_expression(tree_postfix_expression &)
Definition: pt-walk.cc:529
virtual void visit_prefix_expression(tree_prefix_expression &)
Definition: pt-walk.cc:538
virtual void visit_anon_fcn_handle(tree_anon_fcn_handle &)
Definition: pt-walk.cc:34
virtual void visit_parameter_list(tree_parameter_list &)
Definition: pt-walk.cc:515
virtual void visit_statement(tree_statement &)
Definition: pt-walk.cc:567
virtual void visit_index_expression(tree_index_expression &)
Definition: pt-walk.cc:401
virtual void visit_decl_command(tree_decl_command &)
Definition: pt-walk.cc:199
virtual void visit_switch_case(tree_switch_case &)
Definition: pt-walk.cc:359
virtual void visit_cell(tree_cell &)
Definition: pt-walk.cc:469
virtual void visit_identifier(tree_identifier &)
Definition: pt-walk.cc:316
virtual void visit_decl_init_list(tree_decl_init_list &)
Definition: pt-walk.cc:222
virtual void visit_if_clause(tree_if_clause &)
Definition: pt-walk.cc:322
virtual void visit_if_command(tree_if_command &)
Definition: pt-walk.cc:336
virtual void visit_argument_list(tree_argument_list &)
Definition: pt-walk.cc:48
virtual void visit_decl_elt(tree_decl_elt &)
Definition: pt-walk.cc:208
virtual void visit_constant(tree_constant &)
Definition: pt-walk.cc:503
virtual void visit_simple_assignment(tree_simple_assignment &)
Definition: pt-walk.cc:553
virtual void visit_superclass_ref(tree_superclass_ref &)
Definition: pt-walk.cc:654
virtual void visit_octave_user_script(octave_user_script &)
Definition: pt-walk.cc:287
virtual void visit_octave_user_function(octave_user_function &)
Definition: pt-walk.cc:296
virtual void visit_function_def(tree_function_def &)
Definition: pt-walk.cc:305
virtual void visit_while_command(tree_while_command &)
Definition: pt-walk.cc:626
virtual void visit_switch_command(tree_switch_command &)
Definition: pt-walk.cc:387
virtual void accept(tree_walker &tw)=0
OCTAVE_BEGIN_NAMESPACE(octave) static octave_value daspk_fcn
void() error(const char *fmt,...)
Definition: error.cc:988
#define panic_impossible()
Definition: error.h:503
cdef_manager & __get_cdef_manager__()
return octave_value(v1.char_array_value() . concat(v2.char_array_value(), ra_idx),((a1.is_sq_string()||a2.is_sq_string()) ? '\'' :'"'))