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, ¤t_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