GNU Octave 10.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
 
Loading...
Searching...
No Matches
cdef-object.cc
Go to the documentation of this file.
1////////////////////////////////////////////////////////////////////////
2//
3// Copyright (C) 2012-2025 The Octave Project Developers
4//
5// See the file COPYRIGHT.md in the top-level directory of this
6// distribution or <https://octave.org/copyright/>.
7//
8// This file is part of Octave.
9//
10// Octave is free software: you can redistribute it and/or modify it
11// under the terms of the GNU General Public License as published by
12// the Free Software Foundation, either version 3 of the License, or
13// (at your option) any later version.
14//
15// Octave is distributed in the hope that it will be useful, but
16// WITHOUT ANY WARRANTY; without even the implied warranty of
17// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18// GNU General Public License for more details.
19//
20// You should have received a copy of the GNU General Public License
21// along with Octave; see the file COPYING. If not, see
22// <https://www.gnu.org/licenses/>.
23//
24////////////////////////////////////////////////////////////////////////
25
26#if defined (HAVE_CONFIG_H)
27# include "config.h"
28#endif
29
30#include "cdef-class.h"
31#include "cdef-object.h"
32#include "cdef-property.h"
33#include "cdef-utils.h"
34#include "interpreter.h"
35#include "interpreter-private.h"
36#include "ov-classdef.h"
37
38// Define to 1 to enable debugging statements.
39#define DEBUG_TRACE 0
40
42
43void
45{
46 // We need to be careful to keep a reference to the object if we are
47 // calling the delete method. The object is passed to the delete
48 // method as an argument and if the count is already zero when we
49 // do that, then we will increment the count while creating the
50 // argument list for the delete method and then it will be decremented
51 // back to zero and we'll find ourselves in an infinite loop.
52
53 if (m_count - 1 > static_count ())
54 {
55 --m_count;
56 return;
57 }
58
59 if (is_handle_object () && ! is_meta_object ())
60 {
61 unwind_protect frame;
62
63 // Clear interrupts.
66
67 // Disallow quit().
69 quit_allowed = false;
70
72
73 interpreter_try (frame);
74
75 try
76 {
77 // Call classdef "delete()" method on object
78 get_class ().delete_object (obj);
79 }
80 catch (const interrupt_exception&)
81 {
82 interp.recover_from_exception ();
83
84 warning ("interrupt occurred in handle class delete method");
85 }
86 catch (const execution_exception& ee)
87 {
88 interp.recover_from_exception ();
89
90 std::string msg = ee.message ();
91
92 warning ("error caught while executing handle class delete method:\n%s\n",
93 msg.c_str ());
94 }
95 catch (const exit_exception&)
96 {
97 // This shouldn't happen since we disabled quit above.
98 warning ("exit disabled while executing handle class delete method");
99 }
100 catch (...) // Yes, the black hole. We're in a d-tor.
101 {
102 // This shouldn't happen, in theory.
103 warning ("internal error: unhandled exception in handle class delete method");
104 }
105 }
106
107 // Now it is safe to set the count to zero.
108 m_count--;
109
110 destroy ();
111}
112
115{
116 err_invalid_object ("get_class");
117}
118
119std::string
121{
122 return get_class ().get_name ();
123}
124
127{
128 cdef_class cls = get_class ();
129
130 if (cls.ok ())
131 return cls.get_names ();
132
133 return string_vector ();
134}
135
138{
139 octave_map retval;
140
141 warning_with_id ("Octave:classdef-to-struct",
142 "struct: converting a classdef object into a struct "
143 "overrides the access restrictions defined for properties. "
144 "All properties are returned, including private and "
145 "protected ones.");
146
147 cdef_class cls = get_class ();
148
149 if (cls.ok ())
150 {
151 std::map<property_key, cdef_property> props;
152
154
155 // FIXME: Why not const here?
156 for (auto& prop_val : props)
157 {
158 if (is_array ())
159 {
161
162 Cell cvalue (a_obj.dims ());
163
164 for (octave_idx_type i = 0; i < a_obj.numel (); i++)
165 cvalue (i) = prop_val.second.get_value (a_obj(i), false);
166
167 retval.setfield (prop_val.second.get_name (), cvalue);
168 }
169 else
170 {
171 Cell cvalue (dim_vector (1, 1),
172 prop_val.second.get_value (*this, false));
173
174 retval.setfield (prop_val.second.get_name (), cvalue);
175 }
176 }
177 }
178
179 return retval;
180}
181
184{
185 return m_rep->get_class ();
186}
187
190{
191 return cdef_class (m_klass);
192}
193
194void
196{
197 if ((m_klass.ok () && cls.ok () && cls != get_class ())
198 || (m_klass.ok () && ! cls.ok ())
199 || (! m_klass.ok () && cls.ok ()))
200 {
201 m_klass = cls;
202 }
203}
204
207{
209
210 r->set_class (get_class ());
211
212 return r;
213}
214
216cdef_object_array::subsref (const std::string& type,
217 const std::list<octave_value_list>& idx,
218 int /* nargout */, std::size_t& skip,
219 const cdef_class& /* context */, bool auto_add)
220{
221 octave_value_list retval;
222
223 skip = 1;
224
225 switch (type[0])
226 {
227 case '(':
228 {
229 const octave_value_list& ival = idx.front ();
230
231 if (ival.empty ())
232 {
233 m_count++;
234 retval(0) = to_ov (cdef_object (this));
235 break;
236 }
237
238 bool is_scalar = true;
239 Array<idx_vector> iv (dim_vector (1, ival.length ()));
240
241 for (int i = 0; i < ival.length (); i++)
242 {
243 try
244 {
245 iv(i) = ival(i).index_vector ();
246 }
247 catch (index_exception& ie)
248 {
249 // Rethrow to allow more info to be reported later.
250 ie.set_pos_if_unset (ival.length (), i+1);
251 throw;
252 }
253
254 is_scalar = is_scalar && iv(i).is_scalar ();
255 }
256
257 Array<cdef_object> ires = m_array.index (iv, auto_add);
258
259 // If resizing is enabled (auto_add = true), it's possible
260 // indexing was out-of-bound and the result array contains
261 // invalid cdef_objects.
262
263 if (auto_add)
264 fill_empty_values (ires);
265
266 if (is_scalar)
267 retval(0) = to_ov (ires(0));
268 else
269 {
270 cdef_object array_obj (new cdef_object_array (ires));
271
272 array_obj.set_class (get_class ());
273
274 retval(0) = to_ov (array_obj);
275 }
276 }
277 break;
278
279 case '.':
280 if (type.size () == 1 && idx.size () == 1)
281 {
282 Cell c (dims ());
283
284 octave_idx_type n = m_array.numel ();
285
286 // dummy variables
287 std::size_t dummy_skip;
288 cdef_class dummy_cls;
289
290 for (octave_idx_type i = 0; i < n; i++)
291 {
292 octave_value_list r = m_array(i).subsref (type, idx, 1,
293 dummy_skip,
294 dummy_cls);
295
296 if (r.length () > 0)
297 c(i) = r(0);
298 }
299
300 retval(0) = octave_value (c, true);
301
302 break;
303 }
304 OCTAVE_FALLTHROUGH;
305
306 default:
307 error ("can't perform indexing operation on array of %s objects",
308 class_name ().c_str ());
309 break;
310 }
311
312 return retval;
313}
314
316cdef_object_array::subsasgn (const std::string& type,
317 const std::list<octave_value_list>& idx,
318 const octave_value& rhs)
319{
320 octave_value retval;
321
322 switch (type[0])
323 {
324 case '(':
325 if (type.length () == 1)
326 {
327 cdef_object rhs_obj = to_cdef (rhs);
328
329 if (rhs_obj.get_class () != get_class ())
330 error ("can't assign %s object into array of %s objects",
331 rhs_obj.class_name ().c_str (),
332 class_name ().c_str ());
333
334 const octave_value_list& ival = idx.front ();
335 bool is_scalar = true;
336 Array<idx_vector> iv (dim_vector (1, ival.length ()));
337
338 for (int i = 0; i < ival.length (); i++)
339 {
340 try
341 {
342 iv(i) = ival(i).index_vector ();
343 }
344 catch (index_exception& ie)
345 {
346 ie.set_pos_if_unset (ival.length (), i+1);
347 throw; // var name set in pt-idx.cc / pt-assign.cc
348 }
349
350 is_scalar = is_scalar && iv(i).is_scalar ();
351 }
352
353 Array<cdef_object> rhs_mat;
354
355 if (! rhs_obj.is_array ())
356 {
357 rhs_mat = Array<cdef_object> (dim_vector (1, 1));
358 rhs_mat(0) = rhs_obj;
359 }
360 else
361 rhs_mat = rhs_obj.array_value ();
362
363 octave_idx_type n = m_array.numel ();
364
365 m_array.assign (iv, rhs_mat, cdef_object ());
366
367 if (m_array.numel () > n)
368 fill_empty_values ();
369
370 m_count++;
371 retval = to_ov (cdef_object (this));
372 }
373 else
374 {
375 const octave_value_list& ivl = idx.front ();
376
377 // Fill in trailing singleton dimensions so that
378 // array.index doesn't create a new blank entry (bug #46660).
379 const octave_idx_type one = static_cast<octave_idx_type> (1);
380 const octave_value_list& ival = ivl.length () >= 2
381 ? ivl : ((m_array.dims ()(0) == 1)
382 ? ovl (one, ivl(0))
383 : ovl (ivl(0), one));
384
385 bool is_scalar = true;
386
387 Array<idx_vector> iv (dim_vector (1, ival.length ()));
388
389 for (int i = 0; i < ival.length (); i++)
390 {
391 try
392 {
393 iv(i) = ival(i).index_vector ();
394 }
395 catch (index_exception& ie)
396 {
397 // Rethrow to allow more info to be reported later.
398 ie.set_pos_if_unset (ival.length (), i+1);
399 throw;
400 }
401
402 is_scalar = is_scalar && iv(i).is_scalar ();
403
404 if (! is_scalar)
405 error ("subsasgn: invalid indexing for object array assignment"
406 ", the index must reference a single object in the "
407 "array.");
408 }
409
410 Array<cdef_object> a = m_array.index (iv, true);
411
412 if (a.numel () != 1)
413 error ("subsasgn: invalid indexing for object array assignment");
414
415 cdef_object obj = a(0);
416
417 int ignore_copies = 0;
418
419 // If the object in 'a' is not valid, this means the index
420 // was out-of-bound and we need to create a new object.
421
422 if (! obj.ok ())
424 else
425 // Optimize the subsasgn call to come. There are 2 copies
426 // that we can safely ignore:
427 // - 1 in "array"
428 // - 1 in "a"
429 ignore_copies = 2;
430
431 std::list<octave_value_list> next_idx (idx);
432
433 next_idx.erase (next_idx.begin ());
434
435 octave_value tmp = obj.subsasgn (type.substr (1), next_idx,
436 rhs, ignore_copies);
437
438 cdef_object robj = to_cdef (tmp);
439
440 if (! robj.ok ()
441 || robj.is_array ()
442 || robj.get_class () != get_class ())
443 error ("subsasgn: invalid assignment into array of %s objects",
444 class_name ().c_str ());
445
446 // Small optimization, when dealing with handle
447 // objects, we don't need to re-assign the result
448 // of subsasgn back into the array.
449
450 if (! robj.is (a(0)))
451 {
452 Array<cdef_object> rhs_a (dim_vector (1, 1),
453 robj);
454
455 octave_idx_type n = m_array.numel ();
456
457 m_array.assign (iv, rhs_a);
458
459 if (m_array.numel () > n)
460 fill_empty_values ();
461 }
462
463 m_count++;
464
465 retval = to_ov (cdef_object (this));
466 }
467 break;
468
469 default:
470 error ("can't perform indexing operation on array of %s objects",
471 class_name ().c_str ());
472 break;
473 }
474
475 return retval;
476}
477
478void
479cdef_object_array::fill_empty_values (Array<cdef_object>& arr)
480{
481 cdef_class cls = get_class ();
482
483 cdef_object obj;
484
485 int n = arr.numel ();
486
487 for (int i = 0; i < n; i++)
488 {
489 if (! arr.xelem (i).ok ())
490 {
491 if (! obj.ok ())
492 {
493 obj = cls.construct_object (octave_value_list ());
494
495 arr.xelem (i) = obj;
496 }
497 else
498 arr.xelem (i) = obj.copy ();
499 }
500 }
501}
502
503void
504cdef_object_scalar::break_closure_cycles (const std::shared_ptr<stack_frame>& frame)
505{
506 for (octave_idx_type i = 0; i < m_map.nfields (); i++)
508}
509
511cdef_object_scalar::subsref (const std::string& type,
512 const std::list<octave_value_list>& idx,
513 int nargout, std::size_t& skip,
514 const cdef_class& context, bool auto_add)
515{
516 skip = 0;
517
518 cdef_class cls = (context.ok () ? context : get_class ());
519
520 octave_value_list retval;
521
522 if (! cls.ok ())
523 return retval;
524
525 switch (type[0])
526 {
527 case '.':
528 {
529 std::string name = (idx.front ())(0).string_value ();
530
531 cdef_method meth = cls.find_method (name);
532
533 if (meth.ok ())
534 {
535 int _nargout = (type.length () > 2 ? 1 : nargout);
536
538
539 skip = 1;
540
541 if (type.length () > 1 && type[1] == '(')
542 {
543 auto it = idx.begin ();
544
545 args = *++it;
546
547 skip++;
548 }
549
550 if (meth.is_static ())
551 retval = meth.execute (args, _nargout, true, "subsref");
552 else
553 {
554 m_count++;
555 retval = meth.execute (cdef_object (this), args, _nargout,
556 true, "subsref");
557 }
558 }
559
560 if (skip == 0)
561 {
562 cdef_property prop = cls.find_property (name);
563
564 if (! prop.ok ())
565 error ("subsref: unknown method or property: %s", name.c_str ());
566
567 if (prop.is_constant ())
568 retval(0) = prop.get_value (true, "subsref");
569 else
570 {
571 m_count++;
572 retval(0) = prop.get_value (cdef_object (this),
573 true, "subsref");
574 }
575
576 skip = 1;
577 }
578 break;
579 }
580
581 case '(':
582 {
583 const octave_value_list& ival = idx.front ();
584
585 m_count++;
586 cdef_object this_obj (this);
587
588 if (ival.empty ())
589 {
590 skip++;
591 retval(0) = to_ov (this_obj);
592 }
593 else
594 {
595 Array<cdef_object> arr (dim_vector (1, 1), this_obj);
596
597 cdef_object new_obj = cdef_object (new cdef_object_array (arr));
598
599 new_obj.set_class (get_class ());
600
601 retval = new_obj.subsref (type, idx, nargout, skip, cls, auto_add);
602 }
603 }
604 break;
605
606 default:
607 error ("object cannot be indexed with '%c'", type[0]);
608 break;
609 }
610
611 return retval;
612}
613
615cdef_object_scalar::subsasgn (const std::string& type,
616 const std::list<octave_value_list>& idx,
617 const octave_value& rhs)
618{
619 octave_value retval;
620
621 cdef_class cls = get_class ();
622
623 switch (type[0])
624 {
625 case '.':
626 {
627 std::string name = (idx.front ())(0).string_value ();
628
629 cdef_property prop = cls.find_property (name);
630
631 if (! prop.ok ())
632 error ("subsasgn: unknown property: %s", name.c_str ());
633
634 if (prop.is_constant ())
635 error ("subsasgn: cannot assign constant property: %s",
636 name.c_str ());
637
638 m_count++;
639
640 cdef_object obj (this);
641
642 if (type.length () == 1)
643 {
644 prop.set_value (obj, rhs, true, "subsasgn");
645
646 retval = to_ov (obj);
647 }
648 else
649 {
650 octave_value val = prop.get_value (obj, true, "subsasgn");
651
652 std::list<octave_value_list> args (idx);
653
654 args.erase (args.begin ());
655
657 type.substr (1), args, rhs);
658
659 if (val.class_name () != "object"
660 || ! to_cdef (val).is_handle_object ())
661 prop.set_value (obj, val, true, "subsasgn");
662
663 retval = to_ov (obj);
664 }
665 }
666 break;
667
668 case '(':
669 {
670 m_count++;
671
672 cdef_object this_obj (this);
673
674 Array<cdef_object> arr (dim_vector (1, 1), this_obj);
675
676 cdef_object new_obj = cdef_object (new cdef_object_array (arr));
677
678 new_obj.set_class (get_class ());
679
680 octave_value tmp = new_obj.subsasgn (type, idx, rhs);
681
682 retval = tmp;
683 }
684 break;
685
686 default:
687 error ("subsasgn: object cannot be index with '%c'", type[0]);
688 break;
689 }
690
691 return retval;
692}
693
694void
696{
697 std::string cls_name = cls.get_name ();
698
699 Cell supcls = cls.get ("SuperClasses").cell_value ();
700
701 std::list<cdef_class> supcls_list = lookup_classes (supcls);
702
703 m_ctor_list[cls] = supcls_list;
704}
705
706bool
708{
709 return (is_constructed ()
710 || m_ctor_list.find (cls) == m_ctor_list.end ());
711}
712
713bool
715{
716 if (is_constructed ())
717 return true;
718
719 std::map<cdef_class, std::list<cdef_class>>::const_iterator it
720 = m_ctor_list.find (cls);
721
722 if (it == m_ctor_list.end () || it->second.empty ())
723 return true;
724
725 for (const auto& cdef_cls : it->second)
726 if (! is_partially_constructed_for (cdef_cls))
727 return false;
728
729 return true;
730}
731
732void
734{
735 m_ctor_list.erase (cls);
736}
737
739{
740#if DEBUG_TRACE
741 std::cerr << "deleting " << get_class ().get_name ()
742 << " object (handle)" << std::endl;
743#endif
744}
745
747{
748#if DEBUG_TRACE
749 std::cerr << "deleting " << get_class ().get_name ()
750 << " object (value)" << std::endl;
751#endif
752}
753
754OCTAVE_END_NAMESPACE(octave)
bool is_scalar(const dim_vector &dim)
cdef_object to_cdef(const octave_value &val)
octave_value to_ov(const cdef_object &obj)
std::list< cdef_class > lookup_classes(const Cell &cls_list)
N Dimensional Array with copy-on-write semantics.
Definition Array.h:130
const dim_vector & dims() const
Return a const-reference so that dims ()(i) works efficiently.
Definition Array.h:507
T & xelem(octave_idx_type n)
Size of the specified dimension.
Definition Array.h:525
Array< T, Alloc > index(const octave::idx_vector &i) const
Indexing without resizing.
void assign(const octave::idx_vector &i, const Array< T, Alloc > &rhs, const T &rfv)
Indexed assignment (always with resize & fill).
octave_idx_type numel() const
Number of elements in the array.
Definition Array.h:418
Definition Cell.h:41
void protect_var(T &var)
cdef_method find_method(const std::string &nm, bool local=false)
Definition cdef-class.h:468
string_vector get_names()
Definition cdef-class.h:313
std::map< property_key, cdef_property > get_property_map(int mode=property_normal)
Definition cdef-class.h:308
std::string get_name() const
Definition cdef-class.h:329
void delete_object(const cdef_object &obj)
Definition cdef-class.h:333
cdef_property find_property(const std::string &nm)
Definition cdef-class.h:474
cdef_object construct_object(const octave_value_list &args)
Definition cdef-class.h:384
octave_value_list execute(const octave_value_list &args, int nargout, bool do_check_access=true, const std::string &who="")
bool is_static() const
dim_vector dims() const
octave_value_list subsref(const std::string &type, const std::list< octave_value_list > &idx, int nargout, std::size_t &skip, const cdef_class &context, bool auto_add)
octave_value subsasgn(const std::string &type, const std::list< octave_value_list > &idx, const octave_value &rhs)
cdef_class get_class() const
void set_class(const cdef_class &cls)
cdef_object_rep * make_array() const
void release(const cdef_object &obj)
virtual void set_class(const cdef_class &)
Definition cdef-object.h:62
virtual string_vector map_keys() const
virtual bool is_meta_object() const
Definition cdef-object.h:93
virtual void destroy()
virtual bool is_handle_object() const
Definition cdef-object.h:91
std::string class_name() const
virtual octave_idx_type static_count() const
virtual cdef_class get_class() const
refcount< octave_idx_type > m_count
octave_scalar_map m_map
octave_value subsasgn(const std::string &type, const std::list< octave_value_list > &idx, const octave_value &rhs)
std::map< cdef_class, std::list< cdef_class > > m_ctor_list
bool is_constructed() const
void mark_for_construction(const cdef_class &)
bool is_partially_constructed_for(const cdef_class &cls) const
void break_closure_cycles(const std::shared_ptr< stack_frame > &frame)
bool is_constructed_for(const cdef_class &cls) const
octave_value_list subsref(const std::string &type, const std::list< octave_value_list > &idx, int nargout, std::size_t &skip, const cdef_class &context, bool auto_add)
bool is_array() const
cdef_class get_class() const
octave_map map_value() const
bool ok() const
void set_class(const cdef_class &cls)
std::string class_name() const
octave_value subsasgn(const std::string &type, const std::list< octave_value_list > &idx, const octave_value &rhs, int ignore_copies=0)
cdef_object copy() const
bool is(const cdef_object &obj) const
octave_value get(const std::string &pname) const
octave_value_list subsref(const std::string &type, const std::list< octave_value_list > &idx, int nargout, std::size_t &skip, const cdef_class &context, bool auto_add=false)
Array< cdef_object > array_value() const
void set_value(cdef_object &obj, const octave_value &val, bool do_check_access=true, const std::string &who="")
bool is_constant() const
octave_value get_value(const cdef_object &obj, bool do_check_access=true, const std::string &who="") const
Vector representing the dimensions (size) of an Array.
Definition dim-vector.h:90
void set_pos_if_unset(octave_idx_type nd_arg, octave_idx_type dim_arg)
void recover_from_exception()
void setfield(const std::string &key, const Cell &val)
Definition oct-map.cc:282
const octave_value & contents(const_iterator p) const
Definition oct-map.h:197
octave_idx_type nfields() const
Definition oct-map.h:210
bool empty() const
Definition ovl.h:113
octave_idx_type length() const
Definition ovl.h:111
std::string class_name() const
Definition ov.h:1362
Cell cell_value() const
void break_closure_cycles(const std::shared_ptr< octave::stack_frame > &)
@ op_asn_eq
Definition ov.h:135
octave_value & assign(assign_op op, const std::string &type, const std::list< octave_value_list > &idx, const octave_value &rhs)
OCTAVE_BEGIN_NAMESPACE(octave) static octave_value daspk_fcn
void interpreter_try(unwind_protect &frame)
Definition error.cc:2198
void warning(const char *fmt,...)
Definition error.cc:1078
void warning_with_id(const char *id, const char *fmt,...)
Definition error.cc:1093
void error(const char *fmt,...)
Definition error.cc:1003
interpreter & __get_interpreter__()
bool quit_allowed
octave_value_list ovl(const OV_Args &... args)
Construct an octave_value_list with less typing.
Definition ovl.h:217
std::atomic< sig_atomic_t > octave_interrupt_state
Definition quit.cc:39