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