Mercurial > octave
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) |