comparison libinterp/corefcn/stack-frame.cc @ 31607:aac27ad79be6 stable

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