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