GNU Octave 10.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
 
Loading...
Searching...
No Matches
stack-frame.cc
Go to the documentation of this file.
1////////////////////////////////////////////////////////////////////////
2//
3// Copyright (C) 1995-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 <iostream>
31
32#include "lo-regexp.h"
33#include "lo-sysdep.h"
34#include "str-vec.h"
35
36#include "defun.h"
37#include "error.h"
38#include "interpreter.h"
39#include "interpreter-private.h"
40#include "oct-map.h"
41#include "ov.h"
42#include "ov-fcn.h"
43#include "ov-fcn-handle.h"
44#include "ov-usr-fcn.h"
45#include "pager.h"
46#include "parse.h"
47#include "pt-eval.h"
48#include "stack-frame.h"
49#include "syminfo.h"
50#include "symrec.h"
51#include "symscope.h"
52#include "utils.h"
53#include "variables.h"
54
56
57// FIXME: There should probably be a display method for the script,
58// fcn, and scope objects and the script and function objects should
59// be responsible for displaying the scopes they contain.
60
61static void display_scope (std::ostream& os, const symbol_scope& scope)
62{
63 if (scope)
64 {
65 os << "scope: " << scope.name () << std::endl;
66
67 if (scope.num_symbols () > 0)
68 {
69 os << "name (frame offset, data offset, storage class):"
70 << std::endl;
71
72 std::list<symbol_record> symbols = scope.symbol_list ();
73
74 for (auto& sym : symbols)
75 {
76 os << " " << sym.name () << " (" << sym.frame_offset ()
77 << ", " << sym.data_offset () << ", " << sym.storage_class ()
78 << ")" << std::endl;
79 }
80 }
81 }
82}
83
84class compiled_fcn_stack_frame;
85class script_stack_frame;
86class user_fcn_stack_frame;
87class scope_stack_frame;
88
89class stack_frame_walker
90{
91protected:
92
93 stack_frame_walker () { }
94
95 virtual ~stack_frame_walker () = default;
96
97public:
98
99 OCTAVE_DISABLE_COPY_MOVE (stack_frame_walker)
100
101 virtual void
102 visit_compiled_fcn_stack_frame (compiled_fcn_stack_frame&) = 0;
103
104 virtual void
105 visit_script_stack_frame (script_stack_frame&) = 0;
106
107 virtual void
108 visit_user_fcn_stack_frame (user_fcn_stack_frame&) = 0;
109
110 virtual void
111 visit_scope_stack_frame (scope_stack_frame&) = 0;
112};
113
114class compiled_fcn_stack_frame : public stack_frame
115{
116public:
117
118 compiled_fcn_stack_frame () = delete;
119
120 compiled_fcn_stack_frame (tree_evaluator& tw, octave_function *fcn,
121 std::size_t index,
122 const std::shared_ptr<stack_frame>& parent_link,
123 const std::shared_ptr<stack_frame>& static_link)
126 m_fcn (fcn)
127 { }
128
129 compiled_fcn_stack_frame (const compiled_fcn_stack_frame& elt) = default;
130
131 compiled_fcn_stack_frame&
132 operator = (const compiled_fcn_stack_frame& elt) = delete;
133
134 ~compiled_fcn_stack_frame () = default;
135
136 bool is_compiled_fcn_frame () const { return true; }
137
138 symbol_scope get_scope () const
139 {
140 return m_static_link->get_scope ();
141 }
142
143 octave_function * function () const { return m_fcn; }
144
145 symbol_record lookup_symbol (const std::string& name) const
146 {
147 return m_static_link->lookup_symbol (name);
148 }
149
150 symbol_record insert_symbol (const std::string& name)
151 {
152 return m_static_link->insert_symbol (name);
153 }
154
155 stack_frame::scope_flags scope_flag (const symbol_record& sym) const
156 {
157 // Look in closest stack frame that contains values (either the
158 // top scope, or a user-defined function or script).
159
160 return m_static_link->scope_flag (sym);
161 }
162
163 void set_auto_fcn_var (auto_var_type avt, const octave_value& val)
164 {
165 m_static_link->set_auto_fcn_var (avt, val);
166 }
167
168 octave_value get_auto_fcn_var (auto_var_type avt) const
169 {
170 return m_static_link->get_auto_fcn_var (avt);
171 }
172
173 // We only need to override one of each of these functions. The
174 // using declaration will avoid warnings about partially-overloaded
175 // virtual functions.
178
179 octave_value varval (const symbol_record& sym) const
180 {
181 // Look in closest stack frame that contains values (either the
182 // top scope, or a user-defined function or script).
183
184 return m_static_link->varval (sym);
185 }
186
187 octave_value& varref (const symbol_record& sym)
188 {
189 // Look in closest stack frame that contains values (either the
190 // top scope, or a user-defined function or script).
191
192 return m_static_link->varref (sym);
193 }
194
195 std::string inputname (int n, bool ids_only) const
196 {
197 // Look in closest stack frame that contains values (either the
198 // top scope, or a user-defined function or script).
199
200 return m_static_link->inputname (n, ids_only);
201 }
202
203 void mark_scope (const symbol_record& sym, scope_flags flag)
204 {
205 // Look in closest stack frame that contains values (either the
206 // top scope, or a user-defined function or script).
207
208 m_static_link->mark_scope (sym, flag);
209 }
210
211 void display (bool follow = true) const;
212
213 void accept (stack_frame_walker& sfw);
214
215private:
216
217 // Compiled function object associated with this stack frame.
218 // Should always be a built-in, .oct or .mex file function and
219 // should always be valid.
220 octave_function *m_fcn;
221};
222
223// Scripts have a symbol_scope object to store the set of variables
224// in the script, but values for those variables are stored in the
225// stack frame corresponding to the nearest calling function or in
226// the top-level scope (the evaluation stack frame).
227//
228// Accessing values in a scope requires a mapping from the index of
229// the variable for the script scope to the list of values in the
230// evaluation frame(s). The frame offset tells us how many access
231// links we must follow to find the stack frame that holds the
232// value. The value offset is the index into the vector of values
233// in that stack frame that we should use to find the value.
234//
235// Frame and value offsets are set in this stack frame when it is
236// created using information from the script and enclosing scopes.
237//
238// If a script is invoked in a nested function context, the frame
239// offsets for individual values may be different. Some may be
240// accessed from the invoking function and some may come from a
241// parent function.
242
243class script_stack_frame : public stack_frame
244{
245public:
246
247 script_stack_frame () = delete;
248
249 script_stack_frame (tree_evaluator& tw, octave_user_script *script,
250 std::size_t index,
251 const std::shared_ptr<stack_frame>& parent_link,
252 const std::shared_ptr<stack_frame>& static_link);
253
254 script_stack_frame (const script_stack_frame& elt) = default;
255
256 script_stack_frame& operator = (const script_stack_frame& elt) = delete;
257
258 ~script_stack_frame ()
259 {
260 delete m_unwind_protect_frame;
261 }
262
263 bool is_user_script_frame () const { return true; }
264
265 static std::shared_ptr<stack_frame>
266 get_access_link (const std::shared_ptr<stack_frame>& static_link);
267
268 static std::size_t get_num_symbols (octave_user_script *script);
269
270 void set_script_offsets ();
271
272 void set_script_offsets_internal (const std::map<std::string,
273 symbol_record>& symbols);
274
275 void resize_and_update_script_offsets (const symbol_record& sym);
276
277 symbol_scope get_scope () const { return m_script->scope (); }
278
279 octave_function * function () const { return m_script; }
280
281 unwind_protect * unwind_protect_frame ();
282
283 symbol_record lookup_symbol (const std::string& name) const;
284
285 symbol_record insert_symbol (const std::string&);
286
287 std::size_t size () const { return m_lexical_frame_offsets.size (); }
288
289 void resize (std::size_t size)
290 {
291 m_lexical_frame_offsets.resize (size, 0);
292 m_value_offsets.resize (size, 0);
293 }
294
295 void get_val_offsets_with_insert (const symbol_record& sym,
296 std::size_t& frame_offset,
297 std::size_t& data_offset);
298
299 bool get_val_offsets_internal (const symbol_record& sym,
300 std::size_t& frame_offset,
301 std::size_t& data_offset) const;
302
303 bool get_val_offsets (const symbol_record& sym, std::size_t& frame_offset,
304 std::size_t& data_offset) const;
305
306 scope_flags scope_flag (const symbol_record& sym) const;
307
308 void set_auto_fcn_var (auto_var_type avt, const octave_value& val)
309 {
310 m_access_link->set_auto_fcn_var (avt, val);
311 }
312
313 octave_value get_auto_fcn_var (auto_var_type avt) const
314 {
315 return m_access_link->get_auto_fcn_var (avt);
316 }
317
318 // We only need to override one of each of these functions. The
319 // using declaration will avoid warnings about partially-overloaded
320 // virtual functions.
323
324 octave_value varval (const symbol_record& sym) const;
325
326 octave_value& varref (const symbol_record& sym);
327
328 std::string inputname (int n, bool ids_only) const
329 {
330 return m_access_link->inputname (n, ids_only);
331 }
332
333 void mark_scope (const symbol_record& sym, scope_flags flag);
334
335 void display (bool follow = true) const;
336
337 void accept (stack_frame_walker& sfw);
338
339private:
340
341 // Script object associated with this stack frame. Should always
342 // be valid.
343 octave_user_script *m_script;
344
345 // The nearest unwind protect frame that was active when this
346 // stack frame was created. Should always be valid.
347 unwind_protect *m_unwind_protect_frame;
348
349 // Mapping between the symbols in the symbol_scope object of the
350 // script to the stack frame in which the script is executed. The
351 // frame offsets may be greater than one if the script is executed
352 // in a nested function context.
353
354 std::vector<std::size_t> m_lexical_frame_offsets;
355 std::vector<std::size_t> m_value_offsets;
356};
357
358// Base class for values and offsets shared by user_fcn and scope
359// frames.
360
361class base_value_stack_frame : public stack_frame
362{
363public:
364
365 base_value_stack_frame () = delete;
366
367 base_value_stack_frame (tree_evaluator& tw, std::size_t num_symbols,
368 std::size_t index,
369 const std::shared_ptr<stack_frame>& parent_link,
370 const std::shared_ptr<stack_frame>& static_link,
371 const std::shared_ptr<stack_frame>& access_link)
373 m_values (num_symbols, octave_value ()),
374 m_flags (num_symbols, LOCAL),
375 m_auto_vars (NUM_AUTO_VARS, octave_value ())
376 { }
377
378 base_value_stack_frame (const base_value_stack_frame& elt) = default;
379
380 base_value_stack_frame&
381 operator = (const base_value_stack_frame& elt) = delete;
382
383 ~base_value_stack_frame ()
384 {
385 // The C++ standard doesn't guarantee in which order the elements of a
386 // std::vector are destroyed. GNU libstdc++ and LLVM libc++ seem to
387 // destroy them in a different order. So, erase elements manually
388 // from first to last to be able to guarantee a destructor call order
389 // independent of the used STL, e.g., for classdef objects.
390
391 // Member dtor order is last to first. So, m_auto_vars before m_values.
392
393 for (auto auto_vars_iter = m_auto_vars.begin ();
394 auto_vars_iter != m_auto_vars.end ();)
395 auto_vars_iter = m_auto_vars.erase (auto_vars_iter);
396
397 for (auto values_iter = m_values.begin ();
398 values_iter != m_values.end ();)
399 values_iter = m_values.erase (values_iter);
400 }
401
402 std::size_t size () const
403 {
404 return m_values.size ();
405 }
406
407 void resize (std::size_t size)
408 {
409 m_values.resize (size, octave_value ());
410 m_flags.resize (size, LOCAL);
411 }
412
413 stack_frame::scope_flags get_scope_flag (std::size_t data_offset) const
414 {
415 return m_flags.at (data_offset);
416 }
417
418 void set_scope_flag (std::size_t data_offset, scope_flags flag)
419 {
420 m_flags.at (data_offset) = flag;
421 }
422
423 octave_value get_auto_fcn_var (auto_var_type avt) const
424 {
425 return m_auto_vars.at (avt);
426 }
427
428 void set_auto_fcn_var (auto_var_type avt, const octave_value& val)
429 {
430 m_auto_vars.at (avt) = val;
431 }
432
433 // We only need to override one of each of these functions. The
434 // using declaration will avoid warnings about partially-overloaded
435 // virtual functions.
438
439 octave_value varval (std::size_t data_offset) const
440 {
441 return m_values.at (data_offset);
442 }
443
444 octave_value& varref (std::size_t data_offset)
445 {
446 return m_values.at (data_offset);
447 }
448
449 void display (bool follow = true) const;
450
451protected:
452
453 // Variable values. This array is indexed by the data_offset
454 // value stored in the symbol_record objects of the scope
455 // associated with this stack frame.
456 std::vector<octave_value> m_values;
457
458 // The type of each variable (local, global, persistent) of each
459 // value. This array is indexed by the data_offset value stored
460 // in the symbol_record objects of the scope associated with this
461 // stack frame. Local values are found in the M_VALUES array.
462 // Global values are stored in the tree_evaluator object that contains
463 // the stack frame. Persistent values are stored in the function
464 // scope corresponding to the stack frame.
465 std::vector<scope_flags> m_flags;
466
467 // A fixed list of Automatic variables created for this function.
468 // The elements of this vector correspond to the auto_var_type
469 // enum.
470 std::vector<octave_value> m_auto_vars;
471};
472
473// User-defined functions have a symbol_scope object to store the set
474// of variables in the function and values are stored in the stack
475// frame corresponding to the invocation of the function or one of
476// its parents. The frame offset tells us how many access links we
477// must follow to find the stack frame that holds the value. The
478// value offset is the index into the vector of values in that stack
479// frame that we should use to find the value.
480//
481// Frame and value offsets are determined when the corresponding
482// function is parsed.
483
484class user_fcn_stack_frame : public base_value_stack_frame
485{
486public:
487
488 user_fcn_stack_frame () = delete;
489
490 user_fcn_stack_frame (tree_evaluator& tw, octave_user_function *fcn,
491 std::size_t index,
492 const std::shared_ptr<stack_frame>& parent_link,
493 const std::shared_ptr<stack_frame>& static_link,
494 const std::shared_ptr<stack_frame>& access_link = std::shared_ptr<stack_frame> ())
495 : base_value_stack_frame (tw, get_num_symbols (fcn), index,
499 : get_access_link (fcn, static_link))),
500 m_fcn (fcn), m_unwind_protect_frame (nullptr)
501 { }
502
503 user_fcn_stack_frame (tree_evaluator& tw, octave_user_function *fcn,
504 std::size_t index,
505 const std::shared_ptr<stack_frame>& parent_link,
506 const std::shared_ptr<stack_frame>& static_link,
507 const local_vars_map& local_vars,
508 const std::shared_ptr<stack_frame>& access_link = std::shared_ptr<stack_frame> ())
509 : base_value_stack_frame (tw, get_num_symbols (fcn), index,
513 : get_access_link (fcn, static_link))),
514 m_fcn (fcn), m_unwind_protect_frame (nullptr)
515 {
516 // Initialize local variable values.
517
518 for (const auto& nm_ov : local_vars)
519 assign (nm_ov.first, nm_ov.second);
520 }
521
522 user_fcn_stack_frame (const user_fcn_stack_frame& elt) = default;
523
524 user_fcn_stack_frame&
525 operator = (const user_fcn_stack_frame& elt) = delete;
526
527 ~user_fcn_stack_frame ()
528 {
529 delete m_unwind_protect_frame;
530 }
531
532 bool is_user_fcn_frame () const { return true; }
533
534 static std::shared_ptr<stack_frame>
535 get_access_link (octave_user_function *fcn,
536 const std::shared_ptr<stack_frame>& static_link);
537
538 static std::size_t get_num_symbols (octave_user_function *fcn)
539 {
540 symbol_scope fcn_scope = fcn->scope ();
541
542 return fcn_scope.num_symbols ();
543 }
544
545 void clear_values ();
546
547 symbol_scope get_scope () const { return m_fcn->scope (); }
548
549 octave_function * function () const { return m_fcn; }
550
551 unwind_protect * unwind_protect_frame ();
552
553 symbol_record lookup_symbol (const std::string& name) const;
554
555 symbol_record insert_symbol (const std::string&);
556
557 scope_flags scope_flag (const symbol_record& sym) const;
558
559 // We only need to override one of each of these functions. The
560 // using declaration will avoid warnings about partially-overloaded
561 // virtual functions.
562 using base_value_stack_frame::varval;
563 using base_value_stack_frame::varref;
564
565 octave_value varval (const symbol_record& sym) const;
566
567 octave_value& varref (const symbol_record& sym);
568
569 std::string inputname (int n, bool ids_only) const;
570
571 void mark_scope (const symbol_record& sym, scope_flags flag);
572
573 void display (bool follow = true) const;
574
575 void accept (stack_frame_walker& sfw);
576
577 void break_closure_cycles (const std::shared_ptr<stack_frame>& frame);
578
579private:
580
581 // User-defined object associated with this stack frame. Should
582 // always be valid.
584
585 // The nearest unwind protect frame that was active when this
586 // stack frame was created. Should always be valid.
587 unwind_protect *m_unwind_protect_frame;
588};
589
590// Pure scope stack frames (primarily the top-level workspace) have
591// a set of variables and values are stored in the stack frame. All
592// variable accesses are direct as there are no parent stack frames.
593//
594// Value offsets are determined when the corresponding variable is
595// entered into the symbol_scope object corresponding to the frame.
596
597class scope_stack_frame : public base_value_stack_frame
598{
599public:
600
601 scope_stack_frame () = delete;
602
603 scope_stack_frame (tree_evaluator& tw, const symbol_scope& scope,
604 std::size_t index,
605 const std::shared_ptr<stack_frame>& parent_link,
606 const std::shared_ptr<stack_frame>& static_link)
607 : base_value_stack_frame (tw, scope.num_symbols (), index,
608 parent_link, static_link, nullptr),
609 m_scope (scope)
610 { }
611
612 scope_stack_frame (const scope_stack_frame& elt) = default;
613
614 scope_stack_frame& operator = (const scope_stack_frame& elt) = delete;
615
616 ~scope_stack_frame () = default;
617
618 bool is_scope_frame () const { return true; }
619
620 symbol_scope get_scope () const { return m_scope; }
621
622 symbol_record lookup_symbol (const std::string& name) const
623 {
624 return m_scope.lookup_symbol (name);
625 }
626
627 symbol_record insert_symbol (const std::string&);
628
629 scope_flags scope_flag (const symbol_record& sym) const;
630
631 // We only need to override one of each of these functions. The
632 // using declaration will avoid warnings about partially-overloaded
633 // virtual functions.
634 using base_value_stack_frame::varval;
635 using base_value_stack_frame::varref;
636
637 octave_value varval (const symbol_record& sym) const;
638
639 octave_value& varref (const symbol_record& sym);
640
641 std::string inputname (int, bool) const
642 {
643 if (m_index == 0)
644 error ("invalid call to inputname outside of a function");
645
646 return "";
647 }
648
649 void mark_scope (const symbol_record& sym, scope_flags flag);
650
651 void display (bool follow = true) const;
652
653 void accept (stack_frame_walker& sfw);
654
655private:
656
657 // The scope object associated with this stack frame.
658 symbol_scope m_scope;
659};
660
661class symbol_cleaner : public stack_frame_walker
662{
663public:
664
665 symbol_cleaner (const std::string& pattern, bool have_regexp = false)
666 : stack_frame_walker (), m_patterns (pattern),
667 m_clear_all_names (false), m_clear_objects (false),
668 m_have_regexp (have_regexp), m_cleared_names ()
669 { }
670
671 symbol_cleaner (const string_vector& patterns, bool have_regexp = false)
672 : stack_frame_walker (), m_patterns (patterns),
673 m_clear_all_names (false), m_clear_objects (false),
674 m_have_regexp (have_regexp), m_cleared_names ()
675 { }
676
677 symbol_cleaner (bool clear_all_names = true, bool clear_objects = false)
678 : stack_frame_walker (), m_patterns (),
679 m_clear_all_names (clear_all_names), m_clear_objects (clear_objects),
680 m_have_regexp (false), m_cleared_names ()
681 { }
682
683 OCTAVE_DISABLE_COPY_MOVE (symbol_cleaner)
684
685 ~symbol_cleaner () = default;
686
687 void visit_compiled_fcn_stack_frame (compiled_fcn_stack_frame& frame)
688 {
689 // This one follows static link always. Hmm, should the access
690 // link for a compiled_fcn_stack_frame be the same as the static
691 // link?
692
693 std::shared_ptr<stack_frame> slink = frame.static_link ();
694
695 if (slink)
696 slink->accept (*this);
697 }
698
699 void visit_script_stack_frame (script_stack_frame& frame)
700 {
701 std::shared_ptr<stack_frame> alink = frame.access_link ();
702
703 if (alink)
704 alink->accept (*this);
705 }
706
707 void visit_user_fcn_stack_frame (user_fcn_stack_frame& frame)
708 {
709 clean_frame (frame);
710
711 std::shared_ptr<stack_frame> alink = frame.access_link ();
712
713 if (alink)
714 alink->accept (*this);
715 }
716
717 void visit_scope_stack_frame (scope_stack_frame& frame)
718 {
719 clean_frame (frame);
720
721 std::shared_ptr<stack_frame> alink = frame.access_link ();
722
723 if (alink)
724 alink->accept (*this);
725 }
726
727private:
728
729 void maybe_clear_symbol (stack_frame& frame, const symbol_record& sym)
730 {
731 std::string name = sym.name ();
732
733 if (m_cleared_names.find (name) == m_cleared_names.end ())
734 {
735 // FIXME: Should we check that the name is defined and skip if
736 // it is not? Is it possible for another symbol with the same
737 // name to appear in a later stack frame?
738
739 // FIXME: If we are clearing objects and a symbol is found,
740 // should we add it to the list of cleared names (since
741 // we did find a symbol) but skip clearing the object?
742
743 if (m_clear_objects && ! frame.is_object (sym))
744 return;
745
746 m_cleared_names.insert (name);
747
748 frame.clear (sym);
749 }
750 }
751
752 // FIXME: It would be nice to avoid the duplication in the following
753 // function.
754
755 void clear_symbols (stack_frame& frame,
756 const std::list<symbol_record>& symbols)
757 {
758 if (m_clear_all_names)
759 {
760 for (const auto& sym : symbols)
761 maybe_clear_symbol (frame, sym);
762 }
763 else if (m_have_regexp)
764 {
765 octave_idx_type npatterns = m_patterns.numel ();
766
767 for (octave_idx_type j = 0; j < npatterns; j++)
768 {
769 std::string pattern = m_patterns[j];
770
771 regexp pat (pattern);
772
773 for (const auto& sym : symbols)
774 {
775 if (pat.is_match (sym.name ()))
776 maybe_clear_symbol (frame, sym);
777 }
778 }
779 }
780 else
781 {
782 octave_idx_type npatterns = m_patterns.numel ();
783
784 for (octave_idx_type j = 0; j < npatterns; j++)
785 {
786 std::string pattern = m_patterns[j];
787
788 symbol_match pat (pattern);
789
790 for (const auto& sym : symbols)
791 {
792 if (pat.match (sym.name ()))
793 maybe_clear_symbol (frame, sym);
794 }
795 }
796 }
797 }
798
799 void clean_frame (stack_frame& frame)
800 {
801 symbol_scope scope = frame.get_scope ();
802
803 std::list<symbol_record> symbols = scope.symbol_list ();
804
805 if (m_clear_all_names || ! m_patterns.empty ())
806 clear_symbols (frame, symbols);
807 }
808
809 string_vector m_patterns;
810
811 bool m_clear_all_names;
812 bool m_clear_objects;
813 bool m_have_regexp;
814
815 std::set<std::string> m_cleared_names;
816};
817
818class symbol_info_accumulator : public stack_frame_walker
819{
820public:
821
822 symbol_info_accumulator (const std::string& pattern,
823 bool have_regexp = false)
824 : stack_frame_walker (), m_patterns (pattern), m_match_all (false),
825 m_first_only (false), m_have_regexp (have_regexp), m_sym_inf_list (),
826 m_found_names ()
827 { }
828
829 symbol_info_accumulator (const string_vector& patterns,
830 bool have_regexp = false)
831 : stack_frame_walker (), m_patterns (patterns), m_match_all (false),
832 m_first_only (false), m_have_regexp (have_regexp), m_sym_inf_list (),
833 m_found_names ()
834 { }
835
836 symbol_info_accumulator (bool match_all = true, bool first_only = true)
837 : stack_frame_walker (), m_patterns (), m_match_all (match_all),
838 m_first_only (first_only), m_have_regexp (false),
839 m_sym_inf_list (), m_found_names ()
840 { }
841
842 OCTAVE_DISABLE_COPY_MOVE (symbol_info_accumulator)
843
844 ~symbol_info_accumulator () = default;
845
846 bool is_empty () const
847 {
848 for (const auto& nm_sil : m_sym_inf_list)
849 {
850 const symbol_info_list& lst = nm_sil.second;
851
852 if (! lst.empty ())
853 return false;
854 }
855
856 return true;
857 }
858
859 std::list<std::string> names () const
860 {
861 std::list<std::string> retval;
862
863 for (const auto& nm_sil : m_sym_inf_list)
864 {
865 const symbol_info_list& lst = nm_sil.second;
866
867 std::list<std::string> nm_list = lst.names ();
868
869 for (const auto& nm : nm_list)
870 retval.push_back (nm);
871 }
872
873 return retval;
874 }
875
877 {
878 symbol_info_list retval;
879
880 for (const auto& nm_sil : m_sym_inf_list)
881 {
882 const symbol_info_list& lst = nm_sil.second;
883
884 for (const auto& syminf : lst)
885 retval.push_back (syminf);
886 }
887
888 return retval;
889 }
890
891 octave_map map_value () const
892 {
893 octave_map retval;
894
895 // FIXME: is there a better way to concatenate structures?
896
897 std::size_t n_frames = m_sym_inf_list.size ();
898
899 OCTAVE_LOCAL_BUFFER (octave_map, map_list, n_frames);
900
901 std::size_t j = 0;
902 for (const auto& nm_sil : m_sym_inf_list)
903 {
904 std::string scope_name = nm_sil.first;
905 const symbol_info_list& lst = nm_sil.second;
906
907 map_list[j] = lst.map_value (scope_name, n_frames-j);
908
909 j++;
910 }
911
912 return octave_map::cat (-1, n_frames, map_list);
913 }
914
915 void display (std::ostream& os, const std::string& format) const
916 {
917 for (const auto& nm_sil : m_sym_inf_list)
918 {
919 os << "\nvariables in scope: " << nm_sil.first << "\n\n";
920
921 const symbol_info_list& lst = nm_sil.second;
922
923 lst.display (os, format);
924 }
925 }
926
927 void visit_compiled_fcn_stack_frame (compiled_fcn_stack_frame& frame)
928 {
929 // This one follows static link always. Hmm, should the access
930 // link for a compiled_fcn_stack_frame be the same as the static
931 // link?
932
933 std::shared_ptr<stack_frame> slink = frame.static_link ();
934
935 if (slink)
936 slink->accept (*this);
937 }
938
939 void visit_script_stack_frame (script_stack_frame& frame)
940 {
941 std::shared_ptr<stack_frame> alink = frame.access_link ();
942
943 if (alink)
944 alink->accept (*this);
945 }
946
947 void visit_user_fcn_stack_frame (user_fcn_stack_frame& frame)
948 {
949 append_list (frame);
950
951 std::shared_ptr<stack_frame> alink = frame.access_link ();
952
953 if (alink)
954 alink->accept (*this);
955 }
956
957 void visit_scope_stack_frame (scope_stack_frame& frame)
958 {
959 append_list (frame);
960
961 std::shared_ptr<stack_frame> alink = frame.access_link ();
962
963 if (alink)
964 alink->accept (*this);
965 }
966
967private:
968
969 typedef std::pair<std::string, symbol_info_list> syminf_list_elt;
970
971 // FIXME: the following is too complex and duplicates too much
972 // code. Maybe it should be split up so we have separate classes
973 // that do each job that is needed?
974
975 std::list<symbol_record>
976 filter (stack_frame& frame, const std::list<symbol_record>& symbols)
977 {
978 std::list<symbol_record> new_symbols;
979
980 if (m_match_all)
981 {
982 for (const auto& sym : symbols)
983 {
984 if (frame.is_defined (sym))
985 {
986 std::string name = sym.name ();
987
988 if (m_first_only
989 && m_found_names.find (name) != m_found_names.end ())
990 continue;
991
992 m_found_names.insert (name);
993
994 new_symbols.push_back (sym);
995 }
996 }
997 }
998 else if (m_have_regexp)
999 {
1000 octave_idx_type npatterns = m_patterns.numel ();
1001
1002 for (octave_idx_type j = 0; j < npatterns; j++)
1003 {
1004 std::string pattern = m_patterns[j];
1005
1006 regexp pat (pattern);
1007
1008 for (const auto& sym : symbols)
1009 {
1010 std::string name = sym.name ();
1011
1012 if (pat.is_match (name) && frame.is_defined (sym))
1013 {
1014 if (m_first_only
1015 && m_found_names.find (name) != m_found_names.end ())
1016 continue;
1017
1018 m_found_names.insert (name);
1019
1020 new_symbols.push_back (sym);
1021 }
1022 }
1023 }
1024 }
1025 else
1026 {
1027 octave_idx_type npatterns = m_patterns.numel ();
1028
1029 for (octave_idx_type j = 0; j < npatterns; j++)
1030 {
1031 std::string pattern = m_patterns[j];
1032
1033 symbol_match pat (pattern);
1034
1035 for (const auto& sym : symbols)
1036 {
1037 std::string name = sym.name ();
1038
1039 if (pat.match (name) && frame.is_defined (sym))
1040 {
1041 if (m_first_only
1042 && m_found_names.find (name) == m_found_names.end ())
1043 continue;
1044
1045 m_found_names.insert (name);
1046
1047 new_symbols.push_back (sym);
1048 }
1049 }
1050 }
1051 }
1052
1053 return new_symbols;
1054 }
1055
1056 void append_list (stack_frame& frame)
1057 {
1058 symbol_scope scope = frame.get_scope ();
1059
1060 std::list<symbol_record> symbols = scope.symbol_list ();
1061
1062 if (m_match_all || ! m_patterns.empty ())
1063 symbols = filter (frame, symbols);
1064
1065 symbol_info_list syminf_list = frame.make_symbol_info_list (symbols);
1066
1067 m_sym_inf_list.push_back (syminf_list_elt (scope.name (), syminf_list));
1068 }
1069
1070 string_vector m_patterns;
1071
1072 bool m_match_all;
1073 bool m_first_only;
1074 bool m_have_regexp;
1075
1076 std::list<std::pair<std::string, symbol_info_list>> m_sym_inf_list;
1077
1078 std::set<std::string> m_found_names;
1079};
1080
1083 std::size_t index,
1084 const std::shared_ptr<stack_frame>& parent_link,
1085 const std::shared_ptr<stack_frame>& static_link)
1086{
1087 return new compiled_fcn_stack_frame (tw, fcn, index,
1089}
1090
1093 octave_user_script *script,
1094 std::size_t index,
1095 const std::shared_ptr<stack_frame>& parent_link,
1096 const std::shared_ptr<stack_frame>& static_link)
1097{
1098 return new script_stack_frame (tw, script, index, parent_link, static_link);
1099}
1100
1103 octave_user_function *fcn, std::size_t index,
1104 const std::shared_ptr<stack_frame>& parent_link,
1105 const std::shared_ptr<stack_frame>& static_link,
1106 const std::shared_ptr<stack_frame>& access_link)
1107{
1108 return new user_fcn_stack_frame (tw, fcn, index,
1110}
1111
1114 octave_user_function *fcn, std::size_t index,
1115 const std::shared_ptr<stack_frame>& parent_link,
1116 const std::shared_ptr<stack_frame>& static_link,
1117 const local_vars_map& local_vars,
1118 const std::shared_ptr<stack_frame>& access_link)
1119{
1120 return new user_fcn_stack_frame (tw, fcn, index,
1121 parent_link, static_link, local_vars,
1122 access_link);
1123}
1124
1127 const symbol_scope& scope, std::size_t index,
1128 const std::shared_ptr<stack_frame>& parent_link,
1129 const std::shared_ptr<stack_frame>& static_link)
1130{
1131 return new scope_stack_frame (tw, scope, index, parent_link, static_link);
1132}
1133
1134// This function is only implemented and should only be called for
1135// user_fcn stack frames. Anything else indicates an error in the
1136// implementation, but we'll simply warn if that happens.
1137
1138void
1140{
1141 warning ("invalid call to stack_frame::clear_values; please report");
1142}
1143
1145stack_frame::make_symbol_info_list (const std::list<symbol_record>& symrec_list) const
1146{
1147 symbol_info_list symbol_stats;
1148
1149 for (const auto& sym : symrec_list)
1150 {
1151 octave_value value = varval (sym);
1152
1153 if (! value.is_defined ()
1154 || (is_user_fcn_frame () && sym.frame_offset () > 0))
1155 continue;
1156
1157 symbol_info syminf (sym.name (), value, sym.is_formal (),
1158 is_global (sym), is_persistent (sym));
1159
1160 symbol_stats.push_back (syminf);
1161 }
1162
1163 return symbol_stats;
1164}
1165
1168 bool have_regexp, bool return_list,
1169 bool verbose, const std::string& whos_line_fmt,
1170 const std::string& msg)
1171{
1172 symbol_info_accumulator sym_inf_accum (patterns, have_regexp);
1173
1174 accept (sym_inf_accum);
1175
1176 if (return_list)
1177 {
1178 if (verbose)
1179 return sym_inf_accum.map_value ();
1180 else
1181 return Cell (string_vector (sym_inf_accum.names ()));
1182 }
1183 else if (! sym_inf_accum.is_empty ())
1184 {
1185
1186 if (msg.empty ())
1187 octave_stdout << "Variables visible from the current scope:\n";
1188 else
1189 octave_stdout << msg;
1190
1191 if (verbose)
1192 sym_inf_accum.display (octave_stdout, whos_line_fmt);
1193 else
1194 {
1195 octave_stdout << "\n";
1196 string_vector names (sym_inf_accum.names ());
1198 }
1199
1200 octave_stdout << "\n";
1201 }
1202
1203 return octave_value ();
1204}
1205
1206// Return first occurrence of variables in current stack frame and any
1207// parent frames reachable through access links.
1208
1211{
1212 symbol_info_accumulator sia (true, true);
1213
1214 accept (sia);
1215
1216 return sia.symbol_info ();
1217}
1218
1221{
1222 std::list<octave_scalar_map> ws_list;
1223
1224 stack_frame *frame = this;
1225
1226 while (frame)
1227 {
1228 symbol_info_list symbols = frame->all_variables ();
1229
1231
1232 for (const auto& sym_name : symbols.names ())
1233 {
1234 octave_value val = symbols.varval (sym_name);
1235
1236 if (val.is_defined ())
1237 ws.assign (sym_name, val);
1238 }
1239
1240 ws_list.push_back (ws);
1241
1242 std::shared_ptr<stack_frame> nxt = frame->access_link ();
1243 frame = nxt.get ();
1244 }
1245
1246 Cell ws_frames (ws_list.size (), 1);
1247
1248 octave_idx_type i = 0;
1249 for (const auto& elt : ws_list)
1250 ws_frames(i++) = elt;
1251
1252 return ws_frames;
1253}
1254
1255// FIXME: Should this function also find any variables in parent
1256// scopes accessible through access_links?
1257
1258std::list<std::string>
1260{
1261 std::list<std::string> retval;
1262
1263 symbol_scope scope = get_scope ();
1264
1265 const std::map<std::string, symbol_record>& symbols = scope.symbols ();
1266
1267 for (const auto& nm_sr : symbols)
1268 {
1269 if (is_variable (nm_sr.second))
1270 retval.push_back (nm_sr.first);
1271 }
1272
1273 retval.sort ();
1274
1275 return retval;
1276}
1277
1279stack_frame::glob_symbol_info (const std::string& pattern)
1280{
1281 symbol_info_accumulator sia (pattern, false);
1282
1283 accept (sia);
1284
1285 return sia.symbol_info ();
1286}
1287
1289stack_frame::regexp_symbol_info (const std::string& pattern)
1290{
1291 symbol_info_accumulator sia (pattern, true);
1292
1293 accept (sia);
1294
1295 return sia.symbol_info ();
1296}
1297
1298std::size_t
1300{
1301 // This function should only be called for user_fcn_stack_frame or
1302 // scope_stack_frame objects. Anything else indicates an error in
1303 // the implementation.
1304
1305 error ("unexpected call to stack_frame::size () - please report this bug");
1306}
1307
1308void
1310{
1311 // This function should only be called for user_fcn_stack_frame or
1312 // scope_stack_frame objects. Anything else indicates an error in
1313 // the implementation.
1314
1315 error ("unexpected call to stack_frame::resize () - please report this bug");
1316}
1317
1320{
1321 // This function should only be called for user_fcn_stack_frame or
1322 // scope_stack_frame objects. Anything else indicates an error in
1323 // the implementation.
1324
1325 error ("unexpected call to stack_frame::get_scope_flag (std::size_t) - please report this bug");
1326}
1327
1328void
1330{
1331 // This function should only be called for user_fcn_stack_frame or
1332 // scope_stack_frame objects. Anything else indicates an error in
1333 // the implementation.
1334
1335 error ("unexpected call to stack_frame::get_scope_flag (std::size_t, scope_flags) - please report this bug");
1336}
1337
1338void
1340 const octave_value& value, bool global)
1341{
1342 if (global && ! is_global (sym))
1343 {
1344 octave_value val = varval (sym);
1345
1346 if (val.is_defined ())
1347 {
1348 std::string nm = sym.name ();
1349
1350 warning_with_id ("Octave:global-local-conflict",
1351 "global: '%s' is defined in the current scope.\n",
1352 nm.c_str ());
1353 warning_with_id ("Octave:global-local-conflict",
1354 "global: in a future version, global variables must be declared before use.\n");
1355
1356 // If the symbol is defined in the local but not the
1357 // global scope, then use the local value as the
1358 // initial value. This value will also override any
1359 // initializer in the global statement.
1360 octave_value global_val = m_evaluator.global_varval (nm);
1361
1362 if (global_val.is_defined ())
1363 {
1364 warning_with_id ("Octave:global-local-conflict",
1365 "global: global value overrides existing local value");
1366
1367 clear (sym);
1368 }
1369 else
1370 {
1371 warning_with_id ("Octave:global-local-conflict",
1372 "global: existing local value used to initialize global variable");
1373
1374 m_evaluator.global_varref (nm) = val;
1375 }
1376 }
1377
1378 mark_global (sym);
1379 }
1380
1381 if (value.is_defined ())
1382 assign (sym, value);
1383}
1384
1386stack_frame::varval (std::size_t) const
1387{
1388 // This function should only be called for user_fcn_stack_frame or
1389 // scope_stack_frame objects. Anything else indicates an error in
1390 // the implementation.
1391
1392 error ("unexpected call to stack_frame::varval (std::size_t) - please report this bug");
1393}
1394
1397{
1398 // This function should only be called for user_fcn_stack_frame or
1399 // scope_stack_frame objects. Anything else indicates an error in
1400 // the implementation.
1401
1402 error ("unexpected call to stack_frame::varref (std::size_t) - please report this bug");
1403}
1404
1405std::string
1406stack_frame::inputname (int, bool) const
1407{
1408 // This function should only be called for user_fcn_stack_frame.
1409 // Anything else indicates an error in the implementation.
1410
1411 error ("unexpected call to stack_frame::inputname (int, bool) - please report this bug");
1412}
1413
1414void
1416{
1417 symbol_cleaner sc (true, true);
1418
1419 accept (sc);
1420}
1421
1422void
1423stack_frame::clear_variable (const std::string& name)
1424{
1425 symbol_cleaner sc (name);
1426
1427 accept (sc);
1428}
1429
1430void
1431stack_frame::clear_variable_pattern (const std::string& pattern)
1432{
1433 symbol_cleaner sc (pattern);
1434
1435 accept (sc);
1436}
1437
1438void
1440{
1441 symbol_cleaner sc (patterns);
1442
1443 accept (sc);
1444}
1445
1446void
1447stack_frame::clear_variable_regexp (const std::string& pattern)
1448{
1449 symbol_cleaner sc (pattern, true);
1450
1451 accept (sc);
1452}
1453
1454void
1456{
1457 symbol_cleaner sc (patterns, true);
1458
1459 accept (sc);
1460}
1461
1462void
1464{
1465 symbol_cleaner sc;
1466
1467 accept (sc);
1468}
1469
1470void
1472{
1473 if (index () == 0)
1474 os << "at top level" << std::endl;
1475 else
1476 {
1477 os << "stopped in " << fcn_name ();
1478
1479 int l = line ();
1480 if (l > 0)
1481 os << " at line " << line ();
1482
1483 os << " [" << fcn_file_name () << "] " << std::endl;
1484 }
1485}
1486
1487void
1488stack_frame::debug_list (std::ostream& os, int num_lines) const
1489{
1490 std::string file_name = fcn_file_name ();
1491
1492 int target_line = line ();
1493 int start = std::max (target_line - num_lines/2, 0);
1494 int end = target_line + num_lines/2;
1495
1496 display_file_lines (os, fcn_file_name (), start, end, target_line, "-->", "dblist");
1497}
1498
1499void
1500stack_frame::debug_type (std::ostream& os, int start_line, int end_line) const
1501{
1502 display_file_lines (os, fcn_file_name (), start_line, end_line, -1, "", "dbtype");
1503}
1504
1505void
1506stack_frame::display (bool follow) const
1507{
1508 std::ostream& os = octave_stdout;
1509
1510 os << "-- [stack_frame] (" << this << ") --" << std::endl;
1511
1512 os << "parent link: ";
1513 if (m_parent_link)
1514 os << m_parent_link.get ();
1515 else
1516 os << "NULL";
1517 os << std::endl;
1518
1519 os << "static link: ";
1520 if (m_static_link)
1521 os << m_static_link.get ();
1522 else
1523 os << "NULL";
1524 os << std::endl;
1525
1526 os << "access link: ";
1527 if (m_access_link)
1528 os << m_access_link.get ();
1529 else
1530 os << "NULL";
1531 os << std::endl;
1532
1533 os << "line: " << m_line << std::endl;
1534 os << "column: " << m_column << std::endl;
1535 os << "index: " << m_index << std::endl;
1536
1537 os << std::endl;
1538
1539 if (! follow)
1540 return;
1541
1542 os << "FOLLOWING ACCESS LINKS:" << std::endl;
1543 std::shared_ptr<stack_frame> frm = access_link ();
1544 while (frm)
1545 {
1546 frm->display (false);
1547 os << std::endl;
1548
1549 frm = frm->access_link ();
1550 }
1551}
1552
1553void
1554compiled_fcn_stack_frame::display (bool follow) const
1555{
1556 std::ostream& os = octave_stdout;
1557
1558 os << "-- [compiled_fcn_stack_frame] (" << this << ") --" << std::endl;
1559 stack_frame::display (follow);
1560
1561 os << "fcn: " << m_fcn->name ()
1562 << " (" << m_fcn->type_name () << ")" << std::endl;
1563}
1564
1565void
1566compiled_fcn_stack_frame::accept (stack_frame_walker& sfw)
1567{
1568 sfw.visit_compiled_fcn_stack_frame (*this);
1569}
1570
1571script_stack_frame::script_stack_frame (tree_evaluator& tw,
1572 octave_user_script *script,
1573 std::size_t index,
1574 const std::shared_ptr<stack_frame>& parent_link,
1575 const std::shared_ptr<stack_frame>& static_link)
1576 : stack_frame (tw, index, parent_link, static_link,
1577 get_access_link (static_link)),
1578 m_script (script), m_unwind_protect_frame (nullptr),
1579 m_lexical_frame_offsets (get_num_symbols (script), 1),
1580 m_value_offsets (get_num_symbols (script), 0)
1581{
1582 set_script_offsets ();
1583}
1584
1585std::size_t
1586script_stack_frame::get_num_symbols (octave_user_script *script)
1587{
1588 symbol_scope script_scope = script->scope ();
1589
1590 return script_scope.num_symbols ();
1591}
1592
1593void
1594script_stack_frame::set_script_offsets ()
1595{
1596 // Set frame and data offsets inside stack frame based on enclosing
1597 // scope(s).
1598
1599 symbol_scope script_scope = m_script->scope ();
1600
1601 std::size_t num_script_symbols = script_scope.num_symbols ();
1602
1603 resize (num_script_symbols);
1604
1605 const std::map<std::string, symbol_record>& script_symbols
1606 = script_scope.symbols ();
1607
1608 set_script_offsets_internal (script_symbols);
1609}
1610
1611void script_stack_frame::set_script_offsets_internal
1612(const std::map<std::string, symbol_record>& script_symbols)
1613{
1614 // This scope will be used to evaluate the script. Find (or
1615 // possibly insert) symbols from the dummy script scope here.
1616
1617 symbol_scope eval_scope = m_access_link->get_scope ();
1618
1619 if (eval_scope.is_nested ())
1620 {
1621 bool found = false;
1622
1623 for (const auto& nm_sr : script_symbols)
1624 {
1625 std::string name = nm_sr.first;
1626 symbol_record script_sr = nm_sr.second;
1627
1628 symbol_scope parent_scope = eval_scope;
1629
1630 std::size_t count = 1;
1631
1632 while (parent_scope)
1633 {
1634 const std::map<std::string, symbol_record>& parent_scope_symbols
1635 = parent_scope.symbols ();
1636
1637 auto p = parent_scope_symbols.find (name);
1638
1639 if (p != parent_scope_symbols.end ())
1640 {
1641 found = true;
1642 symbol_record parent_scope_sr = p->second;
1643
1644 std::size_t script_sr_data_offset = script_sr.data_offset ();
1645
1646 m_lexical_frame_offsets.at (script_sr_data_offset)
1647 = parent_scope_sr.frame_offset () + count;
1648
1649 m_value_offsets.at (script_sr_data_offset)
1650 = parent_scope_sr.data_offset ();
1651
1652 break;
1653 }
1654 else
1655 {
1656 count++;
1657 parent_scope = parent_scope.parent_scope ();
1658 }
1659 }
1660
1661 if (! found)
1662 error ("symbol '%s' cannot be added to static scope",
1663 name.c_str ());
1664 }
1665 }
1666 else
1667 {
1668 const std::map<std::string, symbol_record>& eval_scope_symbols
1669 = eval_scope.symbols ();
1670
1671 for (const auto& nm_sr : script_symbols)
1672 {
1673 std::string name = nm_sr.first;
1674 symbol_record script_sr = nm_sr.second;
1675
1676 auto p = eval_scope_symbols.find (name);
1677
1678 symbol_record eval_scope_sr;
1679
1680 if (p == eval_scope_symbols.end ())
1681 eval_scope_sr = eval_scope.insert (name);
1682 else
1683 eval_scope_sr = p->second;
1684
1685 std::size_t script_sr_data_offset = script_sr.data_offset ();
1686
1687 // The +1 is for going from the script frame to the eval
1688 // frame. Only one access_link should need to be followed.
1689
1690 m_lexical_frame_offsets.at (script_sr_data_offset)
1691 = eval_scope_sr.frame_offset () + 1;
1692
1693 m_value_offsets.at (script_sr_data_offset)
1694 = eval_scope_sr.data_offset ();
1695 }
1696 }
1697}
1698
1699void
1700script_stack_frame::resize_and_update_script_offsets (const symbol_record& sym)
1701{
1702 std::size_t data_offset = sym.data_offset ();
1703
1704 // This function is called when adding new symbols to a script
1705 // scope. If the symbol wasn't present before, it should be outside
1706 // the range so we need to resize then update offsets.
1707
1708 if (data_offset < size ())
1709 error ("unexpected: data_offset < size () in script_stack_frame::resize_and_update_script_offsets - please report this bug");
1710
1711 resize (data_offset+1);
1712
1713 // FIXME: We should be able to avoid creating the map object and the
1714 // looping in the set_scripts_offsets_internal function. Can we do
1715 // that without (or with minimal) code duplication?
1716
1717 std::map<std::string, symbol_record> tmp_symbols;
1718 tmp_symbols[sym.name ()] = sym;
1719 set_script_offsets_internal (tmp_symbols);
1720}
1721
1722// If this is a nested scope, set access_link to nearest parent
1723// stack frame that corresponds to the lexical parent of this scope.
1724
1725std::shared_ptr<stack_frame>
1726script_stack_frame::get_access_link (const std::shared_ptr<stack_frame>& static_link)
1727{
1728 // If this script is called from another script, set access
1729 // link to ultimate parent stack frame.
1730
1731 std::shared_ptr<stack_frame> alink = static_link;
1732
1733 while (alink->is_user_script_frame ())
1734 {
1735 if (alink->access_link ())
1736 alink = alink->access_link ();
1737 else
1738 break;
1739 }
1740
1741 return alink;
1742}
1743
1745script_stack_frame::unwind_protect_frame ()
1746{
1747 if (! m_unwind_protect_frame)
1748 m_unwind_protect_frame = new unwind_protect ();
1749
1750 return m_unwind_protect_frame;
1751}
1752
1754script_stack_frame::lookup_symbol (const std::string& name) const
1755{
1756 symbol_scope scope = get_scope ();
1757
1758 symbol_record sym = scope.lookup_symbol (name);
1759
1760 if (sym)
1761 {
1762 if (sym.frame_offset () != 0)
1763 error ("unexpected: sym.frame_offset () != 0 in script_stack_frame::lookup_symbol - please report this bug");
1764
1765 return sym;
1766 }
1767
1768 sym = m_access_link->lookup_symbol (name);
1769
1770 // Return symbol record with adjusted frame offset.
1771 symbol_record new_sym = sym.dup ();
1772
1773 new_sym.set_frame_offset (sym.frame_offset () + 1);
1774
1775 return new_sym;
1776}
1777
1779script_stack_frame::insert_symbol (const std::string& name)
1780{
1781 // If the symbols is already in the immediate scope, there is
1782 // nothing more to do.
1783
1784 symbol_scope scope = get_scope ();
1785
1786 symbol_record sym = scope.lookup_symbol (name);
1787
1788 if (sym)
1789 {
1790 // All symbol records in a script scope should have zero offset,
1791 // which means we redirect our lookup using
1792 // lexical_frame_offsets and values_offets.
1793
1794 if (sym.frame_offset () != 0)
1795 error ("unexpected: sym.frame_offset () != 0 in script_stack_frame::insert_symbol - please report this bug");
1796
1797 return sym;
1798 }
1799
1800 // Insert the symbol in the current scope then resize and update
1801 // offsets. This operation should never fail.
1802
1803 sym = scope.find_symbol (name);
1804
1805 if (! sym.is_valid ())
1806 error ("unexpected: sym is not valid in script_stack_frame::insert_symbol - please report this bug");
1807
1808 resize_and_update_script_offsets (sym);
1809
1810 return sym;
1811}
1812
1813// Similar to set_script_offsets_internal except that we only return
1814// frame and data offsets for symbols found by name in parent scopes
1815// instead of updating the offsets stored in the script frame itself.
1816
1817bool
1818script_stack_frame::get_val_offsets_internal (const symbol_record& script_sr,
1819 std::size_t& frame_offset,
1820 std::size_t& data_offset) const
1821{
1822 bool found = false;
1823
1824 // This scope will be used to evaluate the script. Find symbols
1825 // here by name.
1826
1827 symbol_scope eval_scope = m_access_link->get_scope ();
1828
1829 if (eval_scope.is_nested ())
1830 {
1831 std::string name = script_sr.name ();
1832
1833 symbol_scope parent_scope = eval_scope;
1834
1835 std::size_t count = 1;
1836
1837 while (parent_scope)
1838 {
1839 const std::map<std::string, symbol_record>& parent_scope_symbols
1840 = parent_scope.symbols ();
1841
1842 auto p = parent_scope_symbols.find (name);
1843
1844 if (p != parent_scope_symbols.end ())
1845 {
1846 found = true;
1847 symbol_record parent_scope_sr = p->second;
1848
1849 frame_offset = parent_scope_sr.frame_offset () + count;
1850
1851 data_offset = parent_scope_sr.data_offset ();
1852
1853 break;
1854 }
1855 else
1856 {
1857 count++;
1858 parent_scope = parent_scope.parent_scope ();
1859 }
1860 }
1861 }
1862 else
1863 {
1864 const std::map<std::string, symbol_record>& eval_scope_symbols
1865 = eval_scope.symbols ();
1866
1867 std::string name = script_sr.name ();
1868
1869 auto p = eval_scope_symbols.find (name);
1870
1871 symbol_record eval_scope_sr;
1872
1873 if (p != eval_scope_symbols.end ())
1874 {
1875 found = true;
1876 eval_scope_sr = p->second;
1877
1878 // The +1 is for going from the script frame to the eval
1879 // frame. Only one access_link should need to be followed.
1880
1881 frame_offset = eval_scope_sr.frame_offset () + 1;
1882
1883 data_offset = eval_scope_sr.data_offset ();
1884 }
1885 }
1886
1887 return found;
1888}
1889
1890bool
1891script_stack_frame::get_val_offsets (const symbol_record& sym,
1892 std::size_t& frame_offset,
1893 std::size_t& data_offset) const
1894{
1895 data_offset = sym.data_offset ();
1896 frame_offset = sym.frame_offset ();
1897
1898 if (frame_offset == 0)
1899 {
1900 // An out of range data_offset value here means that we have a
1901 // symbol that was not originally in the script. But this
1902 // function is called in places where we can't insert a new
1903 // symbol, so we fail and it is up to the caller to decide what
1904 // to do.
1905
1906 if (data_offset >= size ())
1907 return get_val_offsets_internal (sym, frame_offset, data_offset);
1908
1909 // Use frame and value offsets stored in this stack frame,
1910 // indexed by data_offset from the symbol_record to find the
1911 // values. These offsets were determined by
1912 // script_stack_frame::set_script_offsets when this script was
1913 // invoked.
1914
1915 frame_offset = m_lexical_frame_offsets.at (data_offset);
1916
1917 if (frame_offset == 0)
1918 {
1919 // If the frame offset stored in m_lexical_frame_offsets is
1920 // zero, then the data offset in the evaluation scope has
1921 // not been determined so try to do that now. The symbol
1922 // may have been added by eval and without calling
1923 // resize_and_update_script_offsets.
1924
1925 return get_val_offsets_internal (sym, frame_offset, data_offset);
1926 }
1927
1928 data_offset = m_value_offsets.at (data_offset);
1929 }
1930 else
1931 {
1932 // If frame_offset is not zero, then then we must have a symbol
1933 // that was not originally in the script. The values should
1934 // have been determined by the script_stack_frame::lookup function.
1935 }
1936
1937 return true;
1938}
1939
1940void
1941script_stack_frame::get_val_offsets_with_insert (const symbol_record& sym,
1942 std::size_t& frame_offset,
1943 std::size_t& data_offset)
1944{
1945 data_offset = sym.data_offset ();
1946 frame_offset = sym.frame_offset ();
1947
1948 if (frame_offset == 0)
1949 {
1950 if (data_offset >= size ())
1951 {
1952 // If the data_offset is out of range, then we must have a
1953 // symbol that was not originally in the script. Resize and
1954 // update the offsets.
1955
1956 resize_and_update_script_offsets (sym);
1957 }
1958
1959 // Use frame and value offsets stored in this stack frame,
1960 // indexed by data_offset from the symbol_record to find the
1961 // values. These offsets were determined by
1962 // script_stack_frame::set_script_offsets when this script was
1963 // invoked.
1964
1965 frame_offset = m_lexical_frame_offsets.at (data_offset);
1966
1967 if (frame_offset == 0)
1968 {
1969 // If the frame offset stored in m_lexical_frame_offsets is
1970 // zero, then the data offset in the evaluation scope has
1971 // not been determined so try to do that now. The symbol
1972 // may have been added by eval and without calling
1973 // resize_and_update_script_offsets.
1974
1975 // We don't need to resize here. That case is handled above.
1976
1977 // FIXME: We should be able to avoid creating the map object
1978 // and the looping in the set_scripts_offsets_internal
1979 // function. Can we do that without (or with minimal) code
1980 // duplication?
1981
1982 std::map<std::string, symbol_record> tmp_symbols;
1983 tmp_symbols[sym.name ()] = sym;
1984 set_script_offsets_internal (tmp_symbols);
1985
1986 // set_script_offsets_internal may have modified
1987 // m_lexical_frame_offsets and m_value_offsets.
1988
1989 frame_offset = m_lexical_frame_offsets.at (data_offset);
1990 }
1991
1992 data_offset = m_value_offsets.at (data_offset);
1993 }
1994 else
1995 {
1996 // If frame_offset is not zero, then then we must have a symbol
1997 // that was not originally in the script. The values were
1998 // determined by the script_stack_frame::lookup function.
1999 }
2000}
2001
2003script_stack_frame::scope_flag (const symbol_record& sym) const
2004{
2005 std::size_t frame_offset;
2006 std::size_t data_offset;
2007
2008 bool found = get_val_offsets (sym, frame_offset, data_offset);
2009
2010 // It can't be global or persistent, so call it local.
2011 if (! found)
2012 return LOCAL;
2013
2014 // Follow frame_offset access links to stack frame that holds
2015 // the value.
2016
2017 const stack_frame *frame = this;
2018
2019 for (std::size_t i = 0; i < frame_offset; i++)
2020 {
2021 std::shared_ptr<stack_frame> nxt = frame->access_link ();
2022 frame = nxt.get ();
2023 }
2024
2025 if (! frame)
2026 error ("internal error: invalid access link in function call stack");
2027
2028 if (data_offset >= frame->size ())
2029 return LOCAL;
2030
2031 return frame->get_scope_flag (data_offset);
2032}
2033
2035script_stack_frame::varval (const symbol_record& sym) const
2036{
2037 std::size_t frame_offset;
2038 std::size_t data_offset;
2039
2040 bool found = get_val_offsets (sym, frame_offset, data_offset);
2041
2042 if (! found)
2043 return octave_value ();
2044
2045 // Follow frame_offset access links to stack frame that holds
2046 // the value.
2047
2048 const stack_frame *frame = this;
2049
2050 for (std::size_t i = 0; i < frame_offset; i++)
2051 {
2052 std::shared_ptr<stack_frame> nxt = frame->access_link ();
2053 frame = nxt.get ();
2054 }
2055
2056 if (! frame)
2057 error ("internal error: invalid access link in function call stack");
2058
2059 if (data_offset >= frame->size ())
2060 return octave_value ();
2061
2062 switch (frame->get_scope_flag (data_offset))
2063 {
2064 case LOCAL:
2065 return frame->varval (data_offset);
2066
2067 case PERSISTENT:
2068 {
2069 symbol_scope scope = frame->get_scope ();
2070
2071 return scope.persistent_varval (data_offset);
2072 }
2073
2074 case GLOBAL:
2075 return m_evaluator.global_varval (sym.name ());
2076 }
2077
2078 error ("internal error: invalid switch case");
2079}
2080
2082script_stack_frame::varref (const symbol_record& sym)
2083{
2084 std::size_t frame_offset;
2085 std::size_t data_offset;
2086 get_val_offsets_with_insert (sym, frame_offset, data_offset);
2087
2088 // Follow frame_offset access links to stack frame that holds
2089 // the value.
2090
2091 stack_frame *frame = this;
2092
2093 for (std::size_t i = 0; i < frame_offset; i++)
2094 {
2095 std::shared_ptr<stack_frame> nxt = frame->access_link ();
2096 frame = nxt.get ();
2097 }
2098
2099 if (data_offset >= frame->size ())
2100 frame->resize (data_offset+1);
2101
2102 switch (frame->get_scope_flag (data_offset))
2103 {
2104 case LOCAL:
2105 return frame->varref (data_offset);
2106
2107 case PERSISTENT:
2108 {
2109 symbol_scope scope = frame->get_scope ();
2110
2111 return scope.persistent_varref (data_offset);
2112 }
2113
2114 case GLOBAL:
2115 return m_evaluator.global_varref (sym.name ());
2116 }
2117
2118 error ("internal error: invalid switch case");
2119}
2120
2121void
2122script_stack_frame::mark_scope (const symbol_record& sym,
2123 scope_flags flag)
2124{
2125 std::size_t data_offset = sym.data_offset ();
2126
2127 if (data_offset >= size ())
2128 resize_and_update_script_offsets (sym);
2129
2130 // Redirection to evaluation context for the script.
2131
2132 std::size_t frame_offset = m_lexical_frame_offsets.at (data_offset);
2133 data_offset = m_value_offsets.at (data_offset);
2134
2135 if (frame_offset > 1)
2136 error ("variables must be made PERSISTENT or GLOBAL in the first scope in which they are used");
2137
2138 std::shared_ptr<stack_frame> frame = access_link ();
2139
2140 if (data_offset >= frame->size ())
2141 frame->resize (data_offset+1);
2142
2143 frame->set_scope_flag (data_offset, flag);
2144}
2145
2146void
2147script_stack_frame::display (bool follow) const
2148{
2149 std::ostream& os = octave_stdout;
2150
2151 os << "-- [script_stack_frame] (" << this << ") --" << std::endl;
2152 stack_frame::display (follow);
2153
2154 os << "script: " << m_script->name ()
2155 << " (" << m_script->type_name () << ")" << std::endl;
2156
2157 os << "lexical_offsets: " << m_lexical_frame_offsets.size ()
2158 << " elements:";
2159
2160 for (std::size_t i = 0; i < m_lexical_frame_offsets.size (); i++)
2161 os << " " << m_lexical_frame_offsets.at (i);
2162 os << std::endl;
2163
2164 os << "value_offsets: " << m_value_offsets.size () << " elements:";
2165 for (std::size_t i = 0; i < m_value_offsets.size (); i++)
2166 os << " " << m_value_offsets.at (i);
2167 os << std::endl;
2168
2169 display_scope (os, get_scope ());
2170}
2171
2172void
2173script_stack_frame::accept (stack_frame_walker& sfw)
2174{
2175 sfw.visit_script_stack_frame (*this);
2176}
2177
2178void
2179base_value_stack_frame::display (bool follow) const
2180{
2181 std::ostream& os = octave_stdout;
2182
2183 os << "-- [base_value_stack_frame] (" << this << ") --" << std::endl;
2184 stack_frame::display (follow);
2185
2186 os << "values: " << m_values.size ()
2187 << " elements (idx, scope flag, type):" << std::endl;
2188
2189 for (std::size_t i = 0; i < m_values.size (); i++)
2190 {
2191 os << " (" << i << ", " << m_flags.at (i) << ", ";
2192
2193 octave_value val = varval (i);
2194
2195 os << (val.is_defined () ? val.type_name () : " UNDEFINED") << ")"
2196 << std::endl;
2197 }
2198}
2199
2200// If this is a nested scope, set access_link to nearest parent
2201// stack frame that corresponds to the lexical parent of this scope.
2202
2203std::shared_ptr<stack_frame>
2204user_fcn_stack_frame::get_access_link (octave_user_function *fcn,
2205 const std::shared_ptr<stack_frame>& static_link)
2206{
2207 std::shared_ptr<stack_frame> alink;
2208
2209 symbol_scope fcn_scope = fcn->scope ();
2210
2211 if (fcn_scope.is_nested ())
2212 {
2213 if (! static_link)
2214 error ("internal call stack error (invalid static link)");
2215
2216 symbol_scope caller_scope = static_link->get_scope ();
2217
2218 int nesting_depth = fcn_scope.nesting_depth ();
2219 int caller_nesting_depth = caller_scope.nesting_depth ();
2220
2221 if (caller_nesting_depth < nesting_depth)
2222 {
2223 // FIXME: do we need to ensure that the called
2224 // function is a child of the caller? Does it hurt
2225 // to panic_unless this condition, at least for now?
2226
2227 alink = static_link;
2228 }
2229 else
2230 {
2231 // FIXME: do we need to check that the parent of the
2232 // called function is also a parent of the caller?
2233 // Does it hurt to panic_unless this condition, at least
2234 // for now?
2235
2236 int links_to_follow = caller_nesting_depth - nesting_depth + 1;
2237
2238 alink = static_link;
2239
2240 while (alink && --links_to_follow >= 0)
2241 alink = alink->access_link ();
2242
2243 if (! alink)
2244 error ("internal function nesting error (invalid access link)");
2245 }
2246 }
2247
2248 return alink;
2249}
2250
2251void
2252user_fcn_stack_frame::clear_values ()
2253{
2254 symbol_scope fcn_scope = m_fcn->scope ();
2255
2256 const std::list<symbol_record>& symbols = fcn_scope.symbol_list ();
2257
2258 if (size () == 0)
2259 return;
2260
2261 for (const auto& sym : symbols)
2262 {
2263 std::size_t frame_offset = sym.frame_offset ();
2264
2265 if (frame_offset > 0)
2266 continue;
2267
2268 std::size_t data_offset = sym.data_offset ();
2269
2270 if (data_offset >= size ())
2271 continue;
2272
2273 if (get_scope_flag (data_offset) == LOCAL)
2274 {
2275 octave_value& ref = m_values.at (data_offset);
2276
2277 if (ref.get_count () == 1)
2278 {
2280 ref = octave_value ();
2281 }
2282 }
2283 }
2284}
2285
2287user_fcn_stack_frame::unwind_protect_frame ()
2288{
2289 if (! m_unwind_protect_frame)
2290 m_unwind_protect_frame = new unwind_protect ();
2291
2292 return m_unwind_protect_frame;
2293}
2294
2296user_fcn_stack_frame::lookup_symbol (const std::string& name) const
2297{
2298 const stack_frame *frame = this;
2299
2300 while (frame)
2301 {
2302 symbol_scope scope = frame->get_scope ();
2303
2304 symbol_record sym = scope.lookup_symbol (name);
2305
2306 if (sym)
2307 return sym;
2308
2309 std::shared_ptr<stack_frame> nxt = frame->access_link ();
2310 frame = nxt.get ();
2311 }
2312
2313 return symbol_record ();
2314}
2315
2317user_fcn_stack_frame::insert_symbol (const std::string& name)
2318{
2319 // If the symbols is already in the immediate scope, there is
2320 // nothing more to do.
2321
2322 symbol_scope scope = get_scope ();
2323
2324 symbol_record sym = scope.lookup_symbol (name);
2325
2326 if (sym)
2327 return sym;
2328
2329 // FIXME: This needs some thought... We may need to add a symbol to
2330 // a static workspace, but the symbol can never be defined as a
2331 // variable. This currently works by tagging the added symbol as
2332 // "added_static". Aside from the bad name, this doesn't seem like
2333 // the best solution. Maybe scopes should have a separate set of
2334 // symbols that may only be defined as functions?
2335
2336 // Insert the symbol in the current scope. This is not possible for
2337 // anonymous functions, nested functions, or functions that contain
2338 // nested functions (their scopes will all be marked static).
2339
2340 // if (scope.is_static ())
2341 // error ("can not add variable '%s' to a static workspace",
2342 // name.c_str ());
2343
2344 // At this point, non-local references are not possible so we only
2345 // need to look in the current scope and insert there. This
2346 // operation should never fail.
2347
2348 sym = scope.find_symbol (name);
2349
2350 if (! sym.is_valid ())
2351 error ("unexpected: sym is not valid in user_fcn_stack_frame::insert_symbol - please report this bug");
2352
2353 return sym;
2354}
2355
2357user_fcn_stack_frame::scope_flag (const symbol_record& sym) const
2358{
2359 std::size_t frame_offset = sym.frame_offset ();
2360 std::size_t data_offset = sym.data_offset ();
2361
2362 // Follow frame_offset access links to stack frame that holds
2363 // the value.
2364
2365 const stack_frame *frame = this;
2366
2367 for (std::size_t i = 0; i < frame_offset; i++)
2368 {
2369 std::shared_ptr<stack_frame> nxt = frame->access_link ();
2370 frame = nxt.get ();
2371 }
2372
2373 if (! frame)
2374 error ("internal error: invalid access link in function call stack");
2375
2376 if (data_offset >= frame->size ())
2377 return LOCAL;
2378
2379 return frame->get_scope_flag (data_offset);
2380}
2381
2383user_fcn_stack_frame::varval (const symbol_record& sym) const
2384{
2385 std::size_t frame_offset = sym.frame_offset ();
2386 std::size_t data_offset = sym.data_offset ();
2387
2388 // Follow frame_offset access links to stack frame that holds
2389 // the value.
2390
2391 const stack_frame *frame = this;
2392
2393 for (std::size_t i = 0; i < frame_offset; i++)
2394 {
2395 std::shared_ptr<stack_frame> nxt = frame->access_link ();
2396 frame = nxt.get ();
2397 }
2398
2399 if (! frame)
2400 error ("internal error: invalid access link in function call stack");
2401
2402 if (data_offset >= frame->size ())
2403 return octave_value ();
2404
2405 switch (frame->get_scope_flag (data_offset))
2406 {
2407 case LOCAL:
2408 return frame->varval (data_offset);
2409
2410 case PERSISTENT:
2411 {
2412 symbol_scope scope = frame->get_scope ();
2413
2414 return scope.persistent_varval (data_offset);
2415 }
2416
2417 case GLOBAL:
2418 return m_evaluator.global_varval (sym.name ());
2419 }
2420
2421 error ("internal error: invalid switch case");
2422}
2423
2425user_fcn_stack_frame::varref (const symbol_record& sym)
2426{
2427 std::size_t frame_offset = sym.frame_offset ();
2428 std::size_t data_offset = sym.data_offset ();
2429
2430 // Follow frame_offset access links to stack frame that holds
2431 // the value.
2432
2433 stack_frame *frame = this;
2434
2435 for (std::size_t i = 0; i < frame_offset; i++)
2436 {
2437 std::shared_ptr<stack_frame> nxt = frame->access_link ();
2438 frame = nxt.get ();
2439 }
2440
2441 if (data_offset >= frame->size ())
2442 frame->resize (data_offset+1);
2443
2444 switch (frame->get_scope_flag (data_offset))
2445 {
2446 case LOCAL:
2447 return frame->varref (data_offset);
2448
2449 case PERSISTENT:
2450 {
2451 symbol_scope scope = frame->get_scope ();
2452
2453 return scope.persistent_varref (data_offset);
2454 }
2455
2456 case GLOBAL:
2457 return m_evaluator.global_varref (sym.name ());
2458 }
2459
2460 error ("internal error: invalid switch case");
2461}
2462
2463std::string
2464user_fcn_stack_frame::inputname (int n, bool ids_only) const
2465{
2466 std::string name;
2467
2468 Array<std::string> arg_names
2469 = m_auto_vars.at (stack_frame::ARG_NAMES).cellstr_value ();
2470
2471 if (n >= 0 && n < arg_names.numel ())
2472 {
2473 name = arg_names(n);
2474
2475 if (ids_only && ! m_static_link->is_variable (name))
2476 name = "";
2477 }
2478
2479 return name;
2480}
2481
2482void
2483user_fcn_stack_frame::mark_scope (const symbol_record& sym, scope_flags flag)
2484{
2485 std::size_t frame_offset = sym.frame_offset ();
2486
2487 if (frame_offset > 0 && (flag == PERSISTENT || flag == GLOBAL))
2488 error ("variables must be made PERSISTENT or GLOBAL in the first scope in which they are used");
2489
2490 std::size_t data_offset = sym.data_offset ();
2491
2492 if (data_offset >= size ())
2493 resize (data_offset+1);
2494
2495 set_scope_flag (data_offset, flag);
2496}
2497
2498void
2499user_fcn_stack_frame::display (bool follow) const
2500{
2501 std::ostream& os = octave_stdout;
2502
2503 os << "-- [user_fcn_stack_frame] (" << this << ") --" << std::endl;
2504 base_value_stack_frame::display (follow);
2505
2506 os << "fcn: " << m_fcn->name ()
2507 << " (" << m_fcn->type_name () << ")" << std::endl;
2508
2509 display_scope (os, get_scope ());
2510}
2511
2512void
2513user_fcn_stack_frame::accept (stack_frame_walker& sfw)
2514{
2515 sfw.visit_user_fcn_stack_frame (*this);
2516}
2517
2518void
2519user_fcn_stack_frame::break_closure_cycles (const std::shared_ptr<stack_frame>& frame)
2520{
2521 for (auto& val : m_values)
2522 val.break_closure_cycles (frame);
2523
2524 if (m_access_link)
2525 m_access_link->break_closure_cycles (frame);
2526}
2527
2529scope_stack_frame::insert_symbol (const std::string& name)
2530{
2531 // There is no access link for scope frames, so there is no other
2532 // frame to search in and the offset must be zero.
2533
2534 symbol_record sym = m_scope.lookup_symbol (name);
2535
2536 if (sym)
2537 return sym;
2538
2539 // If the symbol is not found, insert it. We only need to search in
2540 // the local scope object. This operation should never fail.
2541
2542 sym = m_scope.find_symbol (name);
2543
2544 if (! sym.is_valid ())
2545 error ("unexpected: sym is not valid in scope_stack_frame::insert_symbol - please report this bug");
2546
2547 return sym;
2548}
2549
2551scope_stack_frame::scope_flag (const symbol_record& sym) const
2552{
2553 // There is no access link for scope frames, so the frame
2554 // offset must be zero.
2555
2556 std::size_t data_offset = sym.data_offset ();
2557
2558 if (data_offset >= size ())
2559 return LOCAL;
2560
2561 return get_scope_flag (data_offset);
2562}
2563
2565scope_stack_frame::varval (const symbol_record& sym) const
2566{
2567 // There is no access link for scope frames, so the frame
2568 // offset must be zero.
2569
2570 std::size_t data_offset = sym.data_offset ();
2571
2572 if (data_offset >= size ())
2573 return octave_value ();
2574
2575 switch (get_scope_flag (data_offset))
2576 {
2577 case LOCAL:
2578 return m_values.at (data_offset);
2579
2580 case PERSISTENT:
2581 return m_scope.persistent_varval (data_offset);
2582
2583 case GLOBAL:
2584 return m_evaluator.global_varval (sym.name ());
2585 }
2586
2587 error ("internal error: invalid switch case");
2588}
2589
2591scope_stack_frame::varref (const symbol_record& sym)
2592{
2593 // There is no access link for scope frames, so the frame
2594 // offset must be zero.
2595
2596 std::size_t data_offset = sym.data_offset ();
2597
2598 if (data_offset >= size ())
2599 resize (data_offset+1);
2600
2601 switch (get_scope_flag (data_offset))
2602 {
2603 case LOCAL:
2604 return m_values.at (data_offset);
2605
2606 case PERSISTENT:
2607 return m_scope.persistent_varref (data_offset);
2608
2609 case GLOBAL:
2610 return m_evaluator.global_varref (sym.name ());
2611 }
2612
2613 error ("internal error: invalid switch case");
2614}
2615
2616void
2617scope_stack_frame::mark_scope (const symbol_record& sym,
2618 scope_flags flag)
2619{
2620 // There is no access link for scope frames, so the frame
2621 // offset must be zero.
2622
2623 std::size_t data_offset = sym.data_offset ();
2624
2625 if (data_offset >= size ())
2626 resize (data_offset+1);
2627
2628 set_scope_flag (data_offset, flag);
2629}
2630
2631void
2632scope_stack_frame::display (bool follow) const
2633{
2634 std::ostream& os = octave_stdout;
2635
2636 os << "-- [scope_stack_frame] (" << this << ") --" << std::endl;
2637 base_value_stack_frame::display (follow);
2638
2639 display_scope (os, m_scope);
2640}
2641
2642void
2643scope_stack_frame::accept (stack_frame_walker& sfw)
2644{
2645 sfw.visit_scope_stack_frame (*this);
2646}
2647
2648OCTAVE_END_NAMESPACE(octave)
N Dimensional Array with copy-on-write semantics.
Definition Array.h:130
octave_idx_type numel() const
Number of elements in the array.
Definition Array.h:418
Definition Cell.h:41
static octave_map cat(int dim, octave_idx_type n, const octave_scalar_map *map_list)
Definition oct-map.cc:690
void assign(const std::string &k, const octave_value &val)
Definition oct-map.h:230
octave::symbol_scope scope()
Definition ov-usr-fcn.h:97
void call_object_destructor()
Definition ov.h:1452
bool is_defined() const
Definition ov.h:592
void break_closure_cycles(const std::shared_ptr< octave::stack_frame > &)
octave_idx_type get_count() const
Definition ov.h:421
std::string type_name() const
Definition ov.h:1360
virtual scope_flags get_scope_flag(std::size_t) const
octave_value workspace()
symbol_info_list glob_symbol_info(const std::string &pattern)
std::string fcn_file_name() const
std::list< std::string > variable_names() const
void clear_variables()
virtual octave_value varval(const symbol_record &sym) const =0
void clear(const symbol_record &sym)
void clear_objects()
static stack_frame * create(tree_evaluator &tw, octave_function *fcn, std::size_t index, const std::shared_ptr< stack_frame > &parent_link, const std::shared_ptr< stack_frame > &static_link)
std::shared_ptr< stack_frame > static_link() const
octave_value value(const symbol_record &sym, const std::string &type, const std::list< octave_value_list > &idx) const
bool is_global(const symbol_record &sym) const
void clear_variable_regexp(const std::string &pattern)
void mark_global(const symbol_record &sym)
std::size_t m_index
virtual int line() const
virtual octave_value & varref(const symbol_record &sym)=0
void clear_variable_pattern(const std::string &pattern)
void debug_type(std::ostream &os, int start_line, int end_line) const
std::size_t index() const
symbol_info_list regexp_symbol_info(const std::string &pattern)
virtual void display(bool follow=true) const
virtual void set_scope_flag(std::size_t, scope_flags)
bool is_defined(const symbol_record &sym) const
std::shared_ptr< stack_frame > access_link() const
virtual void accept(stack_frame_walker &sfw)=0
void display_stopped_in_message(std::ostream &os) const
std::shared_ptr< stack_frame > m_parent_link
std::shared_ptr< stack_frame > parent_link() const
bool is_persistent(const symbol_record &sym) const
std::shared_ptr< stack_frame > m_access_link
virtual bool is_user_fcn_frame() const
std::string fcn_name(bool print_subfn=true) const
void assign(const symbol_record &sym, const octave_value &val)
bool is_object(const symbol_record &sym) const
void install_variable(const symbol_record &sym, const octave_value &value, bool global)
void clear_variable(const std::string &name)
std::shared_ptr< stack_frame > m_static_link
std::map< std::string, octave_value > local_vars_map
octave_value who(const string_vector &patterns, bool have_regexp, bool return_list, bool verbose, const std::string &whos_line_fmt, const std::string &msg)
virtual std::string inputname(int n, bool ids_only) const
tree_evaluator & m_evaluator
virtual std::size_t size() const
symbol_info_list all_variables()
virtual void clear_values()
virtual symbol_scope get_scope() const =0
virtual void resize(std::size_t)
bool is_variable(const symbol_record &sym) const
void debug_list(std::ostream &os, int num_lines) const
symbol_info_list make_symbol_info_list(const std::list< symbol_record > &symrec_list) const
std::ostream & list_in_columns(std::ostream &, int width=0, const std::string &prefix="") const
Definition str-vec.cc:201
std::list< std::string > names() const
Definition syminfo.cc:182
void display(std::ostream &os, const std::string &format) const
Definition syminfo.cc:334
octave_value varval(const std::string &name) const
Definition syminfo.cc:170
octave_map map_value(const std::string &caller_function_name, int nesting_level) const
Definition syminfo.cc:193
bool is_valid() const
Definition symrec.h:202
symbol_record dup() const
Definition symrec.h:216
std::string name() const
Definition symrec.h:218
void set_frame_offset(std::size_t offset)
Definition symrec.h:206
std::size_t frame_offset() const
Definition symrec.h:209
std::size_t data_offset() const
Definition symrec.h:214
bool is_formal() const
Definition symrec.h:223
std::string name() const
Definition symscope.h:586
const std::map< std::string, symbol_record > & symbols() const
Definition symscope.h:718
std::list< symbol_record > symbol_list() const
Definition symscope.h:732
bool is_nested() const
Definition symscope.h:430
symbol_record find_symbol(const std::string &name)
Definition symscope.h:489
std::size_t num_symbols() const
Definition symscope.h:414
octave_value persistent_varval(std::size_t data_offset) const
Definition symscope.h:484
symbol_record insert(const std::string &name)
Definition symscope.h:500
octave_value & persistent_varref(std::size_t data_offset)
Definition symscope.h:477
symbol_record lookup_symbol(const std::string &name) const
Definition symscope.h:495
std::shared_ptr< symbol_scope_rep > parent_scope() const
Definition symscope.h:462
std::size_t nesting_depth() const
Definition symscope.h:446
octave_value & global_varref(const std::string &name)
Definition pt-eval.cc:1992
octave_value global_varval(const std::string &name) const
Definition pt-eval.cc:1986
OCTAVE_BEGIN_NAMESPACE(octave) static octave_value daspk_fcn
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
MArray< T > filter(MArray< T > &b, MArray< T > &a, MArray< T > &x, MArray< T > &si, int dim=0)
Definition filter.cc:48
#define OCTAVE_LOCAL_BUFFER(T, buf, size)
Definition oct-locbuf.h:44
@ PERSISTENT
Definition oct-parse.h:124
@ GLOBAL
Definition oct-parse.h:123
#define octave_stdout
Definition pager.h:301
std::size_t format(std::ostream &os, const char *fmt,...)
Definition utils.cc:1514
void display_file_lines(std::ostream &os, const std::string &file_name, int start, int end, int target_line, const std::string &marker, const std::string &who)
Definition utils.cc:807