1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 2007-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 <istream>
31 #include <memory>
32 #include <ostream>
33 
34 #include "Array-util.h"
35 #include "byte-swap.h"
36 #include "oct-locbuf.h"
37 #include "lo-mappers.h"
38 
39 #include "Cell.h"
40 #include "defun.h"
41 #include "error.h"
42 #include "file-ops.h"
43 #include "errwarn.h"
44 #include "interpreter-private.h"
45 #include "interpreter.h"
46 #include "load-path.h"
47 #include "ls-hdf5.h"
48 #include "ls-oct-text.h"
49 #include "ls-oct-binary.h"
50 #include "ls-utils.h"
51 #include "mxarray.h"
52 #include "oct-lvalue.h"
53 #include "oct-hdf5.h"
54 #include "ov-class.h"
55 #include "ov-fcn.h"
56 #include "ov-typeinfo.h"
57 #include "ov-usr-fcn.h"
58 #include "pager.h"
59 #include "parse.h"
60 #include "pr-output.h"
61 #include "unwind-prot.h"
62 #include "variables.h"
63 
64 
65 int octave_class::t_id (-1);
66 
67 const std::string octave_class::t_name ("class");
68 
69 void
register_type(octave::type_info & ti)70 octave_class::register_type (octave::type_info& ti)
71 {
72   t_id = ti.register_type (octave_class::t_name, "<unknown>",
73                            octave_value (new octave_class ()));
74 }
75 
octave_class(const octave_map & m,const std::string & id,const octave_value_list & parents)76 octave_class::octave_class (const octave_map& m, const std::string& id,
77                             const octave_value_list& parents)
78   : octave_base_value (), map (m), c_name (id), obsolete_copies (0)
79 {
80   octave_idx_type n = parents.length ();
81 
82   for (octave_idx_type idx = 0; idx < n; idx++)
83     {
84       octave_value parent = parents(idx);
85 
86       if (! parent.isobject ())
87         error ("parents must be objects");
88 
89       std::string pcnm = parent.class_name ();
90 
91       if (find_parent_class (pcnm))
92         error ("duplicate class in parent tree");
93 
94       parent_list.push_back (pcnm);
95 
96       octave_idx_type nel = map.numel ();
97       octave_idx_type p_nel = parent.numel ();
98 
99       if (nel == 0)
100         {
101           if (p_nel == 0)
102             {
103               // No elements in MAP or the parent class object,
104               // so just add the field name.
105 
106               map.assign (pcnm, Cell (map.dims ()));
107             }
108           else if (p_nel == 1)
109             {
110               if (map.nfields () == 0)
111                 {
112                   // No elements or fields in MAP, but the
113                   // parent is class object with one element.
114                   // Resize to match size of parent class and
115                   // make the parent a field in MAP.
116 
117                   map.resize (parent.dims ());
118 
119                   map.assign (pcnm, parent);
120                 }
121               else
122                 {
123                   // No elements in MAP, but we have at least
124                   // one field.  So don't resize, just add the
125                   // field name.
126 
127                   map.assign (pcnm, Cell (map.dims ()));
128                 }
129             }
130           else if (map.nfields () == 0)
131             {
132               // No elements or fields in MAP and more than one
133               // element in the parent class object, so we can
134               // resize MAP to match parent dimsenions, then
135               // distribute the elements of the parent object to
136               // the elements of MAP.
137 
138               dim_vector parent_dims = parent.dims ();
139 
140               map.resize (parent_dims);
141 
142               Cell c (parent_dims);
143 
144               octave_map pmap = parent.map_value ();
145 
146               std::list<std::string> plist
147                 = parent.parent_class_name_list ();
148 
149               for (octave_idx_type i = 0; i < p_nel; i++)
150                 c(i) = octave_value (pmap.index (i), pcnm, plist);
151 
152               map.assign (pcnm, c);
153             }
154           else
155             error ("class: parent class dimension mismatch");
156         }
157       else if (nel == 1 && p_nel == 1)
158         {
159           // Simple assignment.
160 
161           map.assign (pcnm, parent);
162         }
163       else
164         {
165           if (p_nel == 1)
166             {
167               // Broadcast the scalar parent class object to
168               // each element of MAP.
169 
170               Cell pcell (map.dims (), parent);
171 
172               map.assign (pcnm, pcell);
173             }
174           else if (nel == p_nel)
175             {
176               // FIXME: is there a better way to do this?
177 
178               // The parent class object has the same number of
179               // elements as the map we are using to create the
180               // new object, so distribute those elements to
181               // each element of the new object by first
182               // splitting the elements of the parent class
183               // object into a cell array with one element per
184               // cell.  Then do the assignment all at once.
185 
186               Cell c (parent.dims ());
187 
188               octave_map pmap = parent.map_value ();
189 
190               std::list<std::string> plist
191                 = parent.parent_class_name_list ();
192 
193               for (octave_idx_type i = 0; i < p_nel; i++)
194                 c(i) = octave_value (pmap.index (i), pcnm, plist);
195 
196               map.assign (pcnm, c);
197             }
198           else
199             error ("class: parent class dimension mismatch");
200         }
201     }
202 
203   octave::symbol_table& symtab = octave::__get_symbol_table__ ("octave_class");
204 
205   symtab.add_to_parent_map (id, parent_list);
206 }
207 
208 octave_base_value *
unique_clone(void)209 octave_class::unique_clone (void)
210 {
211   if (count == obsolete_copies)
212     {
213       // All remaining copies are obsolete.  We don't actually need to clone.
214       count++;
215       return this;
216     }
217   else
218     {
219       // In theory, this shouldn't be happening, but it's here just in case.
220       if (count < obsolete_copies)
221         obsolete_copies = 0;
222 
223       return clone ();
224     }
225 }
226 
227 std::string
get_current_method_class(void)228 octave_class::get_current_method_class (void)
229 {
230   std::string retval = class_name ();
231 
232   if (nparents () > 0)
233     {
234       octave::tree_evaluator& tw
235         = octave::__get_evaluator__ ("octave_class::get_current_method_class");
236 
237       octave_function *fcn = tw.current_function ();
238 
239       // Here we are just looking to see if FCN is a method or constructor
240       // for any class, not specifically this one.
241       if (fcn && (fcn->is_class_method () || fcn->is_class_constructor ()))
242         retval = fcn->dispatch_class ();
243     }
244 
245   return retval;
246 }
247 
248 OCTAVE_NORETURN static
249 void
err_invalid_index1(void)250 err_invalid_index1 (void)
251 {
252   error ("invalid index for class");
253 }
254 
255 OCTAVE_NORETURN static
256 void
err_invalid_index_for_assignment(void)257 err_invalid_index_for_assignment (void)
258 {
259   error ("invalid index for class assignment");
260 }
261 
262 OCTAVE_NORETURN static
263 void
err_invalid_index_type(const std::string & nm,char t)264 err_invalid_index_type (const std::string& nm, char t)
265 {
266   error ("%s cannot be indexed with %c", nm.c_str (), t);
267 }
268 
269 void
break_closure_cycles(const std::shared_ptr<octave::stack_frame> & frame)270 octave_class::break_closure_cycles (const std::shared_ptr<octave::stack_frame>& frame)
271 {
272   for (octave_idx_type j = 0; j < map.nfields (); j++)
273     {
274       Cell& c = map.contents (j);
275 
276       for (octave_idx_type i = 0; i < c.numel (); i++)
277         c(i).break_closure_cycles (frame);
278     }
279 }
280 
281 Cell
dotref(const octave_value_list & idx)282 octave_class::dotref (const octave_value_list& idx)
283 {
284   assert (idx.length () == 1);
285 
286   std::string method_class = get_current_method_class ();
287 
288   // Find the class in which this method resides before attempting to access
289   // the requested field.
290 
291   octave_base_value *obvp = find_parent_class (method_class);
292 
293   if (obvp == nullptr)
294     error ("malformed class");
295 
296   octave_map my_map = (obvp != this) ? obvp->map_value () : map;
297 
298   std::string nm = idx(0).xstring_value ("invalid index for class");
299 
300   octave_map::const_iterator p = my_map.seek (nm);
301 
302   if (p == my_map.end ())
303     error ("class has no member '%s'", nm.c_str ());
304 
305   return my_map.contents (p);
306 }
307 
308 Matrix
size(void)309 octave_class::size (void)
310 {
311   if (in_class_method () || called_from_builtin ())
312     return octave_base_value::size ();
313 
314   Matrix retval (1, 2, 1.0);
315 
316   octave::symbol_table& symtab
317     = octave::__get_symbol_table__ ("octave_class::size");
318 
319   octave_value meth = symtab.find_method ("size", class_name ());
320 
321   if (meth.is_defined ())
322     {
323       count++;
324       octave_value_list args (1, octave_value (this));
325 
326       octave_value_list lv = octave::feval (meth.function_value (), args, 1);
327       if (lv.length () <= 0
328           || ! lv(0).is_matrix_type () || ! lv(0).dims ().isvector ())
329         error ("@%s/size: invalid return value", class_name ().c_str ());
330 
331       retval = lv(0).matrix_value ();
332     }
333   else
334     {
335       dim_vector dv = dims ();
336 
337       int nd = dv.ndims ();
338 
339       retval.resize (1, nd);
340 
341       for (int i = 0; i < nd; i++)
342         retval(i) = dv(i);
343     }
344 
345   return retval;
346 }
347 
348 octave_idx_type
xnumel(const octave_value_list & idx)349 octave_class::xnumel (const octave_value_list& idx)
350 {
351   if (in_class_method () || called_from_builtin ())
352     return octave_base_value::xnumel (idx);
353 
354   octave_idx_type retval = -1;
355   const std::string cn = class_name ();
356 
357   octave::symbol_table& symtab
358     = octave::__get_symbol_table__ ("octave_class::numel");
359 
360   octave_value meth = symtab.find_method ("numel", cn);
361 
362   if (meth.is_defined ())
363     {
364       octave_value_list args (idx.length () + 1, octave_value ());
365 
366       count++;
367       args(0) = octave_value (this);
368 
369       for (octave_idx_type i = 0; i < idx.length (); i++)
370         args(i+1) = idx(i);
371 
372       octave_value_list lv = octave::feval (meth.function_value (), args, 1);
373       if (lv.length () != 1 || ! lv(0).is_scalar_type ())
374         error ("@%s/numel: invalid return value", cn.c_str ());
375 
376       retval = lv(0).idx_type_value (true);
377     }
378   else
379     retval = octave_base_value::xnumel (idx);
380 
381   return retval;
382 }
383 
384 octave_value_list
subsref(const std::string & type,const std::list<octave_value_list> & idx,int nargout)385 octave_class::subsref (const std::string& type,
386                        const std::list<octave_value_list>& idx,
387                        int nargout)
388 {
389   octave_value_list retval;
390 
391   if (in_class_method () || called_from_builtin ())
392     {
393       // FIXME: this block of code is the same as the body of
394       // octave_struct::subsref.  Maybe it could be shared instead of
395       // duplicated.
396 
397       int skip = 1;
398 
399       switch (type[0])
400         {
401         case '(':
402           {
403             if (type.length () > 1 && type[1] == '.')
404               {
405                 auto p = idx.begin ();
406                 octave_value_list key_idx = *++p;
407 
408                 Cell tmp = dotref (key_idx);
409 
410                 Cell t = tmp.index (idx.front ());
411 
412                 retval(0) = (t.numel () == 1 ? t(0)
413                                              : octave_value (t, true));
414 
415                 // We handled two index elements, so tell
416                 // next_subsref to skip both of them.
417                 skip++;
418               }
419             else
420               retval(0) = octave_value (map.index (idx.front ()),
421                                         c_name, parent_list);
422           }
423           break;
424 
425         case '.':
426           {
427             if (map.numel () > 0)
428               {
429                 Cell t = dotref (idx.front ());
430 
431                 retval(0) = (t.numel () == 1) ? t(0) : octave_value (t, true);
432               }
433           }
434           break;
435 
436         case '{':
437           err_invalid_index_type (type_name (), type[0]);
438           break;
439 
440         default:
441           panic_impossible ();
442         }
443 
444       // FIXME: perhaps there should be an
445       // octave_value_list::next_subsref member function?  See also
446       // octave_user_function::subsref.
447 
448       if (idx.size () > 1)
449         retval = retval(0).next_subsref (nargout, type, idx, skip);
450     }
451   else
452     {
453       octave::symbol_table& symtab
454         = octave::__get_symbol_table__ ("octave_class::subsref");
455 
456       octave_value meth = symtab.find_method ("subsref", class_name ());
457 
458       if (meth.is_defined ())
459         {
460           octave_value_list args;
461 
462           args(1) = make_idx_args (type, idx, "subsref");
463 
464           count++;
465           args(0) = octave_value (this);
466 
467           // FIXME: for Matlab compatibility, let us attempt to set up a proper
468           // value for nargout at least in the simple case where the
469           // cs-list-type expression - i.e., {} or ().x, is the leading one.
470           // Note that Octave does not actually need this, since it will
471           // be able to properly react to varargout a posteriori.
472           bool maybe_cs_list_query = (type[0] == '.' || type[0] == '{'
473                                       || (type.length () > 1 && type[0] == '('
474                                           && type[1] == '.'));
475 
476           int true_nargout = nargout;
477 
478           if (maybe_cs_list_query)
479             {
480               // Set up a proper nargout for the subsref call by calling numel.
481               octave_value_list tmp;
482               if (type[0] != '.') tmp = idx.front ();
483               true_nargout = xnumel (tmp);
484             }
485 
486           retval = octave::feval (meth.function_value (), args, true_nargout);
487 
488           // Since we're handling subsref, if the list has more than one
489           // element, return it as a comma-separated list so that we can
490           // pass it to the evaluator
491           if (retval.length () > 1)
492             retval = octave_value (retval);
493         }
494       else
495         {
496           if (type.length () == 1 && type[0] == '(')
497             retval(0) = octave_value (map.index (idx.front ()), c_name,
498                                       parent_list);
499           else
500             err_invalid_index1 ();
501         }
502     }
503 
504   return retval;
505 }
506 
507 octave_value
numeric_conv(const Cell & val,const std::string & type)508 octave_class::numeric_conv (const Cell& val, const std::string& type)
509 {
510   octave_value retval;
511 
512   if (val.numel () != 1)
513     err_invalid_index_for_assignment ();
514 
515   retval = val(0);
516 
517   if (type.length () > 0 && type[0] == '.' && ! retval.isstruct ())
518     retval = octave_map ();
519 
520   return retval;
521 }
522 
523 octave_value
subsasgn(const std::string & type,const std::list<octave_value_list> & idx,const octave_value & rhs)524 octave_class::subsasgn (const std::string& type,
525                         const std::list<octave_value_list>& idx,
526                         const octave_value& rhs)
527 {
528   count++;
529   return subsasgn_common (octave_value (this), type, idx, rhs);
530 }
531 
532 octave_value
undef_subsasgn(const std::string & type,const std::list<octave_value_list> & idx,const octave_value & rhs)533 octave_class::undef_subsasgn (const std::string& type,
534                               const std::list<octave_value_list>& idx,
535                               const octave_value& rhs)
536 {
537   // For compatibility with Matlab, pass [] as the first argument to the
538   // the subsasgn function when the LHS of an indexed assignment is
539   // undefined.
540 
541   return subsasgn_common (Matrix (), type, idx, rhs);
542 }
543 
544 octave_value
subsasgn_common(const octave_value & obj,const std::string & type,const std::list<octave_value_list> & idx,const octave_value & rhs)545 octave_class::subsasgn_common (const octave_value& obj,
546                                const std::string& type,
547                                const std::list<octave_value_list>& idx,
548                                const octave_value& rhs)
549 {
550   octave_value retval;
551 
552   if (! (in_class_method () || called_from_builtin ()))
553     {
554       octave::symbol_table& symtab
555         = octave::__get_symbol_table__ ("octave_class::subsasgn_common");
556 
557       octave_value meth = symtab.find_method ("subsasgn", class_name ());
558 
559       if (meth.is_defined ())
560         {
561           octave_value_list args;
562 
563           if (rhs.is_cs_list ())
564             {
565               octave_value_list lrhs = rhs.list_value ();
566               args.resize (2 + lrhs.length ());
567               for (octave_idx_type i = 0; i < lrhs.length (); i++)
568                 args(2+i) = lrhs(i);
569             }
570           else
571             args(2) = rhs;
572 
573           args(1) = make_idx_args (type, idx, "subsasgn");
574           args(0) = obj;
575 
576           // Now comes the magic.  Count copies with me:
577           // 1. myself (obsolete)
578           // 2. the copy inside args (obsolete)
579           // 3. the copy in method's symbol table (working)
580           // ... possibly more (not obsolete).
581           //
582           // So we mark 2 copies as obsolete and hold our fingers crossed.
583           // But prior to doing that, check whether the routine is amenable
584           // to the optimization.
585           // It is essential that the handling function doesn't store extra
586           // copies anywhere.  If it does, things will not break but the
587           // optimization won't work.
588 
589           octave_value_list tmp;
590 
591           if (obsolete_copies == 0 && meth.is_user_function ()
592               && meth.user_function_value ()->subsasgn_optimization_ok ())
593             {
594               octave::unwind_protect frame;
595               frame.protect_var (obsolete_copies);
596               obsolete_copies = 2;
597 
598               tmp = octave::feval (meth.function_value (), args);
599             }
600           else
601             tmp = octave::feval (meth.function_value (), args);
602 
603           // FIXME: Should the subsasgn method be able to return
604           //        more than one value?
605 
606           if (tmp.length () > 1)
607             error ("@%s/subsasgn returned more than one value",
608                    class_name ().c_str ());
609 
610           else
611             retval = tmp(0);
612 
613           return retval;
614         }
615     }
616 
617   // Find the class in which this method resides before
618   // attempting to do the indexed assignment.
619 
620   std::string method_class = get_current_method_class ();
621 
622   octave_base_value *obvp = unique_parent_class (method_class);
623   if (obvp != this)
624     {
625 
626       if (! obvp)
627         error ("malformed class");
628 
629       obvp->subsasgn (type, idx, rhs);
630 
631       count++;
632       retval = octave_value (this);
633 
634       return retval;
635     }
636 
637   // FIXME: this block of code is the same as the body of
638   // octave_struct::subsasgn.  Maybe it could be shared instead of
639   // duplicated.
640 
641   int n = type.length ();
642 
643   octave_value t_rhs = rhs;
644 
645   if (n > 1 && ! (type.length () == 2 && type[0] == '(' && type[1] == '.'))
646     {
647       switch (type[0])
648         {
649         case '(':
650           {
651             if (type.length () > 1 && type[1] == '.')
652               {
653                 auto p = idx.begin ();
654                 octave_value_list t_idx = *p;
655 
656                 octave_value_list key_idx = *++p;
657 
658                 assert (key_idx.length () == 1);
659 
660                 std::string key = key_idx(0).xstring_value ("invalid index for class assignment");
661 
662                 octave_value u;
663 
664                 if (! map.contains (key))
665                   u = octave_value::empty_conv (type.substr (2), rhs);
666                 else
667                   {
668                     Cell map_val = map.contents (key);
669 
670                     Cell map_elt = map_val.index (idx.front (), true);
671 
672                     u = numeric_conv (map_elt, type.substr (2));
673                   }
674 
675                 std::list<octave_value_list> next_idx (idx);
676 
677                 // We handled two index elements, so subsasgn to
678                 // needs to skip both of them.
679 
680                 next_idx.erase (next_idx.begin ());
681                 next_idx.erase (next_idx.begin ());
682 
683                 u.make_unique ();
684 
685                 t_rhs = u.subsasgn (type.substr (2), next_idx, rhs);
686               }
687             else
688               err_invalid_index_for_assignment ();
689           }
690           break;
691 
692         case '.':
693           {
694             octave_value_list key_idx = idx.front ();
695 
696             assert (key_idx.length () == 1);
697 
698             std::string key = key_idx(0).string_value ();
699 
700             std::list<octave_value_list> next_idx (idx);
701 
702             next_idx.erase (next_idx.begin ());
703 
704             std::string next_type = type.substr (1);
705 
706             Cell tmpc (1, 1);
707             auto pkey = map.seek (key);
708             if (pkey != map.end ())
709               {
710                 map.contents (pkey).make_unique ();
711                 tmpc = map.contents (pkey);
712               }
713 
714             // FIXME: better code reuse?
715             if (tmpc.numel () != 1)
716               err_indexed_cs_list ();
717 
718             octave_value& tmp = tmpc(0);
719 
720             if (! tmp.is_defined () || tmp.is_zero_by_zero ())
721               {
722                 tmp = octave_value::empty_conv (next_type, rhs);
723                 tmp.make_unique (); // probably a no-op.
724               }
725             else
726               // optimization: ignore copy still stored inside our map.
727               tmp.make_unique (1);
728 
729             t_rhs = tmp.subsasgn (next_type, next_idx, rhs);
730           }
731           break;
732 
733         case '{':
734           err_invalid_index_type (type_name (), type[0]);
735           break;
736 
737         default:
738           panic_impossible ();
739         }
740     }
741 
742   switch (type[0])
743     {
744     case '(':
745       {
746         if (n > 1 && type[1] == '.')
747           {
748             auto p = idx.begin ();
749             octave_value_list key_idx = *++p;
750 
751             assert (key_idx.length () == 1);
752 
753             std::string key = key_idx(0).xstring_value ("assignment to class element failed");
754 
755             map.assign (idx.front (), key, t_rhs);
756 
757             count++;
758             retval = octave_value (this);
759           }
760         else
761           {
762             if (t_rhs.isobject () || t_rhs.isstruct ())
763               {
764                 octave_map rhs_map = t_rhs.xmap_value ("invalid class assignment");
765 
766                 map.assign (idx.front (), rhs_map);
767 
768                 count++;
769                 retval = octave_value (this);
770               }
771             else
772               {
773                 if (! t_rhs.isempty ())
774                   error ("invalid class assignment");
775 
776                 map.delete_elements (idx.front ());
777 
778                 count++;
779                 retval = octave_value (this);
780               }
781           }
782       }
783       break;
784 
785     case '.':
786       {
787         octave_value_list key_idx = idx.front ();
788 
789         assert (key_idx.length () == 1);
790 
791         std::string key = key_idx(0).string_value ();
792 
793         if (t_rhs.is_cs_list ())
794           {
795             Cell tmp_cell = Cell (t_rhs.list_value ());
796 
797             // The shape of the RHS is irrelevant, we just want
798             // the number of elements to agree and to preserve the
799             // shape of the left hand side of the assignment.
800 
801             if (numel () == tmp_cell.numel ())
802               tmp_cell = tmp_cell.reshape (dims ());
803 
804             map.setfield (key, tmp_cell);
805           }
806         else
807           {
808             Cell tmp_cell(1, 1);
809             tmp_cell(0) = t_rhs.storable_value ();
810             map.setfield (key, tmp_cell);
811           }
812 
813         count++;
814         retval = octave_value (this);
815       }
816       break;
817 
818     case '{':
819       err_invalid_index_type (type_name (), type[0]);
820       break;
821 
822     default:
823       panic_impossible ();
824     }
825 
826   return retval;
827 }
828 
829 idx_vector
index_vector(bool require_integers) const830 octave_class::index_vector (bool require_integers) const
831 {
832   octave::symbol_table& symtab
833     = octave::__get_symbol_table__ ("octave_class::index_vector");
834 
835   octave_value meth = symtab.find_method ("subsindex", class_name ());
836 
837   if (! meth.is_defined ())
838     error ("no subsindex method defined for class %s",
839            class_name ().c_str ());
840 
841   octave_value_list args;
842   args(0) = octave_value (new octave_class (map, c_name, parent_list));
843 
844   octave_value_list tmp = octave::feval (meth.function_value (), args, 1);
845 
846   if (tmp(0).isobject ())
847     error ("subsindex function must return a valid index vector");
848 
849   // Index vector returned by subsindex is zero based
850   // (why this inconsistency Mathworks?), and so we must
851   // add one to the value returned as the index_vector method
852   // expects it to be one based.
853   return do_binary_op (octave_value::op_add, tmp (0),
854                        octave_value (1.0)).index_vector (require_integers);
855 }
856 
857 std::size_t
byte_size(void) const858 octave_class::byte_size (void) const
859 {
860   // Neglect the size of the fieldnames.
861 
862   std::size_t retval = 0;
863 
864   for (auto it = map.cbegin (); it != map.cend (); it++)
865     {
866       std::string key = map.key (it);
867 
868       octave_value val = octave_value (map.contents (it));
869 
870       retval += val.byte_size ();
871     }
872 
873   return retval;
874 }
875 
876 bool
is_true(void) const877 octave_class::is_true (void) const
878 {
879   bool retval = false;
880 
881   octave::symbol_table& symtab
882     = octave::__get_symbol_table__ ("octave_class::is_true");
883 
884   octave_value meth = symtab.find_method ("logical", class_name ());
885 
886   if (meth.is_defined ())
887     {
888       octave_value in = new octave_class (*this);
889 
890       octave_value_list tmp = octave::feval (meth.function_value (), in, 1);
891       retval = tmp(0).is_true ();
892     }
893 
894   return retval;
895 }
896 
897 string_vector
map_keys(void) const898 octave_class::map_keys (void) const
899 {
900   err_wrong_type_arg ("octave_class::map_keys()", type_name ());
901 }
902 
903 octave_base_value *
find_parent_class(const std::string & parent_class_name)904 octave_class::find_parent_class (const std::string& parent_class_name)
905 {
906   octave_base_value *retval = nullptr;
907 
908   if (parent_class_name == class_name ())
909     retval = this;
910   else
911     {
912       for (auto& par : parent_list)
913         {
914           octave_map::const_iterator smap = map.seek (par);
915 
916           const Cell& tmp = map.contents (smap);
917 
918           octave_value vtmp = tmp(0);
919 
920           octave_base_value *obvp = vtmp.internal_rep ();
921 
922           retval = obvp->find_parent_class (parent_class_name);
923 
924           if (retval)
925             break;
926         }
927     }
928 
929   return retval;
930 }
931 
932 octave_base_value *
unique_parent_class(const std::string & parent_class_name)933 octave_class::unique_parent_class (const std::string& parent_class_name)
934 {
935   octave_base_value *retval = nullptr;
936 
937   if (parent_class_name == class_name ())
938     retval = this;
939   else
940     {
941       for (auto& par : parent_list)
942         {
943           auto smap = map.seek (par);
944 
945           Cell& tmp = map.contents (smap);
946 
947           octave_value& vtmp = tmp(0);
948 
949           octave_base_value *obvp = vtmp.internal_rep ();
950 
951           // Use find_parent_class first to avoid uniquifying if not necessary.
952           retval = obvp->find_parent_class (parent_class_name);
953 
954           if (retval)
955             {
956               vtmp.make_unique ();
957               obvp = vtmp.internal_rep ();
958               retval = obvp->unique_parent_class (parent_class_name);
959 
960               break;
961             }
962         }
963     }
964 
965   return retval;
966 }
967 
968 bool
is_instance_of(const std::string & cls_name) const969 octave_class::is_instance_of (const std::string& cls_name) const
970 {
971   bool retval = false;
972 
973   if (cls_name == class_name ())
974     retval = true;
975   else
976     {
977       for (auto& par : parent_list)
978         {
979           octave_map::const_iterator smap = map.seek (par);
980 
981           const Cell& tmp = map.contents (smap);
982 
983           const octave_value& vtmp = tmp(0);
984 
985           retval = vtmp.is_instance_of (cls_name);
986 
987           if (retval)
988             break;
989         }
990     }
991 
992   return retval;
993 }
994 
995 string_vector
string_vector_value(bool pad) const996 octave_class::string_vector_value (bool pad) const
997 {
998   string_vector retval;
999 
1000   octave::symbol_table& symtab
1001     = octave::__get_symbol_table__ ("octave_class::string_vector_value");
1002 
1003   octave_value meth = symtab.find_method ("char", class_name ());
1004 
1005   if (! meth.is_defined ())
1006     error ("no char method defined for class %s", class_name ().c_str ());
1007 
1008   octave_value_list args;
1009   args(0) = octave_value (new octave_class (map, c_name, parent_list));
1010 
1011   octave_value_list tmp = octave::feval (meth.function_value (), args, 1);
1012 
1013   if (tmp.length () >= 1)
1014     {
1015       if (! tmp(0).is_string ())
1016         error ("cname/char method did not return a string");
1017 
1018       retval = tmp(0).string_vector_value (pad);
1019     }
1020 
1021   return retval;
1022 }
1023 
1024 void
print(std::ostream & os,bool)1025 octave_class::print (std::ostream& os, bool)
1026 {
1027   print_raw (os);
1028 }
1029 
1030 void
print_raw(std::ostream & os,bool) const1031 octave_class::print_raw (std::ostream& os, bool) const
1032 {
1033   octave::unwind_protect frame;
1034 
1035   indent (os);
1036   os << "  <class " << class_name () << '>';
1037   newline (os);
1038 }
1039 
1040 // Loading a class properly requires an exemplar map entry for success.
1041 // If we don't have one, we attempt to create one by calling the constructor
1042 // with no arguments.
1043 bool
reconstruct_exemplar(void)1044 octave_class::reconstruct_exemplar (void)
1045 {
1046   bool retval = false;
1047 
1048   octave_class::exemplar_const_iterator it
1049     = octave_class::exemplar_map.find (c_name);
1050 
1051   if (it != octave_class::exemplar_map.end ())
1052     retval = true;
1053   else
1054     {
1055       octave::interpreter& interp
1056         = octave::__get_interpreter__  ("octave_class::reconstruct_exemplar");
1057 
1058       octave::symbol_table& symtab = interp.get_symbol_table ();
1059 
1060       octave_value ctor = symtab.find_method (c_name, c_name);
1061 
1062       bool have_ctor = false;
1063 
1064       if (ctor.is_defined () && ctor.is_function ())
1065         {
1066           octave_function *fcn = ctor.function_value ();
1067 
1068           if (fcn && fcn->is_class_constructor (c_name))
1069             have_ctor = true;
1070 
1071           // Something has gone terribly wrong if
1072           // symbol_table::find_method (c_name, c_name) does not return
1073           // a class constructor for the class c_name...
1074           assert (have_ctor);
1075         }
1076 
1077       if (have_ctor)
1078         {
1079           octave::unwind_protect frame;
1080 
1081           // Simulate try/catch.
1082 
1083           interpreter_try (frame);
1084 
1085           bool execution_error = false;
1086 
1087           octave_value_list result;
1088 
1089           try
1090             {
1091               result = octave::feval (ctor, ovl (), 1);
1092             }
1093           catch (const octave::execution_exception&)
1094             {
1095               interp.recover_from_exception ();
1096 
1097               execution_error = true;
1098             }
1099 
1100           if (! execution_error && result.length () == 1)
1101             retval = true;
1102         }
1103       else
1104         warning ("no constructor for class %s", c_name.c_str ());
1105     }
1106 
1107   return retval;
1108 }
1109 
1110 void
clear_exemplar_map(void)1111 octave_class::clear_exemplar_map (void)
1112 {
1113   exemplar_map.clear ();
1114 }
1115 
1116 //  Load/save does not provide enough information to reconstruct the
1117 //  class inheritance structure.  reconstruct_parents () attempts to
1118 //  do so.  If successful, a "true" value is returned.
1119 //
1120 //  Note that we don't check the loaded object structure against the
1121 //  class structure here so the user's loadobj method has a chance
1122 //  to do its magic.
1123 bool
reconstruct_parents(void)1124 octave_class::reconstruct_parents (void)
1125 {
1126   bool retval = true;
1127   bool might_have_inheritance = false;
1128   std::string dbgstr = "dork";
1129 
1130   // First, check to see if there might be an issue with inheritance.
1131   for (auto it = map.cbegin (); it != map.cend (); it++)
1132     {
1133       std::string key = map.key (it);
1134       Cell        val = map.contents (it);
1135       if (val(0).isobject ())
1136         {
1137           dbgstr = "blork";
1138           if (key == val(0).class_name ())
1139             {
1140               might_have_inheritance = true;
1141               dbgstr = "cork";
1142               break;
1143             }
1144         }
1145     }
1146 
1147   if (might_have_inheritance)
1148     {
1149       octave_class::exemplar_const_iterator it
1150         = octave_class::exemplar_map.find (c_name);
1151 
1152       if (it == octave_class::exemplar_map.end ())
1153         retval = false;
1154       else
1155         {
1156           octave_class::exemplar_info exmplr = it->second;
1157           parent_list = exmplr.parents ();
1158           for (auto& par : parent_list)
1159             {
1160               dbgstr = par;
1161               bool dbgbool = map.contains (par);
1162               if (! dbgbool)
1163                 {
1164                   retval = false;
1165                   break;
1166                 }
1167             }
1168         }
1169     }
1170 
1171   return retval;
1172 }
1173 
1174 bool
save_ascii(std::ostream & os)1175 octave_class::save_ascii (std::ostream& os)
1176 {
1177   os << "# classname: " << class_name () << "\n";
1178   octave_map m;
1179 
1180   octave::load_path& lp = octave::__get_load_path__ ("octave_class::save_ascii");
1181 
1182   if (lp.find_method (class_name (), "saveobj") != "")
1183     {
1184       octave_value in = new octave_class (*this);
1185       octave_value_list tmp = octave::feval ("saveobj", in, 1);
1186 
1187       m = tmp(0).map_value ();
1188     }
1189   else
1190     m = map_value ();
1191 
1192   os << "# length: " << m.nfields () << "\n";
1193 
1194   auto i = m.begin ();
1195   while (i != m.end ())
1196     {
1197       octave_value val = map.contents (i);
1198 
1199       bool b = save_text_data (os, val, m.key (i), false, 0);
1200 
1201       if (! b)
1202         return ! os.fail ();
1203 
1204       i++;
1205     }
1206 
1207   return true;
1208 }
1209 
1210 bool
load_ascii(std::istream & is)1211 octave_class::load_ascii (std::istream& is)
1212 {
1213   octave_idx_type len = 0;
1214   std::string classname;
1215 
1216   if (! extract_keyword (is, "classname", classname) || classname.empty ())
1217     error ("load: failed to extract name of class");
1218 
1219   if (! extract_keyword (is, "length", len) || len < 0)
1220     error ("load: failed to extract number of elements in class");
1221 
1222   if (len > 0)
1223     {
1224       octave_map m (map);
1225 
1226       for (octave_idx_type j = 0; j < len; j++)
1227         {
1228           octave_value t2;
1229           bool dummy;
1230 
1231           // recurse to read cell elements
1232           std::string nm
1233             = read_text_data (is, "", dummy, t2, j);
1234 
1235           if (! is)
1236             break;
1237 
1238           Cell tcell = (t2.iscell () ? t2.xcell_value ("load: internal error loading class elements") : Cell (t2));
1239 
1240           m.assign (nm, tcell);
1241         }
1242 
1243       if (! is)
1244         error ("load: failed to load class");
1245 
1246       c_name = classname;
1247       reconstruct_exemplar ();
1248 
1249       map = m;
1250 
1251       if (! reconstruct_parents ())
1252         warning ("load: unable to reconstruct object inheritance");
1253 
1254       octave::load_path& lp = octave::__get_load_path__ ("octave_class::load_ascii");
1255 
1256       if (lp.find_method (classname, "loadobj") != "")
1257         {
1258           octave_value in = new octave_class (*this);
1259           octave_value_list tmp = octave::feval ("loadobj", in, 1);
1260 
1261           map = tmp(0).map_value ();
1262         }
1263     }
1264   else if (len == 0)
1265     {
1266       map = octave_map (dim_vector (1, 1));
1267       c_name = classname;
1268     }
1269   else
1270     panic_impossible ();
1271 
1272   return true;
1273 }
1274 
1275 bool
save_binary(std::ostream & os,bool save_as_floats)1276 octave_class::save_binary (std::ostream& os, bool save_as_floats)
1277 {
1278   int32_t classname_len = class_name ().length ();
1279 
1280   os.write (reinterpret_cast<char *> (&classname_len), 4);
1281   os << class_name ();
1282 
1283   octave_map m;
1284 
1285   octave::load_path& lp = octave::__get_load_path__ ("octave_class::save_binary");
1286 
1287   if (lp.find_method (class_name (), "saveobj") != "")
1288     {
1289       octave_value in = new octave_class (*this);
1290       octave_value_list tmp = octave::feval ("saveobj", in, 1);
1291 
1292       m = tmp(0).map_value ();
1293     }
1294   else
1295     m = map_value ();
1296 
1297   int32_t len = m.nfields ();
1298   os.write (reinterpret_cast<char *> (&len), 4);
1299 
1300   auto i = m.begin ();
1301   while (i != m.end ())
1302     {
1303       octave_value val = map.contents (i);
1304 
1305       bool b = save_binary_data (os, val, m.key (i), "", 0, save_as_floats);
1306 
1307       if (! b)
1308         return ! os.fail ();
1309 
1310       i++;
1311     }
1312 
1313   return true;
1314 }
1315 
1316 bool
load_binary(std::istream & is,bool swap,octave::mach_info::float_format fmt)1317 octave_class::load_binary (std::istream& is, bool swap,
1318                            octave::mach_info::float_format fmt)
1319 {
1320   bool success = true;
1321 
1322   int32_t classname_len;
1323 
1324   is.read (reinterpret_cast<char *> (&classname_len), 4);
1325   if (! is)
1326     return false;
1327   else if (swap)
1328     swap_bytes<4> (&classname_len);
1329 
1330   {
1331     OCTAVE_LOCAL_BUFFER (char, classname, classname_len+1);
1332     classname[classname_len] = '\0';
1333     if (! is.read (reinterpret_cast<char *> (classname), classname_len))
1334       return false;
1335     c_name = classname;
1336   }
1337   reconstruct_exemplar ();
1338 
1339   int32_t len;
1340   if (! is.read (reinterpret_cast<char *> (&len), 4))
1341     return false;
1342   if (swap)
1343     swap_bytes<4> (&len);
1344 
1345   if (len > 0)
1346     {
1347       octave_map m (map);
1348 
1349       for (octave_idx_type j = 0; j < len; j++)
1350         {
1351           octave_value t2;
1352           bool dummy;
1353           std::string doc;
1354 
1355           // recurse to read cell elements
1356           std::string nm = read_binary_data (is, swap, fmt, "",
1357                                              dummy, t2, doc);
1358 
1359           if (! is)
1360             break;
1361 
1362           Cell tcell = (t2.iscell () ? t2.xcell_value ("load: internal error loading class elements") : Cell (t2));
1363 
1364           m.assign (nm, tcell);
1365         }
1366 
1367       if (is)
1368         {
1369           map = m;
1370 
1371           if (! reconstruct_parents ())
1372             warning ("load: unable to reconstruct object inheritance");
1373 
1374           octave::load_path& lp = octave::__get_load_path__ ("octave_class::load_binary");
1375 
1376           if (lp.find_method (c_name, "loadobj") != "")
1377             {
1378               octave_value in = new octave_class (*this);
1379               octave_value_list tmp = octave::feval ("loadobj", in, 1);
1380 
1381               map = tmp(0).map_value ();
1382             }
1383         }
1384       else
1385         {
1386           warning ("load: failed to load class");
1387           success = false;
1388         }
1389     }
1390   else if (len == 0)
1391     map = octave_map (dim_vector (1, 1));
1392   else
1393     panic_impossible ();
1394 
1395   return success;
1396 }
1397 
1398 bool
save_hdf5(octave_hdf5_id loc_id,const char * name,bool save_as_floats)1399 octave_class::save_hdf5 (octave_hdf5_id loc_id, const char *name,
1400                          bool save_as_floats)
1401 {
1402 #if defined (HAVE_HDF5)
1403 
1404   hsize_t hdims[3];
1405   hid_t group_hid = -1;
1406   hid_t type_hid = -1;
1407   hid_t space_hid = -1;
1408   hid_t class_hid = -1;
1409   hid_t data_hid = -1;
1410   octave_map m;
1411   octave_map::iterator i;
1412 
1413   octave::load_path& lp = octave::__get_load_path__ ("octave_class::save_hdf5");
1414 
1415 #if defined (HAVE_HDF5_18)
1416   group_hid = H5Gcreate (loc_id, name, octave_H5P_DEFAULT, octave_H5P_DEFAULT,
1417                          octave_H5P_DEFAULT);
1418 #else
1419   group_hid = H5Gcreate (loc_id, name, 0);
1420 #endif
1421   if (group_hid < 0)
1422     goto error_cleanup;
1423 
1424   // Add the class name to the group
1425   type_hid = H5Tcopy (H5T_C_S1); H5Tset_size (type_hid, c_name.length () + 1);
1426   if (type_hid < 0)
1427     goto error_cleanup;
1428 
1429   hdims[0] = 0;
1430   space_hid = H5Screate_simple (0, hdims, nullptr);
1431   if (space_hid < 0)
1432     goto error_cleanup;
1433 #if defined (HAVE_HDF5_18)
1434   class_hid = H5Dcreate (group_hid, "classname",  type_hid, space_hid,
1435                          octave_H5P_DEFAULT, octave_H5P_DEFAULT,
1436                          octave_H5P_DEFAULT);
1437 #else
1438   class_hid = H5Dcreate (group_hid, "classname",  type_hid, space_hid,
1439                          octave_H5P_DEFAULT);
1440 #endif
1441   if (class_hid < 0 || H5Dwrite (class_hid, type_hid, octave_H5S_ALL,
1442                                  octave_H5S_ALL, octave_H5P_DEFAULT,
1443                                  c_name.c_str ()) < 0)
1444     goto error_cleanup;
1445 
1446 #if defined (HAVE_HDF5_18)
1447   data_hid = H5Gcreate (group_hid, "value", octave_H5P_DEFAULT,
1448                         octave_H5P_DEFAULT, octave_H5P_DEFAULT);
1449 #else
1450   data_hid = H5Gcreate (group_hid, "value", 0);
1451 #endif
1452   if (data_hid < 0)
1453     goto error_cleanup;
1454 
1455   if (lp.find_method (class_name (), "saveobj") != "")
1456     {
1457       octave_value in = new octave_class (*this);
1458       octave_value_list tmp = octave::feval ("saveobj", in, 1);
1459 
1460       m = tmp(0).map_value ();
1461     }
1462   else
1463     m = map_value ();
1464 
1465   // recursively add each element of the class to this group
1466   i = m.begin ();
1467   while (i != m.end ())
1468     {
1469       octave_value val = map.contents (i);
1470 
1471       bool retval2 = add_hdf5_data (data_hid, val, m.key (i), "", false,
1472                                     save_as_floats);
1473 
1474       if (! retval2)
1475         break;
1476 
1477       i++;
1478     }
1479 
1480 error_cleanup:
1481 
1482   if (data_hid > 0)
1483     H5Gclose (data_hid);
1484 
1485   if (class_hid > 0)
1486     H5Dclose (class_hid);
1487 
1488   if (space_hid > 0)
1489     H5Sclose (space_hid);
1490 
1491   if (type_hid > 0)
1492     H5Tclose (type_hid);
1493 
1494   if (group_hid > 0)
1495     H5Gclose (group_hid);
1496 
1497   return true;
1498 
1499 #else
1500   octave_unused_parameter (loc_id);
1501   octave_unused_parameter (name);
1502   octave_unused_parameter (save_as_floats);
1503 
1504   warn_save ("hdf5");
1505 
1506   return false;
1507 #endif
1508 }
1509 
1510 bool
load_hdf5(octave_hdf5_id loc_id,const char * name)1511 octave_class::load_hdf5 (octave_hdf5_id loc_id, const char *name)
1512 {
1513   bool retval = false;
1514 
1515 #if defined (HAVE_HDF5)
1516 
1517   hid_t group_hid = -1;
1518   hid_t data_hid = -1;
1519   hid_t type_hid = -1;
1520   hid_t type_class_hid = -1;
1521   hid_t space_hid = -1;
1522   hid_t subgroup_hid = -1;
1523   hid_t st_id = -1;
1524 
1525   hdf5_callback_data dsub;
1526 
1527   herr_t retval2 = 0;
1528   octave_map m (dim_vector (1, 1));
1529   int current_item = 0;
1530   hsize_t num_obj = 0;
1531   int slen = 0;
1532   hsize_t rank = 0;
1533 
1534 #if defined (HAVE_HDF5_18)
1535   group_hid = H5Gopen (loc_id, name, octave_H5P_DEFAULT);
1536 #else
1537   group_hid = H5Gopen (loc_id, name);
1538 #endif
1539   if (group_hid < 0)
1540     goto error_cleanup;
1541 
1542 #if defined (HAVE_HDF5_18)
1543   data_hid = H5Dopen (group_hid, "classname", octave_H5P_DEFAULT);
1544 #else
1545   data_hid = H5Dopen (group_hid, "classname");
1546 #endif
1547 
1548   if (data_hid < 0)
1549     goto error_cleanup;
1550 
1551   type_hid = H5Dget_type (data_hid);
1552 
1553   type_class_hid = H5Tget_class (type_hid);
1554 
1555   if (type_class_hid != H5T_STRING)
1556     goto error_cleanup;
1557 
1558   space_hid = H5Dget_space (data_hid);
1559   rank = H5Sget_simple_extent_ndims (space_hid);
1560 
1561   if (rank != 0)
1562     goto error_cleanup;
1563 
1564   slen = H5Tget_size (type_hid);
1565   if (slen < 0)
1566     goto error_cleanup;
1567 
1568   // do-while loop here to prevent goto crossing initialization of classname
1569   do
1570     {
1571       OCTAVE_LOCAL_BUFFER (char, classname, slen);
1572 
1573       // create datatype for (null-terminated) string to read into:
1574       st_id = H5Tcopy (H5T_C_S1);
1575       H5Tset_size (st_id, slen);
1576 
1577       if (H5Dread (data_hid, st_id, octave_H5S_ALL, octave_H5S_ALL,
1578                    octave_H5P_DEFAULT, classname)
1579           < 0)
1580         {
1581           H5Tclose (st_id);
1582           H5Dclose (data_hid);
1583           H5Gclose (group_hid);
1584           return false;
1585         }
1586 
1587       H5Tclose (st_id);
1588       H5Dclose (data_hid);
1589       data_hid = -1;
1590 
1591       c_name = classname;
1592     }
1593   while (0);
1594   reconstruct_exemplar ();
1595 
1596 #if defined (HAVE_HDF5_18)
1597   subgroup_hid = H5Gopen (group_hid, name, octave_H5P_DEFAULT);
1598 #else
1599   subgroup_hid = H5Gopen (group_hid, name);
1600 #endif
1601   H5Gget_num_objs (subgroup_hid, &num_obj);
1602   H5Gclose (subgroup_hid);
1603 
1604   while (current_item < static_cast<int> (num_obj)
1605          && (retval2 = hdf5_h5g_iterate (group_hid, name, &current_item,
1606                                          &dsub)) > 0)
1607     {
1608       octave_value t2 = dsub.tc;
1609 
1610       Cell tcell = (t2.iscell () ? t2.xcell_value ("load: internal error loading class elements") : Cell (t2));
1611 
1612       m.assign (dsub.name, tcell);
1613 
1614     }
1615 
1616   if (retval2 >= 0)
1617     {
1618       map = m;
1619 
1620       if (! reconstruct_parents ())
1621         warning ("load: unable to reconstruct object inheritance");
1622 
1623       octave::load_path& lp = octave::__get_load_path__ ("octave_class::load_hdf5");
1624 
1625       if (lp.find_method (c_name, "loadobj") != "")
1626         {
1627           octave_value in = new octave_class (*this);
1628           octave_value_list tmp = octave::feval ("loadobj", in, 1);
1629 
1630           map = tmp(0).map_value ();
1631           retval = true;
1632         }
1633     }
1634 
1635 error_cleanup:
1636   if (data_hid > 0)
1637     H5Dclose (data_hid);
1638 
1639   if (data_hid > 0)
1640     H5Gclose (group_hid);
1641 
1642 #else
1643   octave_unused_parameter (loc_id);
1644   octave_unused_parameter (name);
1645 
1646   warn_load ("hdf5");
1647 #endif
1648 
1649   return retval;
1650 }
1651 
1652 mxArray *
as_mxArray(void) const1653 octave_class::as_mxArray (void) const
1654 {
1655   err_wrong_type_arg ("octave_class::as_mxArray ()", type_name ());
1656 }
1657 
1658 bool
in_class_method(void)1659 octave_class::in_class_method (void)
1660 {
1661   octave::tree_evaluator& tw
1662     = octave::__get_evaluator__ ("octave_class::in_class_method");
1663 
1664   octave_function *fcn = tw.current_function ();
1665 
1666   return (fcn
1667           && (fcn->is_class_method ()
1668               || fcn->is_class_constructor ()
1669               || fcn->is_anonymous_function_of_class ()
1670               || fcn->is_private_function_of_class (class_name ()))
1671           && find_parent_class (fcn->dispatch_class ()));
1672 }
1673 
exemplar_info(const octave_value & obj)1674 octave_class::exemplar_info::exemplar_info (const octave_value& obj)
1675   : field_names (), parent_class_names ()
1676 {
1677   if (! obj.isobject ())
1678     error ("invalid call to exemplar_info constructor");
1679 
1680   octave_map m = obj.map_value ();
1681   field_names = m.keys ();
1682 
1683   parent_class_names = obj.parent_class_name_list ();
1684 }
1685 
1686 // A map from class names to lists of fields.
1687 std::map<std::string, octave_class::exemplar_info> octave_class::exemplar_map;
1688 
1689 bool
compare(const octave_value & obj) const1690 octave_class::exemplar_info::compare (const octave_value& obj) const
1691 {
1692 
1693   if (! obj.isobject ())
1694     error ("invalid comparison of class exemplar to non-class object");
1695 
1696   if (nfields () != obj.nfields ())
1697     error ("mismatch in number of fields");
1698 
1699   octave_map obj_map = obj.map_value ();
1700   string_vector obj_fnames = obj_map.keys ();
1701   string_vector fnames = fields ();
1702 
1703   for (octave_idx_type i = 0; i < nfields (); i++)
1704     {
1705       if (obj_fnames[i] != fnames[i])
1706         error ("mismatch in field names");
1707     }
1708 
1709   if (nparents () != obj.nparents ())
1710     error ("mismatch in number of parent classes");
1711 
1712   const std::list<std::string> obj_parents
1713     = obj.parent_class_name_list ();
1714   const std::list<std::string> pnames = parents ();
1715 
1716   auto p = obj_parents.begin ();
1717   auto q = pnames.begin ();
1718 
1719   while (p != obj_parents.end ())
1720     {
1721       if (*p++ != *q++)
1722         error ("mismatch in parent classes");
1723     }
1724 
1725   return true;
1726 }
1727 
1728 DEFMETHOD (class, interp, args, ,
1729            doc: /* -*- texinfo -*-
1730 @deftypefn  {} {@var{classname} =} class (@var{obj})
1731 @deftypefnx {} {} class (@var{s}, @var{id})
1732 @deftypefnx {} {} class (@var{s}, @var{id}, @var{p}, @dots{})
1733 Return the class of the object @var{obj}, or create a class with
1734 fields from structure @var{s} and name (string) @var{id}.
1735 
1736 Additional arguments name a list of parent classes from which the new class
1737 is derived.
1738 @seealso{typeinfo, isa}
1739 @end deftypefn */)
1740 {
1741   int nargin = args.length ();
1742 
1743   if (nargin == 0)
1744     print_usage ();
1745 
1746   octave_value retval;
1747 
1748   if (nargin == 1)
1749     // Called for class of object
1750     retval = args(0).class_name ();
1751   else
1752     {
1753       // Called as class constructor
1754       std::string id = args(1).xstring_value ("class: ID (class name) must be a string");
1755 
1756       octave::tree_evaluator& tw = interp.get_evaluator ();
1757 
1758       octave_function *fcn = tw.caller_function ();
1759 
1760       if (! fcn)
1761         error ("class: invalid call from outside class constructor or method");
1762 
1763       if (! fcn->is_class_constructor (id) && ! fcn->is_class_method (id))
1764         error ("class: '%s' is invalid as a class name in this context",
1765                id.c_str ());
1766 
1767       octave_map m = args(0).xmap_value ("class: S must be a valid structure");
1768 
1769       if (nargin == 2)
1770         retval
1771           = octave_value (new octave_class (m, id, std::list<std::string> ()));
1772       else
1773         {
1774           octave_value_list parents = args.slice (2, nargin-2);
1775 
1776           retval = octave_value (new octave_class (m, id, parents));
1777         }
1778 
1779       octave_class::exemplar_const_iterator it
1780         = octave_class::exemplar_map.find (id);
1781 
1782       if (it == octave_class::exemplar_map.end ())
1783         octave_class::exemplar_map[id] = octave_class::exemplar_info (retval);
1784       else if (! it->second.compare (retval))
1785         error ("class: object of class '%s' does not match previously constructed objects",
1786                id.c_str ());
1787     }
1788 
1789   return retval;
1790 }
1791 
1792 /*
1793 %!assert (class (1.1), "double")
1794 %!assert (class (single (1.1)), "single")
1795 %!assert (class (uint8 (1)), "uint8")
1796 %!testif HAVE_JAVA; usejava ("jvm")
1797 %! jobj = javaObject ("java.lang.StringBuffer");
1798 %! assert (class (jobj), "java.lang.StringBuffer");
1799 
1800 ## Test Input Validation
1801 %!error class ()
1802 */
1803 
1804 DEFUN (isa, args, ,
1805        doc: /* -*- texinfo -*-
1806 @deftypefn {} {} isa (@var{obj}, @var{classname})
1807 Return true if @var{obj} is an object from the class @var{classname}.
1808 
1809 @var{classname} may also be one of the following class categories:
1810 
1811 @table @asis
1812 @item @qcode{"float"}
1813 Floating point value comprising classes @qcode{"double"} and
1814 @qcode{"single"}.
1815 
1816 @item @qcode{"integer"}
1817 Integer value comprising classes (u)int8, (u)int16, (u)int32, (u)int64.
1818 
1819 @item @qcode{"numeric"}
1820 Numeric value comprising either a floating point or integer value.
1821 @end table
1822 
1823 If @var{classname} is a cell array of string, a logical array of the same
1824 size is returned, containing true for each class to which @var{obj}
1825 belongs to.
1826 
1827 @seealso{class, typeinfo}
1828 @end deftypefn */)
1829 {
1830   if (args.length () != 2)
1831     print_usage ();
1832 
1833   octave_value obj = args(0);  // not const because of find_parent_class ()
1834   std::string obj_cls = obj.class_name ();
1835   Array<std::string> clsnames = args(1).xcellstr_value ("isa: CLASSNAME must be a string or cell array of strings");
1836 
1837   boolNDArray matches (clsnames.dims (), false);
1838 
1839   for (octave_idx_type idx = 0; idx < clsnames.numel (); idx++)
1840     {
1841       std::string cls = clsnames(idx);
1842       if (obj_cls == cls
1843           || (cls == "float"   && obj.isfloat   ())
1844           || (cls == "integer" && obj.isinteger ())
1845           || (cls == "numeric" && obj.isnumeric ())
1846           || obj.is_instance_of (cls))
1847         matches(idx) = true;
1848     }
1849 
1850   return ovl (matches);
1851 }
1852 
1853 /*
1854 %!assert (isa ("char", "float"), false)
1855 %!assert (isa (logical (1), "float"), false)
1856 %!assert (isa (double (13), "float"), true)
1857 %!assert (isa (single (13), "float"), true)
1858 %!assert (isa (int8 (13), "float"), false)
1859 %!assert (isa (int16 (13), "float"), false)
1860 %!assert (isa (int32 (13), "float"), false)
1861 %!assert (isa (int64 (13), "float"), false)
1862 %!assert (isa (uint8 (13), "float"), false)
1863 %!assert (isa (uint16 (13), "float"), false)
1864 %!assert (isa (uint32 (13), "float"), false)
1865 %!assert (isa (uint64 (13), "float"), false)
1866 %!assert (isa ("char", "numeric"), false)
1867 %!assert (isa (logical (1), "numeric"), false)
1868 %!assert (isa (double (13), "numeric"), true)
1869 %!assert (isa (single (13), "numeric"), true)
1870 %!assert (isa (int8 (13), "numeric"), true)
1871 %!assert (isa (int16 (13), "numeric"), true)
1872 %!assert (isa (int32 (13), "numeric"), true)
1873 %!assert (isa (int64 (13), "numeric"), true)
1874 %!assert (isa (uint8 (13), "numeric"), true)
1875 %!assert (isa (uint16 (13), "numeric"), true)
1876 %!assert (isa (uint32 (13), "numeric"), true)
1877 %!assert (isa (uint64 (13), "numeric"), true)
1878 %!assert (isa (uint8 (13), "integer"), true)
1879 %!assert (isa (double (13), "integer"), false)
1880 %!assert (isa (single (13), "integer"), false)
1881 %!assert (isa (single (13), {"integer", "float", "single"}), [false true true])
1882 
1883 %!assert (isa (double (13), "double"))
1884 %!assert (isa (single (13), "single"))
1885 %!assert (isa (int8 (13), "int8"))
1886 %!assert (isa (int16 (13), "int16"))
1887 %!assert (isa (int32 (13), "int32"))
1888 %!assert (isa (int64 (13), "int64"))
1889 %!assert (isa (uint8 (13), "uint8"))
1890 %!assert (isa (uint16 (13), "uint16"))
1891 %!assert (isa (uint32 (13), "uint32"))
1892 %!assert (isa (uint64 (13), "uint64"))
1893 %!assert (isa ("string", "char"))
1894 %!assert (isa (true, "logical"))
1895 %!assert (isa (false, "logical"))
1896 %!assert (isa ({1, 2}, "cell"))
1897 %!assert (isa ({1, 2}, {"numeric", "integer", "cell"}), [false false true])
1898 
1899 %!testif HAVE_JAVA; usejava ("jvm")
1900 %! ## The first and last assert() are equal on purpose.  The assert() in
1901 %! ## the middle with an invalid class name will cause the java code to
1902 %! ## throw exceptions which we then must clear properly (or all other calls
1903 %! ## will fail).  So we test this too.
1904 %! assert (isa (javaObject ("java.lang.Double", 10), "java.lang.Number"));
1905 %! assert (isa (javaObject ("java.lang.Double", 10), "not_a_class"), false);
1906 %! assert (isa (javaObject ("java.lang.Double", 10), "java.lang.Number"));
1907 
1908 %!test
1909 %! a.b = 1;
1910 %! assert (isa (a, "struct"));
1911 */
1912 
1913 DEFUN (__parent_classes__, args, ,
1914        doc: /* -*- texinfo -*-
1915 @deftypefn {} {} __parent_classes__ (@var{x})
1916 Undocumented internal function.
1917 @end deftypefn */)
1918 {
1919   if (args.length () != 1)
1920     print_usage ();
1921 
1922   octave_value arg = args(0);
1923 
1924   if (arg.isobject ())
1925     return ovl (Cell (arg.parent_class_names ()));
1926   else
1927     return ovl (Cell ());
1928 }
1929 
1930 DEFUN (isobject, args, ,
1931        doc: /* -*- texinfo -*-
1932 @deftypefn {} {} isobject (@var{x})
1933 Return true if @var{x} is a class object.
1934 @seealso{class, typeinfo, isa, ismethod, isprop}
1935 @end deftypefn */)
1936 {
1937   if (args.length () != 1)
1938     print_usage ();
1939 
1940   return ovl (args(0).isobject ());
1941 }
1942 
1943 static bool
is_built_in_class(const std::string & cn)1944 is_built_in_class (const std::string& cn)
1945 {
1946   static std::set<std::string> built_in_class_names;
1947 
1948   if (built_in_class_names.empty ())
1949     {
1950       built_in_class_names.insert ("double");
1951       built_in_class_names.insert ("single");
1952       built_in_class_names.insert ("cell");
1953       built_in_class_names.insert ("struct");
1954       built_in_class_names.insert ("logical");
1955       built_in_class_names.insert ("char");
1956       built_in_class_names.insert ("function handle");
1957       built_in_class_names.insert ("int8");
1958       built_in_class_names.insert ("uint8");
1959       built_in_class_names.insert ("int16");
1960       built_in_class_names.insert ("uint16");
1961       built_in_class_names.insert ("int32");
1962       built_in_class_names.insert ("uint32");
1963       built_in_class_names.insert ("int64");
1964       built_in_class_names.insert ("uint64");
1965     }
1966 
1967   return built_in_class_names.find (cn) != built_in_class_names.end ();
1968 }
1969 
1970 DEFMETHOD (superiorto, interp, args, ,
1971            doc: /* -*- texinfo -*-
1972 @deftypefn {} {} superiorto (@var{class_name}, @dots{})
1973 When called from a class constructor, mark the object currently constructed
1974 as having a higher precedence than @var{class_name}.
1975 
1976 More that one such class can be specified in a single call.  This function
1977 may @emph{only} be called from a class constructor.
1978 @seealso{inferiorto}
1979 @end deftypefn */)
1980 {
1981   octave::tree_evaluator& tw = interp.get_evaluator ();
1982 
1983   octave_function *fcn = tw.caller_function ();
1984 
1985   if (! fcn || ! fcn->is_class_constructor ())
1986     error ("superiorto: invalid call from outside class constructor");
1987 
1988   for (int i = 0; i < args.length (); i++)
1989     {
1990       std::string inf_class = args(i).xstring_value ("superiorto: CLASS_NAME must be a string");
1991 
1992       // User defined classes always have higher precedence
1993       // than built-in classes
1994       if (is_built_in_class (inf_class))
1995         break;
1996 
1997       octave::symbol_table& symtab = interp.get_symbol_table ();
1998 
1999       std::string sup_class = fcn->name ();
2000       if (! symtab.set_class_relationship (sup_class, inf_class))
2001         error ("superiorto: opposite precedence already set for %s and %s",
2002                sup_class.c_str (), inf_class.c_str ());
2003     }
2004 
2005   return ovl ();
2006 }
2007 
2008 DEFMETHOD (inferiorto, interp, args, ,
2009            doc: /* -*- texinfo -*-
2010 @deftypefn {} {} inferiorto (@var{class_name}, @dots{})
2011 When called from a class constructor, mark the object currently constructed
2012 as having a lower precedence than @var{class_name}.
2013 
2014 More that one such class can be specified in a single call.  This function
2015 may @emph{only} be called from a class constructor.
2016 @seealso{superiorto}
2017 @end deftypefn */)
2018 {
2019   octave::tree_evaluator& tw = interp.get_evaluator ();
2020 
2021   octave_function *fcn = tw.caller_function ();
2022 
2023   if (! fcn || ! fcn->is_class_constructor ())
2024     error ("inferiorto: invalid call from outside class constructor");
2025 
2026   for (int i = 0; i < args.length (); i++)
2027     {
2028       std::string sup_class = args(i).xstring_value ("inferiorto: CLASS_NAME must be a string");
2029 
2030       if (is_built_in_class (sup_class))
2031         error ("inferiorto: cannot give user-defined class lower "
2032                "precedence than built-in class");
2033 
2034       octave::symbol_table& symtab = interp.get_symbol_table ();
2035 
2036       std::string inf_class = fcn->name ();
2037       if (! symtab.set_class_relationship (sup_class, inf_class))
2038         error ("inferiorto: opposite precedence already set for %s and %s",
2039                inf_class.c_str (), sup_class.c_str ());
2040     }
2041 
2042   return octave_value();
2043 }
2044 
2045 // The following classes allow us to define "inline" function objects as
2046 // legacy @class objects (as they appear to be in Matlab) while
2047 // preserving the is_inline_function and function_value methods that
2048 // were previously available in the octave_fcn_inline class.  However,
2049 // inline function objects no longer behave as octave_fcn_handle objects
2050 // so calling is_function_handle for them no longer returns true.  I see
2051 // no reasonable way to preserve that behavior.  The goal here is to
2052 // allow most code that used the old octave_inline_fcn object to
2053 // continue to work while eliminating the octave_inline_fcn class that
2054 // was derived from the octave_fcn_handle class.  Making that change
2055 // appears to be necessary to properly fix function handle behavior and
2056 // improve Matlab compatibility.  It's unfortunate if this change causes
2057 // trouble, but I see no better fix.  Ultimately, we should replace all
2058 // uses of "inline" function objects with anonymous functions.
2059 
2060 class octave_inline;
2061 
2062 // The following class can be removed once the
2063 // octave_value::function_value method is removed.
2064 
2065 class
2066 octave_inline_fcn : public octave_function
2067 {
2068 public:
2069 
octave_inline_fcn(octave_inline * obj)2070   octave_inline_fcn (octave_inline *obj) : m_inline_obj (obj) { }
2071 
2072   // No copying!
2073 
2074   octave_inline_fcn (const octave_inline_fcn& ob) = delete;
2075 
2076   octave_inline_fcn& operator = (const octave_inline_fcn& ob) = delete;
2077 
2078   ~octave_inline_fcn (void) = default;
2079 
2080   // Override default call method because we ultimately use feval to
2081   // execute the inline function and that will push a stack frame.
2082 
2083   octave_value_list
call(octave::tree_evaluator & tw,int nargout=0,const octave_value_list & args=octave_value_list ())2084   call (octave::tree_evaluator& tw, int nargout = 0,
2085         const octave_value_list& args = octave_value_list ())
2086   {
2087     return execute (tw, nargout, args);
2088   }
2089 
2090   octave_value_list
2091   execute (octave::tree_evaluator& tw, int nargout = 0,
2092            const octave_value_list& args = octave_value_list ());
2093 
2094 private:
2095 
2096   octave_inline *m_inline_obj;
2097 };
2098 
2099 // Once the octave_inline_fcn class is removed, we should also be able
2100 // to eliminate the octave_inline class below and replace the
2101 // octave_value::is_inline_function method with
2102 //
2103 // bool octave_value::is_inline_function (void) const
2104 // {
2105 //   return class_name () == "inline";
2106 // }
2107 
2108 class
2109 octave_inline : public octave_class
2110 {
2111 public:
2112 
octave_inline(const octave_map & m)2113   octave_inline (const octave_map& m)
2114     : octave_class (m, "inline"), m_fcn_obj (new octave_inline_fcn (this))
2115   { }
2116 
2117   octave_inline (const octave_inline&) = default;
2118 
2119   ~octave_inline (void) = default;
2120 
clone(void) const2121   octave_base_value * clone (void) const { return new octave_inline (*this); }
2122 
empty_clone(void) const2123   octave_base_value * empty_clone (void) const
2124   {
2125     return new octave_inline (octave_map (map_keys ()));
2126   }
2127 
is_inline_function(void) const2128   bool is_inline_function (void) const { return true; }
2129 
function_value(bool)2130   octave_function * function_value (bool)
2131   {
2132     return m_fcn_obj.get ();
2133   }
2134 
2135 private:
2136 
2137   std::shared_ptr<octave_inline_fcn> m_fcn_obj;
2138 };
2139 
2140 octave_value_list
execute(octave::tree_evaluator & tw,int nargout,const octave_value_list & args)2141 octave_inline_fcn::execute (octave::tree_evaluator& tw, int nargout,
2142                             const octave_value_list& args)
2143 {
2144   octave::interpreter& interp = tw.get_interpreter ();
2145 
2146   return interp.feval (octave_value (m_inline_obj, true), args, nargout);
2147 }
2148 
2149 
2150 DEFUN (__inline_ctor__, args, ,
2151        doc: /* -*- texinfo -*-
2152 @deftypefn {} {} __inline_ctor__ (@var{prop_struct})
2153 Internal function.
2154 
2155 Implements final construction for inline objects.
2156 @end deftypefn */)
2157 {
2158   // Input validation has already been done in input.m.
2159 
2160   return octave_value (new octave_inline (args(0).map_value ()));
2161 }
2162