GNU Octave 10.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
 
Loading...
Searching...
No Matches
cdef-class.cc
Go to the documentation of this file.
1////////////////////////////////////////////////////////////////////////
2//
3// Copyright (C) 2012-2025 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
65static octave_value
66make_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
83cdef_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
92cdef_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
130class ctor_analyzer : public tree_walker
131{
132public:
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&) { }
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
217private:
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
229void
230cdef_class::cdef_class_rep::install_method (const cdef_method& meth)
231{
232 std::string method_name = meth.get_name ();
233 auto it = m_method_map.find (method_name);
234 // Check if a method with the same name already exists before
235 // installing a new one.
236 if (it != m_method_map.end ())
237 {
238 std::string class_file = file_name ();
239 error ("duplicate method '%s' in class '%s' in file '%s'",
240 method_name.c_str (), get_name ().c_str (), class_file.c_str ());
241 }
242
243 // Now safely install the new method
244 m_method_map[method_name] = meth;
245
246 m_member_count++;
247
248 if (meth.is_constructor ())
249 {
250 // Analyze the constructor code to determine what superclass
251 // constructors are called explicitly.
252
253 octave_value ov_fcn = meth.get_function ();
254
255 if (ov_fcn.is_defined ())
256 {
257 octave_user_function *uf = ov_fcn.user_function_value (true);
258
259 if (uf)
260 {
261 tree_parameter_list *ret_list = uf->return_list ();
262 tree_statement_list *body = uf->body ();
263
264 if (! ret_list || ret_list->size () != 1)
265 error ("%s: invalid constructor output arguments",
266 meth.get_name ().c_str ());
267
268 std::string m_obj_name = ret_list->front ()->name ();
269 ctor_analyzer a (meth.get_name (), m_obj_name);
270
271 body->accept (a);
272
273 std::list<cdef_class> explicit_ctor_list
274 = a.get_constructor_list ();
275
276 for (const auto& cdef_cls : explicit_ctor_list)
277 {
278#if DEBUG_TRACE
279 std::cerr << "explicit superclass constructor: "
280 << cdef_cls.get_name () << std::endl;
281#endif
282
283 m_implicit_ctor_list.remove (cdef_cls);
284 }
285 }
286 }
287 }
288}
289
290void
291cdef_class::cdef_class_rep::load_all_methods ()
292{
293 // FIXME: re-scan class directory
294}
295
296Cell
297cdef_class::cdef_class_rep::get_methods (bool include_ctor)
298{
299 std::map<std::string, cdef_method> meths;
300
301 find_methods (meths, false, include_ctor);
302
303 Cell c (meths.size (), 1);
304
305 int idx = 0;
306
307 for (const auto& nm_mthd : meths)
308 c(idx++, 0) = to_ov (nm_mthd.second);
309
310 return c;
311}
312
313std::map<std::string, cdef_method>
314cdef_class::cdef_class_rep::get_method_map (bool only_inherited,
315 bool include_ctor)
316{
317 std::map<std::string, cdef_method> methods;
318
319 find_methods (methods, only_inherited, include_ctor);
320
321 return methods;
322}
323
324void
325cdef_class::cdef_class_rep::find_methods (std::map<std::string,
326 cdef_method>& meths,
327 bool only_inherited,
328 bool include_ctor)
329{
330 load_all_methods ();
331
332 for (const auto& it : m_method_map)
333 {
334 if (include_ctor || ! it.second.is_constructor ())
335 {
336 std::string nm = it.second.get_name ();
337
338 if (meths.find (nm) == meths.end ())
339 {
340 if (only_inherited)
341 {
342 octave_value acc = it.second.get ("Access");
343
344 if (! acc.is_string ()
345 || acc.string_value () == "private")
346 continue;
347 }
348
349 meths[nm] = it.second;
350 }
351 }
352 }
353
354 // Look into superclasses
355
356 Cell super_classes = get ("SuperClasses").cell_value ();
357
358 for (int i = 0; i < super_classes.numel (); i++)
359 {
360 cdef_class cls = lookup_class (super_classes(i));
361
362 cls.get_rep ()->find_methods (meths, true, false);
363 }
364}
365
367cdef_class::cdef_class_rep::find_property (const std::string& nm)
368{
369 auto it = m_property_map.find (nm);
370
371 if (it != m_property_map.end ())
372 {
373 cdef_property& prop = it->second;
374
375 if (prop.ok ())
376 return prop;
377 }
378
379 // Look into superclasses
380
381 Cell super_classes = get ("SuperClasses").cell_value ();
382
383 for (int i = 0; i < super_classes.numel (); i++)
384 {
385 cdef_class cls = lookup_class (super_classes(i));
386
387 cdef_property prop = cls.find_property (nm);
388
389 if (prop.ok ())
390 return prop;
391 }
392
393 return cdef_property ();
394}
395
396void
397cdef_class::cdef_class_rep::install_property (const cdef_property& prop)
398{
399 std::string prop_name = prop.get_name ();
400 auto it = m_property_map.find (prop_name);
401 // Check if a property with the same name already exists before
402 // installing a new one.
403 if (it != m_property_map.end ())
404 {
405 std::string class_file = file_name ();
406 error ("duplicate property '%s' in class '%s' in file '%s'",
407 prop_name.c_str (), get_name ().c_str (), class_file.c_str ());
408 }
409
410 m_property_map[prop_name] = prop;
411
412 // Register the insertion rank of this property
413 m_property_rank_map[prop.get_name ()] = m_member_count;
414
415 m_member_count++;
416}
417
418Cell
419cdef_class::cdef_class_rep::get_properties (int mode)
420{
421 std::map<property_key, cdef_property> props;
422
423 props = get_property_map (mode);
424
425 Cell c (props.size (), 1);
426
427 int idx = 0;
428
429 for (const auto& pname_prop : props)
430 c(idx++, 0) = to_ov (pname_prop.second);
431
432 return c;
433}
434
435std::map<property_key, cdef_property>
436cdef_class::cdef_class_rep::get_property_map (int mode)
437{
438 std::map<property_key, cdef_property> props;
439
440 find_properties (props, mode);
441
442 return props;
443}
444
445void
446cdef_class::cdef_class_rep::find_properties
447 (std::map<property_key, cdef_property>& props, int mode)
448{
449 std::set<std::string> prop_names;
450
451 // The only reason we are introducing 'prop_names' is to keep
452 // track of property names and avoid returning duplicates.
453 // There is no easy way to do it based on 'props', which is going
454 // to use complex keys of type 'property_key'.
455 find_properties_aux (props, prop_names, mode);
456}
457
458void
459cdef_class::cdef_class_rep::find_properties_aux
460 (std::map<property_key, cdef_property>& props,
461 std::set<std::string>& prop_names, int mode)
462{
463 // 'offset' starts at 0 and is incremented whenever we move
464 // in the class ancestry list.
465 static unsigned int property_offset {0};
466
467 for (const auto& it : m_property_map)
468 {
469 std::string nm = it.second.get_name ();
470
471 if (prop_names.find (nm) == prop_names.end ())
472 {
473 if (mode == property_inherited)
474 {
475 octave_value acc = it.second.get ("GetAccess");
476
477 if (! acc.is_string ()
478 || acc.string_value () == "private")
479 continue;
480 }
481
482 const property_key pk =
483 std::make_pair (property_offset + m_property_rank_map[nm], nm);
484 props[pk] = it.second;
485 prop_names.insert (nm);
486 }
487 }
488
489 property_offset += m_member_count;
490
491 // Look into superclasses
492
493 Cell super_classes = get ("SuperClasses").cell_value ();
494
495 for (int i = 0; i < super_classes.numel (); i++)
496 {
497 cdef_class cls = lookup_class (super_classes(i));
498
499 cls.get_rep ()->find_properties_aux (props, prop_names,
500 (mode == property_all
501 ? property_all
502 : property_inherited));
503 }
504
505 property_offset = 0;
506}
507
508void
509cdef_class::cdef_class_rep::find_names (std::set<std::string>& names,
510 bool all)
511{
512 load_all_methods ();
513
514 for (const auto& cls_fnmap : m_method_map)
515 {
516 if (! cls_fnmap.second.is_constructor ())
517 {
518 std::string nm = cls_fnmap.second.get_name ();
519
520 if (! all)
521 {
522 octave_value acc = cls_fnmap.second.get ("Access");
523
524 if (! acc.is_string()
525 || acc.string_value () != "public")
526 continue;
527 }
528
529 names.insert (nm);
530 }
531 }
532
533 for (const auto& pname_prop : m_property_map)
534 {
535 std::string nm = pname_prop.second.get_name ();
536
537 if (! all)
538 {
539 octave_value acc = pname_prop.second.get ("GetAccess");
540
541 if (! acc.is_string()
542 || acc.string_value () != "public")
543 continue;
544 }
545
546 names.insert (nm);
547 }
548
549 // Look into superclasses
550
551 Cell super_classes = get ("SuperClasses").cell_value ();
552
553 for (int i = 0; i < super_classes.numel (); i++)
554 {
555 cdef_class cls = lookup_class (super_classes(i));
556
557 cls.get_rep ()->find_names (names, all);
558 }
559}
560
562cdef_class::cdef_class_rep::get_names ()
563{
564 std::set<std::string> names;
565
566 find_names (names, false);
567
568 string_vector v (names);
569
570 return v.sort (true);
571}
572
573void
574cdef_class::cdef_class_rep::delete_object (const cdef_object& obj)
575{
576 cdef_method dtor = find_method ("delete");
577
578 // FIXME: would it be better to tell find_method above to not find
579 // overloaded functions?
580
581 if (dtor.ok () && dtor.is_defined_in_class (get_name ()))
582 dtor.execute (obj, octave_value_list (), 0, true, "destructor");
583
584 // FIXME: should we destroy corresponding properties here?
585
586 // Call "delete" in super classes
587
588 Cell super_classes = get ("SuperClasses").cell_value ();
589
590 for (int i = 0; i < super_classes.numel (); i++)
591 {
592 cdef_class cls = lookup_class (super_classes(i));
593
594 if (cls.get_name () != "handle")
595 cls.delete_object (obj);
596 }
597}
598
600cdef_class::cdef_class_rep::meta_subsref (const std::string& type,
601 const std::list<octave_value_list>& idx,
602 int nargout)
603{
604 std::size_t skip = 1;
605
606 octave_value_list retval;
607
608 switch (type[0])
609 {
610 case '(':
611 // Constructor call
612
613#if DEBUG_TRACE
614 std::cerr << "constructor" << std::endl;
615#endif
616
617 retval(0) = construct (idx.front ());
618 break;
619
620 case '.':
621 {
622 // Static method, constant (or property?)
623
624#if DEBUG_TRACE
625 std::cerr << "static method/property" << std::endl;
626#endif
627
628 if (idx.front ().length () != 1)
629 error ("invalid meta.class indexing");
630
631 std::string nm = idx.front ()(0).xstring_value ("invalid meta.class indexing, expected a method or property name");
632
633 cdef_method meth = find_method (nm);
634
635 if (meth.ok ())
636 {
637 if (! meth.is_static ())
638 error ("method '%s' is not static", nm.c_str ());
639
641
642 if (type.length () > 1 && idx.size () > 1 && type[1] == '(')
643 {
644 args = *(++(idx.begin ()));
645 skip++;
646 }
647
648 retval = meth.execute (args, (type.length () > skip
649 ? 1 : nargout), true,
650 "meta.class");
651 }
652 else
653 {
654 cdef_property prop = find_property (nm);
655
656 if (! prop.ok ())
657 error ("no such method or property '%s'", nm.c_str ());
658
659 if (! prop.is_constant ())
660 error ("property '%s' is not constant", nm.c_str ());
661
662 retval(0) = prop.get_value (true, "meta.class");
663 }
664 }
665 break;
666
667 default:
668 error ("invalid meta.class indexing");
669 break;
670 }
671
672 if (type.length () > skip && idx.size () > skip && ! retval.empty ())
673 retval = retval(0).next_subsref (nargout, type, idx, skip);
674
675 return retval;
676}
677
678void
679cdef_class::cdef_class_rep::meta_release ()
680{
682
683 cdm.unregister_class (wrap ());
684}
685
686void
687cdef_class::cdef_class_rep::initialize_object (cdef_object& obj)
688{
689 // Populate the object with default property values
690
691 std::list<cdef_class> super_classes
692 = lookup_classes (get ("SuperClasses").cell_value ());
693
694 for (auto& cls : super_classes)
695 cls.initialize_object (obj);
696
697 for (const auto& pname_prop : m_property_map)
698 {
699 if (! pname_prop.second.get ("Dependent").bool_value ())
700 {
701 octave_value pvalue = pname_prop.second.get ("DefaultValue");
702
703 if (pvalue.is_defined ())
704 obj.put (pname_prop.first, pvalue);
705 else
706 obj.put (pname_prop.first, octave_value (Matrix ()));
707 }
708 }
709
710 m_count++;
712}
713
714void
715cdef_class::cdef_class_rep::run_constructor (cdef_object& obj,
716 const octave_value_list& args)
717{
718 octave_value_list empty_args;
719
720 for (const auto& cls : m_implicit_ctor_list)
721 {
722 cdef_class supcls = lookup_class (cls);
723
724 supcls.run_constructor (obj, empty_args);
725 }
726
727 std::string cls_name = get_name ();
728 std::string ctor_name = get_base_name (cls_name);
729
730 cdef_method ctor = find_method (ctor_name);
731
732 if (ctor.ok ())
733 {
734 octave_value_list ctor_args (args);
735 octave_value_list ctor_retval;
736
737 ctor_args.prepend (to_ov (obj));
738 ctor_retval = ctor.execute (ctor_args, 1, true, "constructor");
739
740 if (ctor_retval.length () != 1)
741 error ("%s: invalid number of output arguments for classdef constructor",
742 ctor_name.c_str ());
743
744 obj = to_cdef (ctor_retval(0));
745 }
746
747 obj.mark_as_constructed (wrap ());
748}
749
751cdef_class::cdef_class_rep::get_method (const std::string& name) const
752{
753 auto p = m_method_map.find (name);
754
755 if (p == m_method_map.end ())
756 return octave_value ();
757
758 return p->second.get_function ();
759}
760
762cdef_class::cdef_class_rep::get_method (int line) const
763{
764 octave_value closest_match;
765 int closest_match_end_line = std::numeric_limits<int>::max ();
766 // Since we have a dynamic cast, performance could be an issue if this is
767 // called from a critical path. If performance is an issue, we can cache
768 // an ordered version of the method map
769 for (auto i = m_method_map.cbegin (); i != m_method_map.cend (); ++i)
770 {
771 const octave_value& fcn = i->second.get_function ();
772 octave_user_code *user_code = fcn.user_code_value ();
773
774 if (! user_code)
775 continue;
776
778 = dynamic_cast<octave_user_function *> (user_code);
779
780 if (! pfcn)
781 continue;
782
783 octave::filepos end_pos = pfcn->end_pos ();
784
785 int end_line = end_pos.line ();
786
787 if (line <= end_line && end_line <= closest_match_end_line
788 && pfcn->is_defined () && pfcn->is_user_code ())
789 {
790 closest_match = fcn;
791 closest_match_end_line = end_line;
792 }
793 }
794
795 // repeat the same for set and get methods of properties
796 for (auto i = m_property_map.cbegin (); i != m_property_map.cend (); ++i)
797 {
798 const octave_value& get_meth = i->second.get ("GetMethod");
799 if (get_meth.is_function_handle ())
800 {
802 = get_meth.user_function_value ();
803
804 if (! pfcn)
805 continue;
806
807 octave::filepos end_pos = pfcn->end_pos ();
808
809 int end_line = end_pos.line ();
810
811 if (line <= end_line && end_line <= closest_match_end_line
812 && pfcn->is_defined () && pfcn->is_user_code ())
813 {
814 closest_match = get_meth;
815 closest_match_end_line = end_line;
816 }
817 }
818
819 const octave_value& set_meth = i->second.get ("SetMethod");
820 if (set_meth.is_function_handle ())
821 {
823 = set_meth.user_function_value ();
824
825 if (! pfcn)
826 continue;
827
828 octave::filepos end_pos = pfcn->end_pos ();
829
830 int end_line = end_pos.line ();
831
832 if (line <= end_line && end_line <= closest_match_end_line
833 && pfcn->is_defined () && pfcn->is_user_code ())
834 {
835 closest_match = set_meth;
836 closest_match_end_line = end_line;
837 }
838 }
839 }
840
841 return closest_match;
842}
843
845cdef_class::cdef_class_rep::construct (const octave_value_list& args)
846{
847 cdef_object obj = construct_object (args);
848
849 if (obj.ok ())
850 return to_ov (obj);
851
852 return octave_value ();
853}
854
856cdef_class::cdef_class_rep::construct_object (const octave_value_list& args)
857{
858 if (is_abstract ())
859 error ("cannot instantiate object for abstract class '%s'",
860 get_name ().c_str ());
861
862 cdef_object obj;
863
864 if (is_meta_class ())
865 {
866 // This code path is only used to create empty meta objects
867 // as filler for the empty values within a meta object array.
868
869 cdef_class this_cls = wrap ();
870
871 static cdef_object empty_class;
872
874
875 if (this_cls == cdm.meta_class ())
876 {
877 if (! empty_class.ok ())
878 empty_class = cdm.make_class ("", std::list<cdef_class> ());
879 obj = empty_class;
880 }
881 else if (this_cls == cdm.meta_property ())
882 {
883 static cdef_property empty_property;
884
885 if (! empty_class.ok ())
886 empty_class = cdm.make_class ("", std::list<cdef_class> ());
887 if (! empty_property.ok ())
888 empty_property = cdm.make_property (empty_class, "");
889 obj = empty_property;
890 }
891 else if (this_cls == cdm.meta_method ())
892 {
893 static cdef_method empty_method;
894
895 if (! empty_class.ok ())
896 empty_class = cdm.make_class ("", std::list<cdef_class> ());
897 if (! empty_method.ok ())
898 empty_method = cdm.make_method (empty_class, "", octave_value ());
899 obj = empty_method;
900 }
901 else if (this_cls == cdm.meta_package ())
902 {
903 static cdef_package empty_package;
904
905 if (! empty_package.ok ())
906 empty_package = cdm.make_package ("");
907 obj = empty_package;
908 }
909 else
910 error ("expecting meta class, property, method, or package in cdef_class::cdef_class_rep::construct_object - please report this bug");
911
912 return obj;
913 }
914 else
915 {
916 if (is_handle_class ())
917 obj = cdef_object (new handle_cdef_object ());
918 else
919 obj = cdef_object (new value_cdef_object ());
920 obj.set_class (wrap ());
921
922 initialize_object (obj);
923
924 run_constructor (obj, args);
925
926 return obj;
927 }
928
929 return cdef_object ();
930}
931
932static octave_value
933compute_attribute_value (tree_evaluator& tw,
935{
936 tree_expression *expr = t->expression ();
937
938 if (expr)
939 {
940 if (expr->is_identifier ())
941 {
942 std::string s = expr->name ();
943
944 if (s == "public")
945 return std::string ("public");
946 else if (s == "protected")
947 return std::string ("protected");
948 else if (s == "private")
949 return std::string ("private");
950 }
951
952 return expr->evaluate (tw);
953 }
954 else
955 return octave_value (true);
956}
957
958template <typename T>
959static std::string
960attribute_value_to_string (T *t, octave_value v)
961{
962 if (v.is_string ())
963 return v.string_value ();
964 else if (t->expression ())
965 return t->expression ()->original_text ();
966 else
967 return "true";
968}
969
972 tree_classdef *t, bool is_at_folder)
973{
974 cdef_class retval;
975
976 // Class creation
977
978 std::string class_name = t->ident ()->name ();
979 std::string full_class_name = class_name;
980 if (! t->package_name ().empty ())
981 full_class_name = t->package_name () + '.' + full_class_name;
982
983#if DEBUG_TRACE
984 std::cerr << "class: " << full_class_name << std::endl;
985#endif
986
987 // Push a dummy scope frame on the call stack that corresponds to
988 // the scope that was used when parsing classdef object. Without
989 // this, we may pick up stray values from the current scope when
990 // evaluating expressions found in things like attribute lists.
991
992 tree_evaluator& tw = interp.get_evaluator ();
993
994 tw.push_dummy_scope (full_class_name);
996
997 std::list<cdef_class> slist;
998
999 if (t->superclass_list ())
1000 {
1001 for (auto& scls : (*t->superclass_list ()))
1002 {
1003 std::string sclass_name = (scls)->class_name ();
1004
1005#if DEBUG_TRACE
1006 std::cerr << "superclass: " << sclass_name << std::endl;
1007#endif
1008
1009 cdef_class sclass = lookup_class (sclass_name);
1010
1011 if (sclass.get ("Sealed").bool_value ())
1012 error ("'%s' cannot inherit from '%s', because it is sealed",
1013 full_class_name.c_str (), sclass_name.c_str ());
1014
1015 slist.push_back (sclass);
1016 }
1017 }
1018
1020
1021 retval = cdm.make_class (full_class_name, slist);
1022
1023 retval.doc_string (t->doc_string ());
1024 retval.file_name (t->file_name ());
1025
1026 // Package owning this class
1027
1028 if (! t->package_name ().empty ())
1029 {
1030 cdef_package pack = cdm.find_package (t->package_name ());
1031
1032 if (pack.ok ())
1033 retval.put ("ContainingPackage", to_ov (pack));
1034 }
1035
1036 // FIXME: instead of attaching attributes here, pass them to
1037 // cdef_manager::make_method. The classdef manager contains a meta
1038 // object with a list of all valid properties that can be used to
1039 // validate the attribute list (see bug #60593).
1040
1041 // Class attributes
1042
1043 if (t->attribute_list ())
1044 {
1045 for (const auto& attr : (*t->attribute_list ()))
1046 {
1047 std::string aname = attr->ident ()->name ();
1048 octave_value avalue = compute_attribute_value (tw, attr);
1049
1050#if DEBUG_TRACE
1051 std::cerr << "class attribute: " << aname << " = "
1052 << attribute_value_to_string (attr, avalue) << std::endl;
1053#endif
1054
1055 retval.put (aname, avalue);
1056 }
1057 }
1058
1059 tree_classdef_body *b = t->body ();
1060
1061 if (b)
1062 {
1063 // Keep track of the get/set accessor methods. They will be used
1064 // later on when creating properties.
1065
1066 std::map<std::string, octave_value> get_methods;
1067 std::map<std::string, octave_value> set_methods;
1068
1069 // Method blocks
1070
1071 std::list<tree_classdef_methods_block *> mb_list = b->method_list ();
1072
1073 load_path& lp = interp.get_load_path ();
1074
1075 for (auto& mb_p : mb_list)
1076 {
1077 std::map<std::string, octave_value> amap;
1078
1079#if DEBUG_TRACE
1080 std::cerr << "method block" << std::endl;
1081#endif
1082
1083 // Method attributes
1084
1085 if (mb_p->attribute_list ())
1086 {
1087 for (auto& attr_p : *mb_p->attribute_list ())
1088 {
1089 std::string aname = attr_p->ident ()->name ();
1090 octave_value avalue = compute_attribute_value (tw, attr_p);
1091
1092#if DEBUG_TRACE
1093 std::cerr << "method attribute: " << aname << " = "
1094 << attribute_value_to_string (attr_p, avalue)
1095 << std::endl;
1096#endif
1097
1098 amap[aname] = avalue;
1099 }
1100 }
1101
1102 // Methods
1103
1104 if (mb_p->element_list ())
1105 {
1106 for (auto& mtd : *mb_p->element_list ())
1107 {
1108 std::string mname = mtd.function_value ()->name ();
1109 std::string mprefix = mname.substr (0, 4);
1110
1111 if (mprefix == "get.")
1112 get_methods[mname.substr (4)]
1113 = make_fcn_handle (mtd, mname, full_class_name);
1114 else if (mprefix == "set.")
1115 set_methods[mname.substr (4)]
1116 = make_fcn_handle (mtd, mname, full_class_name);
1117 else
1118 {
1119 cdef_method meth = cdm.make_method (retval, mname, mtd);
1120
1121#if DEBUG_TRACE
1122 std::cerr << (mname == class_name ? "constructor"
1123 : "method")
1124 << ": " << mname << std::endl;
1125#endif
1126
1127 // FIXME: instead of attaching attributes here,
1128 // pass them to cdef_manager::make_method. The
1129 // classdef manager contains a meta object with
1130 // a list of all valid properties that can be
1131 // used to validate the attribute list (see bug
1132 // #60593).
1133
1134 for (auto& attrnm_val : amap)
1135 meth.put (attrnm_val.first, attrnm_val.second);
1136
1137 retval.install_method (meth);
1138 }
1139 }
1140 }
1141 }
1142
1143 if (is_at_folder)
1144 {
1145 // Look for all external methods visible on octave path at the
1146 // time of loading of the class.
1147 //
1148 // FIXME: This is an "extension" to Matlab behavior, which only
1149 // looks in the @-folder containing the original classdef file.
1150 // However, this is easier to implement it that way at the moment.
1151
1152 std::list<std::string> external_methods
1153 = lp.methods (full_class_name);
1154
1155 for (const auto& mtdnm : external_methods)
1156 {
1157 // FIXME: should we issue a warning if the method is already
1158 // defined in the classdef file?
1159
1160 if (mtdnm != class_name
1161 && ! retval.find_method (mtdnm, true).ok ())
1162 {
1163 // Create a dummy method that is used until the actual
1164 // method is loaded.
1166
1167 fcn->stash_function_name (mtdnm);
1168
1169 cdef_method meth
1170 = cdm.make_method (retval, mtdnm, octave_value (fcn));
1171
1172 retval.install_method (meth);
1173 }
1174 }
1175 }
1176
1177 // Property blocks
1178
1179 // FIXME: default property expression should be able to call static
1180 // methods of the class being constructed. A restricted
1181 // CLASSNAME symbol should be added to the scope before
1182 // evaluating default value expressions.
1183
1184 std::list<tree_classdef_properties_block *> pb_list
1185 = b->property_list ();
1186
1187 for (auto& pb_p : pb_list)
1188 {
1189 std::map<std::string, octave_value> amap;
1190
1191#if DEBUG_TRACE
1192 std::cerr << "property block" << std::endl;
1193#endif
1194
1195 // Property attributes
1196
1197 if (pb_p->attribute_list ())
1198 {
1199 for (auto& attr_p : *pb_p->attribute_list ())
1200 {
1201 std::string aname = attr_p->ident ()->name ();
1202 octave_value avalue = compute_attribute_value (tw, attr_p);
1203
1204#if DEBUG_TRACE
1205 std::cerr << "property attribute: " << aname << " = "
1206 << attribute_value_to_string (attr_p, avalue)
1207 << std::endl;
1208#endif
1209
1210 if (aname == "Access")
1211 {
1212 amap["GetAccess"] = avalue;
1213 amap["SetAccess"] = avalue;
1214 }
1215 else
1216 amap[aname] = avalue;
1217 }
1218 }
1219
1220 // Properties
1221
1222 if (pb_p->element_list ())
1223 {
1224 for (auto& prop_p : *pb_p->element_list ())
1225 {
1226 std::string prop_name = prop_p->ident ()->name ();
1227
1228 cdef_property prop = cdm.make_property (retval, prop_name);
1229
1230 prop.doc_string (prop_p->doc_string ());
1231
1232#if DEBUG_TRACE
1233 std::cerr << "property: " << prop_p->ident ()->name ()
1234 << std::endl;
1235#endif
1236
1237 tree_expression *expr = prop_p->expression ();
1238 if (expr)
1239 {
1240 octave_value pvalue = expr->evaluate (tw);
1241
1242#if DEBUG_TRACE
1243 std::cerr << "property default: "
1244 << attribute_value_to_string (prop_p, pvalue)
1245 << std::endl;
1246#endif
1247
1248 prop.put ("DefaultValue", pvalue);
1249 }
1250
1251 // FIXME: instead of attaching attributes here, pass
1252 // them to cdef_manager::make_property. The
1253 // classdef manager contains a meta object with a
1254 // list of all valid properties that can be used to
1255 // validate the attribute list (see bug #60593).
1256
1257 // Install property attributes. This is done before
1258 // assigning the property accessors so we can do validation
1259 // by using cdef_property methods.
1260
1261 for (auto& attrnm_val : amap)
1262 prop.put (attrnm_val.first, attrnm_val.second);
1263
1264 // Install property access methods, if any. Remove the
1265 // accessor methods from the temporary storage map, so we
1266 // can detect which ones are invalid and do not correspond
1267 // to a defined property.
1268
1269 auto git = get_methods.find (prop_name);
1270
1271 if (git != get_methods.end ())
1272 {
1273 make_function_of_class (retval, git->second);
1274 prop.put ("GetMethod", git->second);
1275 get_methods.erase (git);
1276 }
1277
1278 auto sit = set_methods.find (prop_name);
1279
1280 if (sit != set_methods.end ())
1281 {
1282 make_function_of_class (retval, sit->second);
1283 prop.put ("SetMethod", sit->second);
1284 set_methods.erase (sit);
1285 }
1286
1287 retval.install_property (prop);
1288 }
1289 }
1290 }
1291 }
1292
1293 return retval;
1294}
1295
1297cdef_class::get_method_function (const std::string& /* nm */)
1298{
1299 return octave_value (new octave_classdef_meta (*this));
1300}
1301
1302OCTAVE_END_NAMESPACE(octave)
std::pair< unsigned int, std::string > property_key
Definition cdef-class.h:56
cdef_object to_cdef(const octave_value &val)
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)
void make_function_of_class(const std::string &class_name, const octave_value &fcn)
Definition cdef-utils.cc:55
std::list< cdef_class > lookup_classes(const Cell &cls_list)
Array< octave_idx_type > find(octave_idx_type n=-1, bool backward=false) const
Find indices of (at most n) nonzero elements.
octave_idx_type numel() const
Number of elements in the array.
Definition Array.h:418
Definition Cell.h:41
cdef_method find_method(const std::string &nm, bool local=false)
Definition cdef-class.h:468
void install_property(const cdef_property &prop)
Definition cdef-class.h:297
void install_method(const cdef_method &meth)
Definition cdef-class.h:279
void run_constructor(cdef_object &obj, const octave_value_list &args)
Definition cdef-class.h:394
std::string get_name() const
Definition cdef-class.h:329
void file_name(const std::string &nm)
Definition cdef-class.h:413
void delete_object(const cdef_object &obj)
Definition cdef-class.h:333
octave_value get_method_function(const std::string &nm)
cdef_property find_property(const std::string &nm)
Definition cdef-class.h:474
Cell get_methods(bool include_ctor=false)
Definition cdef-class.h:284
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.
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_package() const
cdef_package make_package(const std::string &nm, const std::string &parent="")
const cdef_class & meta_property() const
cdef_package find_package(const std::string &name, bool error_if_not_found=true, bool load_if_not_found=true)
const cdef_class & meta_method() const
void unregister_class(const cdef_class &cls)
const cdef_class & meta_class() const
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)
void doc_string(const std::string &txt)
bool is_constructor() const
octave_value_list execute(const octave_value_list &args, int nargout, bool do_check_access=true, const std::string &who="")
bool is_static() const
bool is_defined_in_class(const std::string &cname) const
octave_value get_function() const
std::string get_name() const
void mark_for_construction(const cdef_class &cls)
void put(const std::string &pname, const octave_value &val)
bool ok() const
void mark_as_constructed()
void set_class(const cdef_class &cls)
std::string class_name() const
octave_value get(const std::string &pname) const
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
load_path & get_load_path()
tree_evaluator & get_evaluator()
std::list< std::string > methods(const std::string &class_name, const std::string &pack_name="")
Definition load-path.h:91
bool is_defined() const
Definition ov-fcn.h:68
std::string name() const
Definition ov-fcn.h:208
octave::filepos end_pos() const
Definition ov-usr-fcn.cc:97
bool is_user_code() const
Definition ov-usr-fcn.h:83
octave::tree_statement_list * body()
Definition ov-usr-fcn.h:125
void stash_function_name(const std::string &s)
Definition ov-usr-fcn.h:304
octave::tree_parameter_list * return_list()
Definition ov-usr-fcn.h:389
bool empty() const
Definition ovl.h:113
octave_idx_type length() const
Definition ovl.h:111
octave_value_list & prepend(const octave_value &val)
Definition ovl.cc:80
bool is_function_handle() const
Definition ov.h:768
bool bool_value(bool warn=false) const
Definition ov.h:891
octave_function * function_value(bool silent=false) const
bool is_string() const
Definition ov.h:637
bool is_defined() const
Definition ov.h:592
std::string string_value(bool force=false) const
Definition ov.h:983
octave_user_function * user_function_value(bool silent=false) const
octave_user_code * user_code_value(bool silent=false) const
tree_expression * expression()
std::list< tree_classdef_properties_block * > property_list()
std::list< tree_classdef_methods_block * > method_list()
std::string package_name() const
tree_classdef_body * body()
tree_identifier * ident()
tree_classdef_superclass_list * superclass_list()
tree_classdef_attribute_list * attribute_list()
std::string doc_string() const
std::string file_name() const
void push_dummy_scope(const std::string &name)
Definition pt-eval.cc:2746
void pop_scope()
Definition pt-eval.cc:2754
virtual octave_value evaluate(tree_evaluator &tw, int nargout=1)=0
virtual bool is_identifier() const
Definition pt-exp.h:68
virtual std::string name() const
Definition pt-exp.h:111
std::string name() const
Definition pt-id.h:71
tree_expression * expression()
Definition pt-idx.h:81
tree_expression * right_hand_side()
Definition pt-assign.h:143
tree_expression * right_hand_side()
Definition pt-assign.h:75
void accept(tree_walker &tw)
Definition pt-stmt.h:213
tree_expression * expression()
Definition pt-stmt.h:104
bool is_expression() const
Definition pt-stmt.h:78
std::string method_name() const
Definition pt-classdef.h:63
std::string class_name() const
Definition pt-classdef.h:68
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:1003
cdef_manager & __get_cdef_manager__()