1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 2012-2021 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 
63 namespace octave
64 {
65   static octave_value
make_fcn_handle(const octave_value & fcn,const std::string & meth_name,const std::string & class_name)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
74         octave_fcn_handle *fh
75           = new octave_fcn_handle (fcn, class_name, meth_name);
76 
77         retval = octave_value (fh);
78       }
79 
80     return retval;
81   }
82 
cdef_class_rep(const std::list<cdef_class> & superclasses)83   cdef_class::cdef_class_rep::cdef_class_rep (const std::list<cdef_class>& superclasses)
84     : cdef_meta_object_rep (), member_count (0), handle_class (false),
85       meta (false)
86   {
87     put ("SuperClasses", to_ov (superclasses));
88     implicit_ctor_list = superclasses;
89   }
90 
91   cdef_method
find_method(const std::string & nm,bool local)92   cdef_class::cdef_class_rep::find_method (const std::string& nm, bool local)
93   {
94     auto it = method_map.find (nm);
95 
96     if (it == 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 (void) = delete;
135 
ctor_analyzer(const std::string & ctor,const std::string & obj)136     ctor_analyzer (const std::string& ctor, const std::string& obj)
137       : tree_walker (), who (ctor), obj_name (obj) { }
138 
139     ctor_analyzer (const ctor_analyzer&) = delete;
140 
141     ctor_analyzer& operator = (const ctor_analyzer&) = delete;
142 
143     ~ctor_analyzer (void) = default;
144 
visit_statement(tree_statement & t)145     void visit_statement (tree_statement& t)
146     {
147       if (t.is_expression ())
148         t.expression ()->accept (*this);
149     }
150 
visit_simple_assignment(tree_simple_assignment & t)151     void visit_simple_assignment (tree_simple_assignment& t)
152     {
153       t.right_hand_side ()->accept (*this);
154     }
155 
visit_multi_assignment(tree_multi_assignment & t)156     void visit_multi_assignment (tree_multi_assignment& t)
157     {
158       t.right_hand_side ()->accept (*this);
159     }
160 
visit_index_expression(tree_index_expression & t)161     void visit_index_expression (tree_index_expression& t)
162     {
163       t.expression ()->accept (*this);
164     }
165 
get_constructor_list(void) const166     std::list<cdef_class> get_constructor_list (void) const
167     { return ctor_list; }
168 
169     // NO-OP
170 
visit_anon_fcn_handle(tree_anon_fcn_handle &)171     void visit_anon_fcn_handle (tree_anon_fcn_handle&) { }
visit_argument_list(tree_argument_list &)172     void visit_argument_list (tree_argument_list&) { }
visit_binary_expression(tree_binary_expression &)173     void visit_binary_expression (tree_binary_expression&) { }
visit_break_command(tree_break_command &)174     void visit_break_command (tree_break_command&) { }
visit_colon_expression(tree_colon_expression &)175     void visit_colon_expression (tree_colon_expression&) { }
visit_continue_command(tree_continue_command &)176     void visit_continue_command (tree_continue_command&) { }
visit_decl_command(tree_decl_command &)177     void visit_decl_command (tree_decl_command&) { }
visit_decl_init_list(tree_decl_init_list &)178     void visit_decl_init_list (tree_decl_init_list&) { }
visit_decl_elt(tree_decl_elt &)179     void visit_decl_elt (tree_decl_elt&) { }
visit_simple_for_command(tree_simple_for_command &)180     void visit_simple_for_command (tree_simple_for_command&) { }
visit_complex_for_command(tree_complex_for_command &)181     void visit_complex_for_command (tree_complex_for_command&) { }
visit_octave_user_script(octave_user_script &)182     void visit_octave_user_script (octave_user_script&) { }
visit_octave_user_function(octave_user_function &)183     void visit_octave_user_function (octave_user_function&) { }
visit_function_def(tree_function_def &)184     void visit_function_def (tree_function_def&) { }
visit_identifier(tree_identifier &)185     void visit_identifier (tree_identifier&) { }
visit_if_clause(tree_if_clause &)186     void visit_if_clause (tree_if_clause&) { }
visit_if_command(tree_if_command &)187     void visit_if_command (tree_if_command&) { }
visit_if_command_list(tree_if_command_list &)188     void visit_if_command_list (tree_if_command_list&) { }
visit_switch_case(tree_switch_case &)189     void visit_switch_case (tree_switch_case&) { }
visit_switch_case_list(tree_switch_case_list &)190     void visit_switch_case_list (tree_switch_case_list&) { }
visit_switch_command(tree_switch_command &)191     void visit_switch_command (tree_switch_command&) { }
visit_matrix(tree_matrix &)192     void visit_matrix (tree_matrix&) { }
visit_cell(tree_cell &)193     void visit_cell (tree_cell&) { }
visit_no_op_command(tree_no_op_command &)194     void visit_no_op_command (tree_no_op_command&) { }
visit_constant(tree_constant &)195     void visit_constant (tree_constant&) { }
visit_fcn_handle(tree_fcn_handle &)196     void visit_fcn_handle (tree_fcn_handle&) { }
visit_parameter_list(tree_parameter_list &)197     void visit_parameter_list (tree_parameter_list&) { }
visit_postfix_expression(tree_postfix_expression &)198     void visit_postfix_expression (tree_postfix_expression&) { }
visit_prefix_expression(tree_prefix_expression &)199     void visit_prefix_expression (tree_prefix_expression&) { }
visit_return_command(tree_return_command &)200     void visit_return_command (tree_return_command&) { }
visit_try_catch_command(tree_try_catch_command &)201     void visit_try_catch_command (tree_try_catch_command&) { }
visit_unwind_protect_command(tree_unwind_protect_command &)202     void visit_unwind_protect_command (tree_unwind_protect_command&) { }
visit_while_command(tree_while_command &)203     void visit_while_command (tree_while_command&) { }
visit_do_until_command(tree_do_until_command &)204     void visit_do_until_command (tree_do_until_command&) { }
205 
visit_superclass_ref(tree_superclass_ref & t)206     void visit_superclass_ref (tree_superclass_ref& t)
207     {
208       if (t.method_name () == obj_name)
209         {
210           std::string class_name = t.class_name ();
211 
212           cdef_class cls = lookup_class (class_name, false);
213 
214           if (cls.ok ())
215             ctor_list.push_back (cls);
216         }
217     }
218 
219   private:
220 
221     // The name of the constructor being analyzed.
222     std::string who;
223 
224     // The name of the first output argument of the constructor.
225     std::string obj_name;
226 
227     // The list of superclass constructors that are explicitly called.
228     std::list<cdef_class> ctor_list;
229   };
230 
231   void
install_method(const cdef_method & meth)232   cdef_class::cdef_class_rep::install_method (const cdef_method& meth)
233   {
234     method_map[meth.get_name ()] = meth;
235 
236     member_count++;
237 
238     if (meth.is_constructor ())
239       {
240         // Analyze the constructor code to determine what superclass
241         // constructors are called explicitly.
242 
243         octave_value ov_fcn = meth.get_function ();
244 
245         if (ov_fcn.is_defined ())
246           {
247             octave_user_function *uf = ov_fcn.user_function_value (true);
248 
249             if (uf)
250               {
251                 tree_parameter_list *ret_list = uf->return_list ();
252                 tree_statement_list *body = uf->body ();
253 
254                 if (! ret_list || ret_list->size () != 1)
255                   error ("%s: invalid constructor output arguments",
256                          meth.get_name ().c_str ());
257 
258                 std::string obj_name = ret_list->front ()->name ();
259                 ctor_analyzer a (meth.get_name (), obj_name);
260 
261                 body->accept (a);
262 
263                 std::list<cdef_class> explicit_ctor_list
264                   = a.get_constructor_list ();
265 
266                 for (const auto& cdef_cls : explicit_ctor_list)
267                   {
268 #if DEBUG_TRACE
269                     std::cerr << "explicit superclass constructor: "
270                               << cdef_cls.get_name () << std::endl;
271 #endif
272 
273                     implicit_ctor_list.remove (cdef_cls);
274                   }
275               }
276           }
277       }
278   }
279 
280   void
load_all_methods(void)281   cdef_class::cdef_class_rep::load_all_methods (void)
282   {
283     // FIXME: re-scan class directory
284   }
285 
286   Cell
get_methods(bool include_ctor)287   cdef_class::cdef_class_rep::get_methods (bool include_ctor)
288   {
289     std::map<std::string,cdef_method> meths;
290 
291     find_methods (meths, false, include_ctor);
292 
293     Cell c (meths.size (), 1);
294 
295     int idx = 0;
296 
297     for (const auto& nm_mthd : meths)
298       c(idx++, 0) = to_ov (nm_mthd.second);
299 
300     return c;
301   }
302 
303   std::map<std::string, cdef_method>
get_method_map(bool only_inherited,bool include_ctor)304   cdef_class::cdef_class_rep::get_method_map (bool only_inherited,
305                                               bool include_ctor)
306   {
307     std::map<std::string, cdef_method> methods;
308 
309     find_methods (methods, only_inherited, include_ctor);
310 
311     return methods;
312   }
313 
314   void
find_methods(std::map<std::string,cdef_method> & meths,bool only_inherited,bool include_ctor)315   cdef_class::cdef_class_rep::find_methods (std::map<std::string, cdef_method>& meths,
316                                             bool only_inherited,
317                                             bool include_ctor)
318   {
319     load_all_methods ();
320 
321     method_const_iterator it;
322 
323     for (it = method_map.begin (); it != method_map.end (); ++it)
324       {
325         if (include_ctor || ! it->second.is_constructor ())
326           {
327             std::string nm = it->second.get_name ();
328 
329             if (meths.find (nm) == meths.end ())
330               {
331                 if (only_inherited)
332                   {
333                     octave_value acc = it->second.get ("Access");
334 
335                     if (! acc.is_string ()
336                         || acc.string_value () == "private")
337                       continue;
338                   }
339 
340                 meths[nm] = it->second;
341               }
342           }
343       }
344 
345     // Look into superclasses
346 
347     Cell super_classes = get ("SuperClasses").cell_value ();
348 
349     for (int i = 0; i < super_classes.numel (); i++)
350       {
351         cdef_class cls = lookup_class (super_classes(i));
352 
353         cls.get_rep ()->find_methods (meths, true, false);
354       }
355   }
356 
357   cdef_property
find_property(const std::string & nm)358   cdef_class::cdef_class_rep::find_property (const std::string& nm)
359   {
360     auto it = property_map.find (nm);
361 
362     if (it != property_map.end ())
363       {
364         cdef_property& prop = it->second;
365 
366         if (prop.ok ())
367           return prop;
368       }
369 
370     // Look into superclasses
371 
372     Cell super_classes = get ("SuperClasses").cell_value ();
373 
374     for (int i = 0; i < super_classes.numel (); i++)
375       {
376         cdef_class cls = lookup_class (super_classes(i));
377 
378         cdef_property prop = cls.find_property (nm);
379 
380         if (prop.ok ())
381           return prop;
382       }
383 
384     return cdef_property ();
385   }
386 
387   void
install_property(const cdef_property & prop)388   cdef_class::cdef_class_rep::install_property (const cdef_property& prop)
389   {
390     property_map[prop.get_name ()] = prop;
391 
392     member_count++;
393   }
394 
395   Cell
get_properties(int mode)396   cdef_class::cdef_class_rep::get_properties (int mode)
397   {
398     std::map<std::string,cdef_property> props;
399 
400     props = get_property_map (mode);
401 
402     Cell c (props.size (), 1);
403 
404     int idx = 0;
405 
406     for (const auto& pname_prop : props)
407       c(idx++, 0) = to_ov (pname_prop.second);
408 
409     return c;
410   }
411 
412   std::map<std::string, cdef_property>
get_property_map(int mode)413   cdef_class::cdef_class_rep::get_property_map (int mode)
414   {
415     std::map<std::string,cdef_property> props;
416 
417     find_properties (props, mode);
418 
419     return props;
420   }
421 
422   void
find_properties(std::map<std::string,cdef_property> & props,int mode)423   cdef_class::cdef_class_rep::find_properties (std::map<std::string,
424                                                cdef_property>& props,
425                                                int mode)
426   {
427     property_const_iterator it;
428 
429     for (it = property_map.begin (); it != property_map.end (); ++it)
430       {
431         std::string nm = it->second.get_name ();
432 
433         if (props.find (nm) == props.end ())
434           {
435             if (mode == property_inherited)
436               {
437                 octave_value acc = it->second.get ("GetAccess");
438 
439                 if (! acc.is_string ()
440                     || acc.string_value () == "private")
441                   continue;
442               }
443 
444             props[nm] = it->second;
445           }
446       }
447 
448     // Look into superclasses
449 
450     Cell super_classes = get ("SuperClasses").cell_value ();
451 
452     for (int i = 0; i < super_classes.numel (); i++)
453       {
454         cdef_class cls = lookup_class (super_classes(i));
455 
456         cls.get_rep ()->find_properties (props,
457                                          (mode == property_all
458                                           ? property_all
459                                           : property_inherited));
460       }
461   }
462 
463   void
find_names(std::set<std::string> & names,bool all)464   cdef_class::cdef_class_rep::find_names (std::set<std::string>& names,
465                                           bool all)
466   {
467     load_all_methods ();
468 
469     for (const auto& cls_fnmap : method_map)
470       {
471         if (! cls_fnmap.second.is_constructor ())
472           {
473             std::string nm = cls_fnmap.second.get_name ();
474 
475             if (! all)
476               {
477                 octave_value acc = cls_fnmap.second.get ("Access");
478 
479                 if (! acc.is_string()
480                     || acc.string_value () != "public")
481                   continue;
482               }
483 
484             names.insert (nm);
485           }
486       }
487 
488     for (const auto& pname_prop : property_map)
489       {
490         std::string nm = pname_prop.second.get_name ();
491 
492         if (! all)
493           {
494             octave_value acc = pname_prop.second.get ("GetAccess");
495 
496             if (! acc.is_string()
497                 || acc.string_value () != "public")
498               continue;
499           }
500 
501         names.insert (nm);
502       }
503 
504     // Look into superclasses
505 
506     Cell super_classes = get ("SuperClasses").cell_value ();
507 
508     for (int i = 0; i < super_classes.numel (); i++)
509       {
510         cdef_class cls = lookup_class (super_classes(i));
511 
512         cls.get_rep ()->find_names (names, all);
513       }
514   }
515 
516   string_vector
get_names(void)517   cdef_class::cdef_class_rep::get_names (void)
518   {
519     std::set<std::string> names;
520 
521     find_names (names, false);
522 
523     string_vector v (names);
524 
525     return v.sort (true);
526   }
527 
528   void
delete_object(const cdef_object & obj)529   cdef_class::cdef_class_rep::delete_object (const cdef_object& obj)
530   {
531     cdef_method dtor = find_method ("delete");
532 
533     // FIXME: would it be better to tell find_method above to not find
534     // overloaded functions?
535 
536     if (dtor.ok () && dtor.is_defined_in_class (get_name ()))
537       dtor.execute (obj, octave_value_list (), 0, true, "destructor");
538 
539     // FIXME: should we destroy corresponding properties here?
540 
541     // Call "delete" in super classes
542 
543     Cell super_classes = get ("SuperClasses").cell_value ();
544 
545     for (int i = 0; i < super_classes.numel (); i++)
546       {
547         cdef_class cls = lookup_class (super_classes(i));
548 
549         if (cls.get_name () != "handle")
550           cls.delete_object (obj);
551       }
552   }
553 
554   octave_value_list
meta_subsref(const std::string & type,const std::list<octave_value_list> & idx,int nargout)555   cdef_class::cdef_class_rep::meta_subsref (const std::string& type,
556                                             const std::list<octave_value_list>& idx,
557                                             int nargout)
558   {
559     std::size_t skip = 1;
560 
561     octave_value_list retval;
562 
563     switch (type[0])
564       {
565       case '(':
566         // Constructor call
567 
568 #if DEBUG_TRACE
569         std::cerr << "constructor" << std::endl;
570 #endif
571 
572         retval(0) = construct (idx.front ());
573         break;
574 
575       case '.':
576         {
577           // Static method, constant (or property?)
578 
579 #if DEBUG_TRACE
580           std::cerr << "static method/property" << std::endl;
581 #endif
582 
583           if (idx.front ().length () != 1)
584             error ("invalid meta.class indexing");
585 
586           std::string nm = idx.front ()(0).xstring_value ("invalid meta.class indexing, expected a method or property name");
587 
588           cdef_method meth = find_method (nm);
589 
590           if (meth.ok ())
591             {
592               if (! meth.is_static ())
593                 error ("method '%s' is not static", nm.c_str ());
594 
595               octave_value_list args;
596 
597               if (type.length () > 1 && idx.size () > 1 && type[1] == '(')
598                 {
599                   args = *(++(idx.begin ()));
600                   skip++;
601                 }
602 
603               retval = meth.execute (args, (type.length () > skip
604                                             ? 1 : nargout), true,
605                                      "meta.class");
606             }
607           else
608             {
609               cdef_property prop = find_property (nm);
610 
611               if (! prop.ok ())
612                 error ("no such method or property '%s'", nm.c_str ());
613 
614               if (! prop.is_constant ())
615                 error ("property '%s' is not constant", nm.c_str ());
616 
617               retval(0) = prop.get_value (true, "meta.class");
618             }
619         }
620         break;
621 
622       default:
623         error ("invalid meta.class indexing");
624         break;
625       }
626 
627     if (type.length () > skip && idx.size () > skip && ! retval.empty ())
628       retval = retval(0).next_subsref (nargout, type, idx, skip);
629 
630     return retval;
631   }
632 
633   void
meta_release(void)634   cdef_class::cdef_class_rep::meta_release (void)
635   {
636     cdef_manager& cdm
637       = __get_cdef_manager__ ("cdef_class::cdef_class_rep::meta_release");
638 
639     cdm.unregister_class (wrap ());
640   }
641 
642   void
initialize_object(cdef_object & obj)643   cdef_class::cdef_class_rep::initialize_object (cdef_object& obj)
644   {
645     // Populate the object with default property values
646 
647     std::list<cdef_class> super_classes
648       = lookup_classes (get ("SuperClasses").cell_value ());
649 
650     for (auto& cls : super_classes)
651       cls.initialize_object (obj);
652 
653     for (const auto& pname_prop : property_map)
654       {
655         if (! pname_prop.second.get ("Dependent").bool_value ())
656           {
657             octave_value pvalue = pname_prop.second.get ("DefaultValue");
658 
659             if (pvalue.is_defined ())
660               obj.put (pname_prop.first, pvalue);
661             else
662               obj.put (pname_prop.first, octave_value (Matrix ()));
663           }
664       }
665 
666     m_count++;
667     obj.mark_for_construction (cdef_class (this));
668   }
669 
670   void
run_constructor(cdef_object & obj,const octave_value_list & args)671   cdef_class::cdef_class_rep::run_constructor (cdef_object& obj,
672                                                const octave_value_list& args)
673   {
674     octave_value_list empty_args;
675 
676     for (const auto& cls : implicit_ctor_list)
677       {
678         cdef_class supcls = lookup_class (cls);
679 
680         supcls.run_constructor (obj, empty_args);
681       }
682 
683     std::string cls_name = get_name ();
684     std::string ctor_name = get_base_name (cls_name);
685 
686     cdef_method ctor = find_method (ctor_name);
687 
688     if (ctor.ok ())
689       {
690         octave_value_list ctor_args (args);
691         octave_value_list ctor_retval;
692 
693         ctor_args.prepend (to_ov (obj));
694         ctor_retval = ctor.execute (ctor_args, 1, true, "constructor");
695 
696         if (ctor_retval.length () != 1)
697           error ("%s: invalid number of output arguments for classdef constructor",
698                  ctor_name.c_str ());
699 
700         obj = to_cdef (ctor_retval(0));
701       }
702 
703     obj.mark_as_constructed (wrap ());
704   }
705 
706   octave_value
get_method(const std::string & name) const707   cdef_class::cdef_class_rep::get_method (const std::string& name) const
708   {
709     auto p = method_map.find (name);
710 
711     if (p == method_map.end ())
712       return octave_value ();
713 
714     return p->second.get_function ();
715   }
716 
717 
718   octave_value
construct(const octave_value_list & args)719   cdef_class::cdef_class_rep::construct (const octave_value_list& args)
720   {
721     cdef_object obj = construct_object (args);
722 
723     if (obj.ok ())
724       return to_ov (obj);
725 
726     return octave_value ();
727   }
728 
729   cdef_object
construct_object(const octave_value_list & args)730   cdef_class::cdef_class_rep::construct_object (const octave_value_list& args)
731   {
732     if (is_abstract ())
733       error ("cannot instantiate object for abstract class '%s'",
734              get_name ().c_str ());
735 
736     cdef_object obj;
737 
738     if (is_meta_class ())
739       {
740         // This code path is only used to create empty meta objects
741         // as filler for the empty values within a meta object array.
742 
743         cdef_class this_cls = wrap ();
744 
745         static cdef_object empty_class;
746 
747         cdef_manager& cdm
748           = __get_cdef_manager__ ("cdef_class::cdef_class_rep::construct_object");
749 
750         if (this_cls == cdm.meta_class ())
751           {
752             if (! empty_class.ok ())
753               empty_class = cdm.make_class ("", std::list<cdef_class> ());
754             obj = empty_class;
755           }
756         else if (this_cls == cdm.meta_property ())
757           {
758             static cdef_property empty_property;
759 
760             if (! empty_class.ok ())
761               empty_class = cdm.make_class ("", std::list<cdef_class> ());
762             if (! empty_property.ok ())
763               empty_property = cdm.make_property (empty_class, "");
764             obj = empty_property;
765           }
766         else if (this_cls == cdm.meta_method ())
767           {
768             static cdef_method empty_method;
769 
770             if (! empty_class.ok ())
771               empty_class = cdm.make_class ("", std::list<cdef_class> ());
772             if (! empty_method.ok ())
773               empty_method = cdm.make_method (empty_class, "", octave_value ());
774             obj = empty_method;
775           }
776         else if (this_cls == cdm.meta_package ())
777           {
778             static cdef_package empty_package;
779 
780             if (! empty_package.ok ())
781               empty_package = cdm.make_package ("");
782             obj = empty_package;
783           }
784         else
785           panic_impossible ();
786 
787         return obj;
788       }
789     else
790       {
791         if (is_handle_class ())
792           obj = cdef_object (new handle_cdef_object ());
793         else
794           obj = cdef_object (new value_cdef_object ());
795         obj.set_class (wrap ());
796 
797         initialize_object (obj);
798 
799         run_constructor (obj, args);
800 
801         return obj;
802       }
803 
804     return cdef_object ();
805   }
806 
807   static octave_value
compute_attribute_value(tree_evaluator & tw,tree_classdef_attribute * t)808   compute_attribute_value (tree_evaluator& tw,
809                            tree_classdef_attribute *t)
810   {
811     tree_expression *expr = t->expression ();
812 
813     if (expr)
814       {
815         if (expr->is_identifier ())
816           {
817             std::string s = expr->name ();
818 
819             if (s == "public")
820               return std::string ("public");
821             else if (s == "protected")
822               return std::string ("protected");
823             else if (s == "private")
824               return std::string ("private");
825           }
826 
827         return expr->evaluate (tw);
828       }
829     else
830       return octave_value (true);
831   }
832 
833   template <typename T>
834   static std::string
attribute_value_to_string(T * t,octave_value v)835   attribute_value_to_string (T *t, octave_value v)
836   {
837     if (v.is_string ())
838       return v.string_value ();
839     else if (t->expression ())
840       return t->expression ()->original_text ();
841     else
842       return "true";
843   }
844 
845   cdef_class
make_meta_class(interpreter & interp,tree_classdef * t,bool is_at_folder)846   cdef_class::make_meta_class (interpreter& interp,
847                                tree_classdef *t, bool is_at_folder)
848   {
849     cdef_class retval;
850     std::string class_name, full_class_name;
851 
852     // Class creation
853 
854     class_name = full_class_name = t->ident ()->name ();
855     if (! t->package_name ().empty ())
856       full_class_name = t->package_name () + '.' + full_class_name;
857 
858 #if DEBUG_TRACE
859     std::cerr << "class: " << full_class_name << std::endl;
860 #endif
861 
862     // Push a dummy scope frame on the call stack that corresponds to
863     // the scope that was used when parsing classdef object.  Without
864     // this, we may pick up stray values from the current scope when
865     // evaluating expressions found in things like attribute lists.
866 
867     unwind_protect frame;
868 
869     tree_evaluator& tw = interp.get_evaluator ();
870 
871     tw.push_dummy_scope (full_class_name);
872     frame.add_method (tw, &octave::tree_evaluator::pop_scope);
873 
874     std::list<cdef_class> slist;
875 
876     if (t->superclass_list ())
877       {
878         for (auto& scls : (*t->superclass_list ()))
879           {
880             std::string sclass_name = (scls)->class_name ();
881 
882 #if DEBUG_TRACE
883             std::cerr << "superclass: " << sclass_name << std::endl;
884 #endif
885 
886             cdef_class sclass = lookup_class (sclass_name);
887 
888             if (sclass.get ("Sealed").bool_value ())
889               error ("'%s' cannot inherit from '%s', because it is sealed",
890                      full_class_name.c_str (), sclass_name.c_str ());
891 
892             slist.push_back (sclass);
893           }
894       }
895 
896     cdef_manager& cdm = __get_cdef_manager__ ("cdef_class::make_meta_class");
897 
898     retval = cdm.make_class (full_class_name, slist);
899 
900     retval.doc_string (t->doc_string ());
901 
902     // Package owning this class
903 
904     if (! t->package_name ().empty ())
905       {
906         cdef_package pack = cdm.find_package (t->package_name ());
907 
908         if (pack.ok ())
909           retval.put ("ContainingPackage", to_ov (pack));
910       }
911 
912     // Class attributes
913 
914     if (t->attribute_list ())
915       {
916         for (const auto& attr : (*t->attribute_list ()))
917           {
918             std::string aname = attr->ident ()->name ();
919             octave_value avalue = compute_attribute_value (tw, attr);
920 
921 #if DEBUG_TRACE
922             std::cerr << "class attribute: " << aname << " = "
923                       << attribute_value_to_string (attr, avalue) << std::endl;
924 #endif
925 
926             retval.put (aname, avalue);
927           }
928       }
929 
930     tree_classdef_body *b = t->body ();
931 
932     if (b)
933       {
934         // Keep track of the get/set accessor methods.  They will be used
935         // later on when creating properties.
936 
937         std::map<std::string, octave_value> get_methods;
938         std::map<std::string, octave_value> set_methods;
939 
940         // Method blocks
941 
942         std::list<tree_classdef_methods_block *> mb_list = b->methods_list ();
943 
944         load_path& lp = interp.get_load_path ();
945 
946         for (auto& mb_p : mb_list)
947           {
948             std::map<std::string, octave_value> amap;
949 
950 #if DEBUG_TRACE
951             std::cerr << "method block" << std::endl;
952 #endif
953 
954             // Method attributes
955 
956             if (mb_p->attribute_list ())
957               {
958                 for (auto& attr_p : *mb_p->attribute_list ())
959                   {
960                     std::string aname = attr_p->ident ()->name ();
961                     octave_value avalue = compute_attribute_value (tw, attr_p);
962 
963 #if DEBUG_TRACE
964                     std::cerr << "method attribute: " << aname << " = "
965                               << attribute_value_to_string (attr_p, avalue)
966                               << std::endl;
967 #endif
968 
969                     amap[aname] = avalue;
970                   }
971               }
972 
973             // Methods
974 
975             if (mb_p->element_list ())
976               {
977                 for (auto& mtd : *mb_p->element_list ())
978                   {
979                     std::string mname = mtd.function_value ()->name ();
980                     std::string mprefix = mname.substr (0, 4);
981 
982                     if (mprefix == "get.")
983                       get_methods[mname.substr (4)]
984                         = make_fcn_handle (mtd, mname, full_class_name);
985                     else if (mprefix == "set.")
986                       set_methods[mname.substr (4)]
987                         = make_fcn_handle (mtd, mname, full_class_name);
988                     else
989                       {
990                         cdef_method meth = cdm.make_method (retval, mname, mtd);
991 
992 #if DEBUG_TRACE
993                         std::cerr << (mname == class_name ? "constructor"
994                                       : "method")
995                                   << ": " << mname << std::endl;
996 #endif
997 
998                         for (auto& attrnm_val : amap)
999                           meth.put (attrnm_val.first, attrnm_val.second);
1000 
1001                         retval.install_method (meth);
1002                       }
1003                   }
1004               }
1005           }
1006 
1007         if (is_at_folder)
1008           {
1009             // Look for all external methods visible on octave path at the
1010             // time of loading of the class.
1011             //
1012             // FIXME: This is an "extension" to Matlab behavior, which only looks
1013             // in the @-folder containing the original classdef file.  However,
1014             // this is easier to implement it that way at the moment.
1015 
1016             std::list<std::string> external_methods
1017               = lp.methods (full_class_name);
1018 
1019             for (const auto& mtdnm : external_methods)
1020               {
1021                 // FIXME: should we issue a warning if the method is already
1022                 // defined in the classdef file?
1023 
1024                 if (mtdnm != class_name
1025                     && ! retval.find_method (mtdnm, true).ok ())
1026                   {
1027                     // Create a dummy method that is used until the actual
1028                     // method is loaded.
1029                     octave_user_function *fcn = new octave_user_function ();
1030 
1031                     fcn->stash_function_name (mtdnm);
1032 
1033                     cdef_method meth
1034                       = cdm.make_method (retval, mtdnm, octave_value (fcn));
1035 
1036                     retval.install_method (meth);
1037                   }
1038               }
1039           }
1040 
1041         // Property blocks
1042 
1043         // FIXME: default property expression should be able to call static
1044         //        methods of the class being constructed.  A restricted CLASSNAME
1045         //        symbol should be added to the scope before evaluating default
1046         //        value expressions.
1047 
1048         std::list<tree_classdef_properties_block *> pb_list
1049           = b->properties_list ();
1050 
1051         for (auto& pb_p : pb_list)
1052           {
1053             std::map<std::string, octave_value> amap;
1054 
1055 #if DEBUG_TRACE
1056             std::cerr << "property block" << std::endl;
1057 #endif
1058 
1059             // Property attributes
1060 
1061             if (pb_p->attribute_list ())
1062               {
1063                 for (auto& attr_p : *pb_p->attribute_list ())
1064                   {
1065                     std::string aname = attr_p->ident ()->name ();
1066                     octave_value avalue = compute_attribute_value (tw, attr_p);
1067 
1068 #if DEBUG_TRACE
1069                     std::cerr << "property attribute: " << aname << " = "
1070                               << attribute_value_to_string (attr_p, avalue)
1071                               << std::endl;
1072 #endif
1073 
1074                     if (aname == "Access")
1075                       {
1076                         amap["GetAccess"] = avalue;
1077                         amap["SetAccess"] = avalue;
1078                       }
1079                     else
1080                       amap[aname] = avalue;
1081                   }
1082               }
1083 
1084             // Properties
1085 
1086             if (pb_p->element_list ())
1087               {
1088                 for (auto& prop_p : *pb_p->element_list ())
1089                   {
1090                     std::string prop_name = prop_p->ident ()->name ();
1091 
1092                     cdef_property prop = cdm.make_property (retval, prop_name);
1093 
1094 #if DEBUG_TRACE
1095                     std::cerr << "property: " << prop_p->ident ()->name ()
1096                               << std::endl;
1097 #endif
1098 
1099                     tree_expression *expr = prop_p->expression ();
1100                     if (expr)
1101                       {
1102                         octave_value pvalue = expr->evaluate (tw);
1103 
1104 #if DEBUG_TRACE
1105                         std::cerr << "property default: "
1106                                   << attribute_value_to_string (prop_p, pvalue)
1107                                   << std::endl;
1108 #endif
1109 
1110                         prop.put ("DefaultValue", pvalue);
1111                       }
1112 
1113                     // Install property attributes.  This is done before assigning
1114                     // the property accessors so we can do validation by using
1115                     // cdef_property methods.
1116 
1117                     for (auto& attrnm_val : amap)
1118                       prop.put (attrnm_val.first, attrnm_val.second);
1119 
1120                     // Install property access methods, if any.  Remove the
1121                     // accessor methods from the temporary storage map, so we can
1122                     // detect which ones are invalid and do not correspond to a
1123                     // defined property.
1124 
1125                     auto git = get_methods.find (prop_name);
1126 
1127                     if (git != get_methods.end ())
1128                       {
1129                         make_function_of_class (retval, git->second);
1130                         prop.put ("GetMethod", git->second);
1131                         get_methods.erase (git);
1132                       }
1133 
1134                     auto sit = set_methods.find (prop_name);
1135 
1136                     if (sit != set_methods.end ())
1137                       {
1138                         make_function_of_class (retval, sit->second);
1139                         prop.put ("SetMethod", sit->second);
1140                         set_methods.erase (sit);
1141                       }
1142 
1143                     retval.install_property (prop);
1144                   }
1145               }
1146           }
1147       }
1148 
1149     return retval;
1150   }
1151 
1152   octave_value
get_method_function(const std::string &)1153   cdef_class::get_method_function (const std::string& /* nm */)
1154   {
1155     return octave_value (new octave_classdef_meta (*this));
1156   }
1157 }
1158