comparison libinterp/octave-value/ov-fcn-handle.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
84 84
85 const std::string octave_fcn_handle::anonymous ("@<anonymous>"); 85 const std::string octave_fcn_handle::anonymous ("@<anonymous>");
86 86
87 OCTAVE_BEGIN_NAMESPACE(octave) 87 OCTAVE_BEGIN_NAMESPACE(octave)
88 88
89 class invalid_fcn_handle : public base_fcn_handle 89 class invalid_fcn_handle : public base_fcn_handle
90 {
91 public:
92
93 invalid_fcn_handle (void) : base_fcn_handle ("<invalid>") { }
94
95 invalid_fcn_handle (const invalid_fcn_handle&) = default;
96
97 ~invalid_fcn_handle (void) = default;
98
99 invalid_fcn_handle * clone (void) const
90 { 100 {
91 public: 101 return new invalid_fcn_handle (*this);
92 102 }
93 invalid_fcn_handle (void) : base_fcn_handle ("<invalid>") { } 103
94 104 std::string type (void) const { return "<invalid>"; }
95 invalid_fcn_handle (const invalid_fcn_handle&) = default; 105
96 106 octave_value_list call (int nargout, const octave_value_list& args);
97 ~invalid_fcn_handle (void) = default; 107 };
98 108
99 invalid_fcn_handle * clone (void) const 109 // Create a handle to an unnamed internal function. There will be no
100 { 110 // way to save and reload it. See, for example, the F__fltk_check__
101 return new invalid_fcn_handle (*this); 111 // function in __init_fltk__.cc.
102 } 112
103 113 class internal_fcn_handle : public base_fcn_handle
104 std::string type (void) const { return "<invalid>"; } 114 {
105 115 public:
106 octave_value_list call (int nargout, const octave_value_list& args); 116
107 }; 117 internal_fcn_handle (const octave_value& fcn)
108 118 : base_fcn_handle ("<internal>"), m_fcn (fcn)
109 // Create a handle to an unnamed internal function. There will be no 119 { }
110 // way to save and reload it. See, for example, the F__fltk_check__ 120
111 // function in __init_fltk__.cc. 121 internal_fcn_handle (const internal_fcn_handle&) = default;
112 122
113 class internal_fcn_handle : public base_fcn_handle 123 ~internal_fcn_handle (void) = default;
124
125 internal_fcn_handle * clone (void) const
114 { 126 {
115 public: 127 return new internal_fcn_handle (*this);
116 128 }
117 internal_fcn_handle (const octave_value& fcn) 129
118 : base_fcn_handle ("<internal>"), m_fcn (fcn) 130 std::string type (void) const { return "<internal>"; }
119 { } 131
120 132 bool is_internal (void) const { return true; }
121 internal_fcn_handle (const internal_fcn_handle&) = default; 133
122 134 octave_value_list call (int nargout, const octave_value_list& args);
123 ~internal_fcn_handle (void) = default; 135
124 136 // FIXME: These must go away. They don't do the right thing for
125 internal_fcn_handle * clone (void) const 137 // scoping or overloads.
126 { 138 octave_function * function_value (bool = false)
127 return new internal_fcn_handle (*this);
128 }
129
130 std::string type (void) const { return "<internal>"; }
131
132 bool is_internal (void) const { return true; }
133
134 octave_value_list call (int nargout, const octave_value_list& args);
135
136 // FIXME: These must go away. They don't do the right thing for
137 // scoping or overloads.
138 octave_function * function_value (bool = false)
139 {
140 return m_fcn.function_value ();
141 }
142
143 octave_user_function * user_function_value (bool = false)
144 {
145 return m_fcn.user_function_value ();
146 }
147
148 octave_value fcn_val (void) { return m_fcn; }
149
150 // Should be const.
151 octave_scalar_map info (void);
152
153 friend bool is_equal_to (const internal_fcn_handle& fh1,
154 const internal_fcn_handle& fh2);
155
156 private:
157
158 octave_value m_fcn;
159 };
160
161 class simple_fcn_handle : public base_fcn_handle
162 { 139 {
163 public: 140 return m_fcn.function_value ();
164 141 }
165 // FIXME: octaveroot is temporary information used when loading 142
166 // handles. Can we avoid using it in the constructor? 143 octave_user_function * user_function_value (bool = false)
167
168 simple_fcn_handle (const std::string& name = "",
169 const std::string& file = "",
170 const std::string& /*octaveroot*/ = "")
171 : base_fcn_handle (name, file), m_fcn ()
172 { }
173
174 simple_fcn_handle (const octave_value& fcn, const std::string& name)
175 : base_fcn_handle (name), m_fcn (fcn)
176 {
177 if (m_fcn.is_defined ())
178 {
179 octave_function *oct_fcn = m_fcn.function_value ();
180
181 if (oct_fcn)
182 m_file = oct_fcn->fcn_file_name ();
183 }
184 }
185
186 simple_fcn_handle (const simple_fcn_handle&) = default;
187
188 ~simple_fcn_handle (void) = default;
189
190 simple_fcn_handle * clone (void) const
191 {
192 return new simple_fcn_handle (*this);
193 }
194
195 std::string type (void) const { return "simple"; }
196
197 bool is_simple (void) const { return true; }
198
199 octave_value_list call (int nargout, const octave_value_list& args);
200
201 // FIXME: These must go away. They don't do the right thing for
202 // scoping or overloads.
203 octave_function * function_value (bool);
204
205 octave_user_function * user_function_value (bool);
206
207 octave_value fcn_val (void);
208
209 // Should be const.
210 octave_scalar_map info (void);
211
212 bool save_ascii (std::ostream& os);
213
214 bool load_ascii (std::istream& is);
215
216 bool save_binary (std::ostream& os, bool save_as_floats);
217
218 bool load_binary (std::istream& is, bool swap, mach_info::float_format fmt);
219
220 bool save_hdf5 (octave_hdf5_id loc_hid, const char *name,
221 bool save_as_floats);
222
223 bool load_hdf5 (octave_hdf5_id& group_hid, octave_hdf5_id& space_hid,
224 octave_hdf5_id& type_hid);
225
226 void print_raw (std::ostream& os, bool pr_as_read_syntax,
227 int current_print_indent_level) const;
228
229 friend bool is_equal_to (const simple_fcn_handle& fh1,
230 const simple_fcn_handle& fh2);
231
232 private:
233
234 octave_value m_fcn;
235 };
236
237 class scoped_fcn_handle : public base_fcn_handle
238 { 144 {
239 public: 145 return m_fcn.user_function_value ();
240 146 }
241 // FIXME: octaveroot is temporary information used when loading 147
242 // handles. Can we avoid using it in the constructor? 148 octave_value fcn_val (void) { return m_fcn; }
243 149
244 scoped_fcn_handle (const std::string& name = "", 150 // Should be const.
245 const std::string& file = "", 151 octave_scalar_map info (void);
246 const std::string& /*octaveroot*/ = "") 152
247 : base_fcn_handle (name, file) 153 friend bool is_equal_to (const internal_fcn_handle& fh1,
248 { } 154 const internal_fcn_handle& fh2);
249 155
250 scoped_fcn_handle (const octave_value& fcn, const std::string& name, 156 private:
251 const std::list<std::string>& parentage); 157
252 158 octave_value m_fcn;
253 scoped_fcn_handle (const scoped_fcn_handle&) = default; 159 };
254 160
255 ~scoped_fcn_handle (void) = default; 161 class simple_fcn_handle : public base_fcn_handle
256 162 {
257 scoped_fcn_handle * clone (void) const 163 public:
258 { 164
259 return new scoped_fcn_handle (*this); 165 // FIXME: octaveroot is temporary information used when loading
260 } 166 // handles. Can we avoid using it in the constructor?
261 167
262 std::string type (void) const { return "scopedfunction"; } 168 simple_fcn_handle (const std::string& name = "",
263 169 const std::string& file = "",
264 bool is_scoped (void) const { return true; } 170 const std::string& /*octaveroot*/ = "")
265 171 : base_fcn_handle (name, file), m_fcn ()
266 octave_value_list call (int nargout, const octave_value_list& args); 172 { }
267 173
268 // FIXME: These must go away. They don't do the right thing for 174 simple_fcn_handle (const octave_value& fcn, const std::string& name)
269 // scoping or overloads. 175 : base_fcn_handle (name), m_fcn (fcn)
270 octave_function * function_value (bool = false)
271 {
272 return m_fcn.function_value ();
273 }
274
275 octave_user_function * user_function_value (bool = false)
276 {
277 return m_fcn.user_function_value ();
278 }
279
280 octave_value fcn_val (void) { return m_fcn; }
281
282 // Should be const.
283 octave_scalar_map info (void);
284
285 bool save_ascii (std::ostream& os);
286
287 bool load_ascii (std::istream& is);
288
289 bool save_binary (std::ostream& os, bool save_as_floats);
290
291 bool load_binary (std::istream& is, bool swap, mach_info::float_format fmt);
292
293 bool save_hdf5 (octave_hdf5_id loc_id, const char *name,
294 bool save_as_floats);
295
296 bool load_hdf5 (octave_hdf5_id& group_hid, octave_hdf5_id& space_hid,
297 octave_hdf5_id& type_hid);
298
299 void print_raw (std::ostream&, bool pr_as_read_syntax,
300 int current_print_indent_level) const;
301
302 friend bool is_equal_to (const scoped_fcn_handle& fh1,
303 const scoped_fcn_handle& fh2);
304
305 protected:
306
307 void find_function (void);
308
309 // The function we are handling.
310 octave_value m_fcn;
311
312 // List of parent function names. The first element is the name of
313 // m_fcn.
314 std::list<std::string> m_parentage;
315 };
316
317 class base_nested_fcn_handle : public base_fcn_handle
318 { 176 {
319 public:
320
321 // FIXME: octaveroot is temporary information used when loading
322 // handles. Can we avoid using it in the constructor?
323
324 base_nested_fcn_handle (const std::string& name = "",
325 const std::string& file = "",
326 const std::string& /*octaveroot*/ = "")
327 : base_fcn_handle (name, file)
328 { }
329
330 base_nested_fcn_handle (const octave_value& fcn, const std::string& name)
331 : base_fcn_handle (name), m_fcn (fcn)
332 { }
333
334 std::string type (void) const { return "nested"; }
335
336 using base_fcn_handle::is_nested;
337
338 bool is_nested (void) const { return true; }
339
340 // FIXME: These must go away. They don't do the right thing for
341 // scoping or overloads.
342 octave_function * function_value (bool = false)
343 {
344 return m_fcn.function_value ();
345 }
346
347 octave_user_function * user_function_value (bool = false)
348 {
349 return m_fcn.user_function_value ();
350 }
351
352 octave_value fcn_val (void) { return m_fcn; }
353
354 virtual octave_value workspace (void) const = 0;
355
356 // Should be const.
357 octave_scalar_map info (void);
358
359 bool save_ascii (std::ostream& os);
360
361 bool load_ascii (std::istream& is);
362
363 bool save_binary (std::ostream& os, bool save_as_floats);
364
365 bool load_binary (std::istream& is, bool swap, mach_info::float_format fmt);
366
367 bool save_hdf5 (octave_hdf5_id loc_id, const char *name,
368 bool save_as_floats);
369
370 bool load_hdf5 (octave_hdf5_id& group_hid, octave_hdf5_id& space_hid,
371 octave_hdf5_id& type_hid);
372
373 void print_raw (std::ostream&, bool pr_as_read_syntax,
374 int current_print_indent_level) const;
375
376 protected:
377
378 // The function we are handling.
379 octave_value m_fcn;
380 };
381
382 class nested_fcn_handle : public base_nested_fcn_handle
383 {
384 public:
385
386 // FIXME: octaveroot is temporary information used when loading
387 // handles. Can we avoid using it in the constructor?
388
389 nested_fcn_handle (const std::string& name = "",
390 const std::string& file = "",
391 const std::string& octaveroot = "")
392 : base_nested_fcn_handle (name, file, octaveroot)
393 { }
394
395 nested_fcn_handle (const octave_value& fcn, const std::string& name,
396 const std::shared_ptr<stack_frame>& stack_context)
397 : base_nested_fcn_handle (fcn, name), m_stack_context (stack_context)
398 {
399 if (m_stack_context)
400 m_stack_context->mark_closure_context ();
401 }
402
403 nested_fcn_handle (const nested_fcn_handle&) = default;
404
405 ~nested_fcn_handle (void) = default;
406
407 using base_nested_fcn_handle::is_nested;
408
409 bool is_nested (const std::shared_ptr<stack_frame>& frame) const
410 {
411 return frame == m_stack_context;
412 }
413
414 nested_fcn_handle * clone (void) const
415 {
416 return new nested_fcn_handle (*this);
417 }
418
419 octave_value make_weak_nested_handle (void) const;
420
421 octave_value_list call (int nargout, const octave_value_list& args);
422
423 octave_value workspace (void) const;
424
425 friend bool is_equal_to (const nested_fcn_handle& fh1,
426 const nested_fcn_handle& fh2);
427
428 std::shared_ptr<stack_frame> stack_context (void) const
429 {
430 return m_stack_context;
431 }
432
433 protected:
434
435 // Pointer to closure stack frames.
436 std::shared_ptr<stack_frame> m_stack_context;
437 };
438
439 class weak_nested_fcn_handle : public base_nested_fcn_handle
440 {
441 public:
442
443 weak_nested_fcn_handle (const nested_fcn_handle& nfh)
444 : base_nested_fcn_handle (nfh), m_stack_context (nfh.stack_context ())
445 { }
446
447 weak_nested_fcn_handle (const weak_nested_fcn_handle&) = default;
448
449 ~weak_nested_fcn_handle (void) = default;
450
451 weak_nested_fcn_handle * clone (void) const
452 {
453 return new weak_nested_fcn_handle (*this);
454 }
455
456 bool is_weak_nested (void) const { return true; }
457
458 octave_value_list call (int nargout, const octave_value_list& args);
459
460 octave_value workspace (void) const;
461
462 friend bool is_equal_to (const weak_nested_fcn_handle& fh1,
463 const weak_nested_fcn_handle& fh2);
464
465 protected:
466
467 // Pointer to closure stack frames.
468 std::weak_ptr<stack_frame> m_stack_context;
469 };
470
471 class class_simple_fcn_handle : public base_fcn_handle
472 {
473 public:
474
475 // FIXME: octaveroot is temporary information used when loading
476 // handles. Can we avoid using it in the constructor?
477
478 class_simple_fcn_handle (const std::string& name,
479 const std::string& file,
480 const std::string& /*octaveroot*/)
481 : base_fcn_handle (name, file)
482 { }
483
484 // FIXME: is the method name supposed to be just the method name or
485 // also contain the object name?
486
487 class_simple_fcn_handle (const std::string& class_nm,
488 const std::string& meth_nm);
489
490 class_simple_fcn_handle (const octave_value& fcn,
491 const std::string& class_nm,
492 const std::string& meth_nm);
493
494 class_simple_fcn_handle (const octave_value& obj, const octave_value& fcn,
495 const std::string& class_nm,
496 const std::string& meth_nm);
497
498 class_simple_fcn_handle (const class_simple_fcn_handle&) = default;
499
500 ~class_simple_fcn_handle (void) = default;
501
502 class_simple_fcn_handle * clone (void) const
503 {
504 return new class_simple_fcn_handle (*this);
505 }
506
507 std::string type (void) const { return "classsimple"; }
508
509 bool is_class_simple (void) const { return true; }
510
511 octave_value_list call (int nargout, const octave_value_list& args);
512
513 // FIXME: These must go away. They don't do the right thing for
514 // scoping or overloads.
515 octave_function * function_value (bool = false)
516 {
517 // FIXME: Shouldn't the lookup rules here match those used in the
518 // call method?
519
520 if (m_fcn.is_defined ())
521 return m_fcn.function_value ();
522
523 symbol_table& symtab = __get_symbol_table__ ();
524
525 // FIXME: is caching the correct thing to do?
526 // Cache this value so that the pointer will be valid as long as the
527 // function handle object is valid.
528
529 // FIXME: This should probably dispatch to the respective class method.
530 // But that breaks if a function handle is used in a class method with
531 // e.g. bsxfun with arguments of a different class (see bug #59661).
532 // m_fcn = symtab.find_method (m_name, m_dispatch_class);
533 m_fcn = symtab.find_function (m_name, octave_value_list ());
534
535 return m_fcn.is_defined () ? m_fcn.function_value () : nullptr;
536 }
537
538 octave_user_function * user_function_value (bool = false)
539 {
540 return m_fcn.user_function_value ();
541 }
542
543 octave_value fcn_val (void) { return m_fcn; }
544
545 // Should be const.
546 octave_scalar_map info (void);
547
548 std::string dispatch_class (void) const { return m_dispatch_class; }
549
550 bool save_ascii (std::ostream& os);
551
552 bool load_ascii (std::istream& is);
553
554 bool save_binary (std::ostream& os, bool save_as_floats);
555
556 bool load_binary (std::istream& is, bool swap, mach_info::float_format fmt);
557
558 bool save_hdf5 (octave_hdf5_id loc_id, const char *name,
559 bool save_as_floats);
560
561 bool load_hdf5 (octave_hdf5_id& group_hid, octave_hdf5_id& space_hid,
562 octave_hdf5_id& type_hid);
563
564 void print_raw (std::ostream&, bool pr_as_read_syntax,
565 int current_print_indent_level) const;
566
567 friend bool is_equal_to (const class_simple_fcn_handle& fh1,
568 const class_simple_fcn_handle& fh2);
569
570 protected:
571
572 // The object containing the method we are handing.
573 octave_value m_obj;
574
575 // The method we are handling.
576 octave_value m_fcn;
577
578 // Name of the class that m_fcn belongs to.
579 std::string m_dispatch_class;
580 };
581
582 // Handles to anonymous functions are similar to handles to nested
583 // functions. If they are created in a context that contains nested
584 // functions, then they store a link to the parent call stack frames
585 // that are active when they are created. These call stack frames
586 // (closure frames) provide access to variables needed by any nested
587 // functions that are called from the anonymous function. Anonymous
588 // functions also store a list of values from their parent scope
589 // corresponding to the symbols in the anonymous function. This list
590 // of values captures the variable values that are visible in the
591 // scope where they are created.
592 //
593 // Note that because handles to anonymous and nested functions capture
594 // call stack frames when they are created, they will cause deletion
595 // of the values in those frames to be deferred until the handles to
596 // the anonymous or nested functions are deleted.
597 //
598 // Would it be possible to avoid storing the closure frames for
599 // handles to anonymous functions if we can determine that the
600 // anonymous function has no unbound variables (or parameters, which
601 // could be handles to nested functions?) or if it is not created in a
602 // context that contains nested functions?
603 //
604 // Would it be possible to define anonymous functions as a special
605 // type of nested function object that also has an variable
606 // initialization list associated with it?
607
608 class base_anonymous_fcn_handle : public base_fcn_handle
609 {
610 public:
611
612 static const std::string anonymous;
613
614 // Setting NAME here is a bit of a kluge to cope with a bad choice
615 // made to append the number of local variables to the @<anonymous>
616 // tag in the binary file format. See also the save_binary and
617 // load_binary functions.
618
619 base_anonymous_fcn_handle (const std::string& name = "")
620 : base_fcn_handle (name)
621 { }
622
623 base_anonymous_fcn_handle (const octave_value& fcn,
624 const stack_frame::local_vars_map& local_vars)
625 : base_fcn_handle (anonymous), m_fcn (fcn), m_local_vars (local_vars)
626 { }
627
628 base_anonymous_fcn_handle (const base_anonymous_fcn_handle&) = default;
629
630 ~base_anonymous_fcn_handle (void) = default;
631
632 std::string type (void) const { return "anonymous"; }
633
634 bool is_anonymous (void) const { return true; }
635
636 // FIXME: These must go away. They don't do the right thing for
637 // scoping or overloads.
638 octave_function * function_value (bool = false)
639 {
640 return m_fcn.function_value ();
641 }
642
643 octave_user_function * user_function_value (bool = false)
644 {
645 return m_fcn.user_function_value ();
646 }
647
648 octave_value fcn_val (void) { return m_fcn; }
649
650 virtual octave_value workspace (void) const = 0;
651
652 // Should be const.
653 octave_scalar_map info (void);
654
655 bool save_ascii (std::ostream& os);
656
657 bool load_ascii (std::istream& is);
658
659 bool save_binary (std::ostream& os, bool save_as_floats);
660
661 bool load_binary (std::istream& is, bool swap, mach_info::float_format fmt);
662
663 bool save_hdf5 (octave_hdf5_id loc_id, const char *name,
664 bool save_as_floats);
665
666 bool load_hdf5 (octave_hdf5_id& group_hid, octave_hdf5_id& space_hid,
667 octave_hdf5_id& type_hid);
668
669 void print_raw (std::ostream&, bool pr_as_read_syntax,
670 int current_print_indent_level) const;
671
672 // Anonymous function handles are printed without a newline.
673 bool print_as_scalar (void) const { return false; }
674
675 bool parse (const std::string& fcn_text);
676
677 protected:
678
679 // The function we are handling.
680 octave_value m_fcn;
681
682 // List of captured variable values for anonymous fucntions.
683 stack_frame::local_vars_map m_local_vars;
684 };
685
686 class anonymous_fcn_handle : public base_anonymous_fcn_handle
687 {
688 public:
689
690 using base_anonymous_fcn_handle::anonymous;
691
692 // Setting NAME here is a bit of a kluge to cope with a bad choice
693 // made to append the number of local variables to the @<anonymous>
694 // tag in the binary file format. See also the save_binary and
695 // load_binary functions.
696
697 anonymous_fcn_handle (const std::string& name = "")
698 : base_anonymous_fcn_handle (name), m_stack_context ()
699 { }
700
701 anonymous_fcn_handle (const octave_value& fcn,
702 const stack_frame::local_vars_map& local_vars,
703 const std::shared_ptr<stack_frame>& stack_context = std::shared_ptr<stack_frame> ());
704
705 anonymous_fcn_handle (const anonymous_fcn_handle&) = default;
706
707 ~anonymous_fcn_handle (void) = default;
708
709 anonymous_fcn_handle * clone (void) const
710 {
711 return new anonymous_fcn_handle (*this);
712 }
713
714 octave_value make_weak_anonymous_handle (void) const;
715
716 octave_value_list call (int nargout, const octave_value_list& args);
717
718 octave_value workspace (void) const;
719
720 friend bool is_equal_to (const anonymous_fcn_handle& fh1,
721 const anonymous_fcn_handle& fh2);
722
723 std::shared_ptr<stack_frame> stack_context (void) const
724 {
725 return m_stack_context;
726 }
727
728 protected:
729
730 // Pointer to closure stack frames.
731 std::shared_ptr<stack_frame> m_stack_context;
732 };
733
734 class weak_anonymous_fcn_handle : public base_anonymous_fcn_handle
735 {
736 public:
737
738 using base_anonymous_fcn_handle::anonymous;
739
740 weak_anonymous_fcn_handle (const anonymous_fcn_handle& afh)
741 : base_anonymous_fcn_handle (afh), m_stack_context (afh.stack_context ())
742 { }
743
744 weak_anonymous_fcn_handle (const weak_anonymous_fcn_handle&) = default;
745
746 ~weak_anonymous_fcn_handle (void) = default;
747
748 weak_anonymous_fcn_handle * clone (void) const
749 {
750 return new weak_anonymous_fcn_handle (*this);
751 }
752
753 bool is_weak_anonymous (void) const { return true; }
754
755 octave_value_list call (int nargout, const octave_value_list& args);
756
757 octave_value workspace (void) const;
758
759 friend bool is_equal_to (const weak_anonymous_fcn_handle& fh1,
760 const weak_anonymous_fcn_handle& fh2);
761
762 protected:
763
764 // Pointer to closure stack frames.
765 std::weak_ptr<stack_frame> m_stack_context;
766 };
767
768 extern bool is_equal_to (const anonymous_fcn_handle& fh1,
769 const anonymous_fcn_handle& fh2);
770
771 static void err_invalid_fcn_handle (const std::string& name)
772 {
773 error ("invalid function handle, unable to find function for @%s",
774 name.c_str ());
775 }
776
777 octave_value base_fcn_handle::make_weak_nested_handle (void) const
778 {
779 std::string type_str = type ();
780 error ("invalid conversion from %s handle to weak nestead handle",
781 type_str.c_str ());
782 }
783
784 octave_value base_fcn_handle::make_weak_anonymous_handle (void) const
785 {
786 std::string type_str = type ();
787 error ("invalid conversion from %s handle to weak anonymous handle",
788 type_str.c_str ());
789 }
790
791 octave_value_list
792 base_fcn_handle::subsref (const std::string& type,
793 const std::list<octave_value_list>& idx,
794 int nargout)
795 {
796 octave_value_list retval;
797
798 switch (type[0])
799 {
800 case '(':
801 {
802 int tmp_nargout = (type.length () > 1 && nargout == 0) ? 1 : nargout;
803
804 retval = call (tmp_nargout, idx.front ());
805 }
806 break;
807
808 case '{':
809 case '.':
810 error ("function handle cannot be indexed with %c", type[0]);
811
812 default:
813 panic_impossible ();
814 }
815
816 // FIXME: perhaps there should be an
817 // octave_value_list::next_subsref member function? See also
818 // octave_builtin::subsref.
819
820 if (idx.size () > 1)
821 retval = retval(0).next_subsref (nargout, type, idx);
822
823 return retval;
824 }
825
826 octave_value
827 base_fcn_handle::convert_to_str_internal (bool, bool, char type) const
828 {
829 std::ostringstream buf;
830 print_raw (buf, true, 0);
831 return octave_value (buf.str (), type);
832 }
833
834 bool
835 base_fcn_handle::save_ascii (std::ostream&)
836 {
837 unimplemented ("save", "text");
838
839 return true;
840 }
841
842 bool
843 base_fcn_handle::load_ascii (std::istream&)
844 {
845 unimplemented ("load", "text");
846
847 return true;
848 }
849
850 bool
851 base_fcn_handle::save_binary (std::ostream&, bool)
852 {
853 unimplemented ("save", "binary");
854
855 return true;
856 }
857
858 bool
859 base_fcn_handle::load_binary (std::istream&, bool, mach_info::float_format)
860 {
861 unimplemented ("load", "binary");
862
863 return true;
864 }
865
866 bool
867 base_fcn_handle::save_hdf5 (octave_hdf5_id, const char *, bool)
868 {
869 unimplemented ("save", "hdf5");
870
871 return true;
872 }
873
874 bool
875 base_fcn_handle::load_hdf5 (octave_hdf5_id&, octave_hdf5_id&, octave_hdf5_id&)
876 {
877 unimplemented ("load", "hdf5");
878
879 return true;
880 }
881
882 void base_fcn_handle::warn_load (const char *file_type) const
883 {
884 std::string obj_type = type ();
885
886 warning_with_id
887 ("Octave:load-save-unavailable",
888 "%s: loading %s files not available in this version of Octave",
889 obj_type.c_str (), file_type);
890 }
891
892 void base_fcn_handle::warn_save (const char *file_type) const
893 {
894 std::string obj_type = type ();
895
896 warning_with_id
897 ("Octave:load-save-unavailable",
898 "%s: saving %s files not available in this version of Octave",
899 obj_type.c_str (), file_type);
900 }
901
902 void base_fcn_handle::unimplemented (const char *op, const char *fmt) const
903 {
904 std::string htype = type ();
905
906 warning ("%s for %s handles with %s format is not implemented",
907 op, htype.c_str (), fmt);
908 }
909
910 octave_value_list
911 invalid_fcn_handle::call (int, const octave_value_list&)
912 {
913 error ("invalid call to invalid function handle");
914 }
915
916 octave_value_list
917 internal_fcn_handle::call (int nargout, const octave_value_list& args)
918 {
919 interpreter& interp = __get_interpreter__ ();
920
921 return interp.feval (m_fcn, args, nargout);
922 }
923
924 octave_scalar_map internal_fcn_handle::info (void)
925 {
926 octave_scalar_map m;
927
928 m.setfield ("function", fcn_name ());
929 m.setfield ("type", type ());
930 m.setfield ("file", "");
931
932 return m;
933 }
934
935 bool is_equal_to (const internal_fcn_handle& fh1,
936 const internal_fcn_handle& fh2)
937 {
938 if (fh1.m_name == fh2.m_name
939 && fh1.m_fcn.is_defined () && fh2.m_fcn.is_defined ())
940 return fh1.m_fcn.is_copy_of (fh2.m_fcn);
941 else
942 return false;
943 }
944
945 octave_value_list
946 simple_fcn_handle::call (int nargout, const octave_value_list& args)
947 {
948 // FIXME: if m_name has a '.' in the name, lookup first component. If
949 // it is a classdef meta object, then build TYPE and IDX arguments and
950 // make a subsref call using them.
951
952 interpreter& interp = __get_interpreter__ ();
953
954 octave_value fcn_to_call;
955
956 // The following code is similar to part of
957 // tree_evaluator::visit_index_expression but simpler because it
958 // handles a more restricted case.
959
960 symbol_table& symtab = interp.get_symbol_table ();
961
962 std::size_t pos = m_name.find ('.');
963
964 if (pos != std::string::npos)
965 {
966 // FIXME: check to see which of these cases actually work in
967 // Octave and Matlab. For the last two, assume handle is
968 // created before object is defined as an object.
969 //
970 // We can have one of
971 //
972 // pkg-list . fcn (args)
973 // pkg-list . cls . meth (args)
974 // class-name . method (args)
975 // class-name . static-method (args)
976 // object . method (args)
977 // object . static-method (args)
978
979 // Evaluate package elements until we find a function,
980 // classdef object, or classdef_meta object that is not a
981 // package. An object may only appear as the first element,
982 // then it must be followed directly by a function name.
983
984 std::size_t beg = 0;
985 std::size_t end = pos;
986
987 std::vector<std::string> idx_elts;
988
989 while (true)
990 {
991 end = m_name.find ('.', beg);
992
993 idx_elts.push_back (m_name.substr (beg, end-beg));
994
995 if (end == std::string::npos)
996 break;
997
998 beg = end+1;
999 }
1000
1001 std::size_t n_elts = idx_elts.size ();
1002
1003 bool have_object = false;
1004 octave_value partial_expr_val;
1005
1006 // Lazy evaluation. The first element was not known to be defined
1007 // as an object in the scope where the handle was created. See if
1008 // there is a definition in the current scope.
1009
1010 partial_expr_val = interp.varval (idx_elts[0]);
1011
1012 if (partial_expr_val.is_defined ())
1013 {
1014 if (! partial_expr_val.is_classdef_object () || n_elts != 2)
1015 err_invalid_fcn_handle (m_name);
1016
1017 have_object = true;
1018 }
1019 else
1020 partial_expr_val = symtab.find_function (idx_elts[0], ovl ());
1021
1022 std::string type;
1023 std::list<octave_value_list> arg_list;
1024
1025 for (std::size_t i = 1; i < n_elts; i++)
1026 {
1027 if (partial_expr_val.is_package ())
1028 {
1029 if (have_object)
1030 err_invalid_fcn_handle (m_name);
1031
1032 type = ".";
1033 arg_list.push_back (ovl (idx_elts[i]));
1034
1035 try
1036 {
1037 // Silently ignore extra output values.
1038
1039 octave_value_list tmp_list
1040 = partial_expr_val.subsref (type, arg_list, 0);
1041
1042 partial_expr_val
1043 = tmp_list.length () ? tmp_list(0) : octave_value ();
1044
1045 if (partial_expr_val.is_cs_list ())
1046 err_invalid_fcn_handle (m_name);
1047
1048 arg_list.clear ();
1049 }
1050 catch (const index_exception&)
1051 {
1052 err_invalid_fcn_handle (m_name);
1053 }
1054 }
1055 else if (have_object || partial_expr_val.is_classdef_meta ())
1056 {
1057 // Object or class name must be the next to the last
1058 // element (it was the previous one, so if this is the
1059 // final element, it should be a classdef method,
1060 // but we'll let the classdef or classdef_meta subsref
1061 // function sort that out.
1062
1063 if (i != n_elts-1)
1064 err_invalid_fcn_handle (m_name);
1065
1066 type = ".(";
1067 arg_list.push_back (ovl (idx_elts[i]));
1068 arg_list.push_back (args);
1069
1070 return partial_expr_val.subsref (type, arg_list, nargout);
1071 }
1072 else
1073 err_invalid_fcn_handle (m_name);
1074 }
1075
1076 // If we get here, we must have a function to call.
1077
1078 if (! partial_expr_val.is_function ())
1079 err_invalid_fcn_handle (m_name);
1080
1081 fcn_to_call = partial_expr_val;
1082 }
1083 else
1084 {
1085 // No "." in the name.
1086
1087 // Perform function lookup given current arguments. We'll need
1088 // to do this regardless of whether a function was found when
1089 // the handle was created.
1090
1091 octave_value ov_fcn = symtab.find_function (m_name, args);
1092
1093 if (m_fcn.is_defined ())
1094 {
1095 // A simple function was found when the handle was created.
1096 // Use that unless we find a class method to override it.
1097
1098 fcn_to_call = m_fcn;
1099
1100 if (ov_fcn.is_defined ())
1101 {
1102 octave_function *fcn = ov_fcn.function_value ();
1103
1104 std::string dispatch_class = fcn->dispatch_class ();
1105
1106 if (fcn->is_class_method ())
1107 {
1108 // Function found through lookup is a class method
1109 // so use it instead of the simple one found when
1110 // the handle was created.
1111
1112 fcn_to_call = ov_fcn;
1113 }
1114 }
1115 }
1116 else
1117 {
1118 // There was no simple function found when the handle was
1119 // created so use the one found here (if any).
1120
1121 fcn_to_call = ov_fcn;
1122 }
1123 }
1124
1125 if (! fcn_to_call.is_defined ())
1126 err_invalid_fcn_handle (m_name);
1127
1128 return interp.feval (fcn_to_call, args, nargout);
1129 }
1130
1131 octave_function * simple_fcn_handle::function_value (bool)
1132 {
1133 // FIXME: Shouldn't the lookup rules here match those used in the
1134 // call method?
1135
1136 if (m_fcn.is_defined ())
1137 return m_fcn.function_value ();
1138
1139 symbol_table& symtab = __get_symbol_table__ ();
1140
1141 // FIXME: is caching the correct thing to do?
1142 // Cache this value so that the pointer will be valid as long as the
1143 // function handle object is valid.
1144
1145 m_fcn = symtab.find_function (m_name, octave_value_list ());
1146
1147 return m_fcn.is_defined () ? m_fcn.function_value () : nullptr;
1148 }
1149
1150 octave_user_function * simple_fcn_handle::user_function_value (bool)
1151 {
1152 // FIXME: Shouldn't the lookup rules here match those used in the
1153 // call method?
1154
1155 if (m_fcn.is_defined ())
1156 return m_fcn.user_function_value ();
1157
1158 symbol_table& symtab = __get_symbol_table__ ();
1159
1160 // FIXME: is caching the correct thing to do?
1161 // Cache this value so that the pointer will be valid as long as the
1162 // function handle object is valid.
1163
1164 m_fcn = symtab.find_user_function (m_name);
1165
1166 return m_fcn.is_defined () ? m_fcn.user_function_value () : nullptr;
1167 }
1168
1169 octave_value simple_fcn_handle::fcn_val (void)
1170 {
1171 if (m_fcn.is_defined ())
1172 return m_fcn;
1173
1174 symbol_table& symtab = __get_symbol_table__ ();
1175
1176 // FIXME: is caching the correct thing to do?
1177 // Cache this value so that the pointer will be valid as long as the
1178 // function handle object is valid.
1179
1180 m_fcn = symtab.find_user_function (m_name);
1181
1182 return m_fcn;
1183 }
1184
1185 octave_scalar_map simple_fcn_handle::info (void)
1186 {
1187 octave_scalar_map m;
1188
1189 m.setfield ("function", fcn_name ());
1190 m.setfield ("type", type ());
1191 // When is FILE defined for simple function handles?
1192 m.setfield ("file", file ());
1193
1194 return m;
1195 }
1196
1197 bool simple_fcn_handle::save_ascii (std::ostream& os)
1198 {
1199 os << "# octaveroot: " << config::octave_exec_home () << "\n";
1200
1201 std::string fnm = file ();
1202 if (! fnm.empty ())
1203 os << "# path: " << fnm << "\n";
1204
1205 os << "# subtype: " << type () << "\n";
1206
1207 os << m_name << "\n";
1208
1209 return true;
1210 }
1211
1212 bool simple_fcn_handle::load_ascii (std::istream& is)
1213 {
1214 // FIXME: If m_file is not empty, try to load the file and define
1215 // the function? Is it an error if that fails? Or should this job
1216 // always be deferred until the handle is used?
1217
1218 return is.good ();
1219 }
1220
1221 bool simple_fcn_handle::save_binary (std::ostream& os, bool)
1222 {
1223 std::ostringstream nmbuf;
1224
1225 // When is FILE defined for simple function handles?
1226 std::string fnm;
1227
1228 nmbuf << m_name << "@<simple>\n" << config::octave_exec_home ()
1229 << "\n" << fnm;
1230
1231 std::string buf_str = nmbuf.str ();
1232 int32_t tmp = buf_str.length ();
1233 os.write (reinterpret_cast<char *> (&tmp), 4);
1234 os.write (buf_str.c_str (), buf_str.length ());
1235
1236 return true;
1237 }
1238
1239 bool simple_fcn_handle::load_binary (std::istream& is, bool,
1240 mach_info::float_format)
1241 {
1242 return is.good ();
1243 }
1244
1245 bool simple_fcn_handle::save_hdf5 (octave_hdf5_id loc_id, const char *name,
1246 bool)
1247 {
1248 #if defined (HAVE_HDF5)
1249
1250 bool retval = true;
1251
1252 octave_hdf5_id group_hid = -1;
1253 #if defined (HAVE_HDF5_18)
1254 group_hid = H5Gcreate (loc_id, name, octave_H5P_DEFAULT, octave_H5P_DEFAULT,
1255 octave_H5P_DEFAULT);
1256 #else
1257 group_hid = H5Gcreate (loc_id, name, 0);
1258 #endif
1259 if (group_hid < 0)
1260 return false;
1261
1262 octave_hdf5_id space_hid, data_hid, type_hid;
1263 space_hid = data_hid = type_hid = -1;
1264
1265 // attach the type of the variable
1266 type_hid = H5Tcopy (H5T_C_S1);
1267 H5Tset_size (type_hid, m_name.length () + 1);
1268 if (type_hid < 0)
1269 {
1270 H5Gclose (group_hid);
1271 return false;
1272 }
1273
1274 OCTAVE_LOCAL_BUFFER (hsize_t, hdims, 2);
1275 hdims[0] = 0;
1276 hdims[1] = 0;
1277 space_hid = H5Screate_simple (0, hdims, nullptr);
1278 if (space_hid < 0)
1279 {
1280 H5Tclose (type_hid);
1281 H5Gclose (group_hid);
1282 return false;
1283 }
1284 #if defined (HAVE_HDF5_18)
1285 data_hid = H5Dcreate (group_hid, "nm", type_hid, space_hid,
1286 octave_H5P_DEFAULT, octave_H5P_DEFAULT,
1287 octave_H5P_DEFAULT);
1288 #else
1289 data_hid = H5Dcreate (group_hid, "nm", type_hid, space_hid,
1290 octave_H5P_DEFAULT);
1291 #endif
1292 if (data_hid < 0
1293 || H5Dwrite (data_hid, type_hid, octave_H5S_ALL, octave_H5S_ALL,
1294 octave_H5P_DEFAULT, m_name.c_str ()) < 0)
1295 {
1296 H5Sclose (space_hid);
1297 H5Tclose (type_hid);
1298 H5Gclose (group_hid);
1299 return false;
1300 }
1301 H5Dclose (data_hid);
1302
1303 std::string octaveroot = config::octave_exec_home ();
1304
1305 // When is FILE defined for simple fucntion handles?
1306 std::string fpath;
1307
1308 H5Sclose (space_hid);
1309 hdims[0] = 1;
1310 hdims[1] = octaveroot.length ();
1311 space_hid = H5Screate_simple (0, hdims, nullptr);
1312 if (space_hid < 0)
1313 {
1314 H5Tclose (type_hid);
1315 H5Gclose (group_hid);
1316 return false;
1317 }
1318
1319 H5Tclose (type_hid);
1320 type_hid = H5Tcopy (H5T_C_S1);
1321 H5Tset_size (type_hid, octaveroot.length () + 1);
1322 octave_hdf5_id a_id;
1323 #if defined (HAVE_HDF5_18)
1324 a_id = H5Acreate (group_hid, "OCTAVEROOT", type_hid, space_hid,
1325 octave_H5P_DEFAULT, octave_H5P_DEFAULT);
1326 #else
1327 a_id = H5Acreate (group_hid, "OCTAVEROOT", type_hid, space_hid,
1328 octave_H5P_DEFAULT);
1329 #endif
1330
1331 if (a_id >= 0)
1332 {
1333 retval = (H5Awrite (a_id, type_hid, octaveroot.c_str ()) >= 0);
1334
1335 H5Aclose (a_id);
1336 }
1337 else
1338 {
1339 H5Sclose (space_hid);
1340 H5Tclose (type_hid);
1341 H5Gclose (group_hid);
1342 return false;
1343 }
1344
1345 H5Sclose (space_hid);
1346 hdims[0] = 1;
1347 hdims[1] = fpath.length ();
1348 space_hid = H5Screate_simple (0, hdims, nullptr);
1349 if (space_hid < 0)
1350 {
1351 H5Tclose (type_hid);
1352 H5Gclose (group_hid);
1353 return false;
1354 }
1355
1356 H5Tclose (type_hid);
1357 type_hid = H5Tcopy (H5T_C_S1);
1358 H5Tset_size (type_hid, fpath.length () + 1);
1359
1360 #if defined (HAVE_HDF5_18)
1361 a_id = H5Acreate (group_hid, "FILE", type_hid, space_hid,
1362 octave_H5P_DEFAULT, octave_H5P_DEFAULT);
1363 #else
1364 a_id = H5Acreate (group_hid, "FILE", type_hid, space_hid,
1365 octave_H5P_DEFAULT);
1366 #endif
1367
1368 if (a_id >= 0)
1369 {
1370 retval = (H5Awrite (a_id, type_hid, fpath.c_str ()) >= 0);
1371
1372 H5Aclose (a_id);
1373 }
1374 else
1375 retval = false;
1376
1377 H5Sclose (space_hid);
1378 H5Tclose (type_hid);
1379 H5Gclose (group_hid);
1380
1381 return retval;
1382
1383 #else
1384
1385 octave_unused_parameter (loc_id);
1386 octave_unused_parameter (name);
1387
1388 warn_save ("hdf5");
1389
1390 return false;
1391
1392 #endif
1393 }
1394
1395 bool simple_fcn_handle::load_hdf5 (octave_hdf5_id& group_hid,
1396 octave_hdf5_id& space_hid,
1397 octave_hdf5_id& type_hid)
1398 {
1399 #if defined (HAVE_HDF5)
1400
1401 unimplemented ("load", "hdf5");
1402
1403 octave_unused_parameter (group_hid);
1404 octave_unused_parameter (space_hid);
1405 octave_unused_parameter (type_hid);
1406
1407 return true;
1408
1409 #else
1410
1411 octave_unused_parameter (group_hid);
1412 octave_unused_parameter (space_hid);
1413 octave_unused_parameter (type_hid);
1414
1415 return false;
1416
1417 #endif
1418 }
1419
1420 void simple_fcn_handle::print_raw (std::ostream& os, bool pr_as_read_syntax,
1421 int current_print_indent_level) const
1422 {
1423 octave_print_internal (os, '@' + m_name, pr_as_read_syntax,
1424 current_print_indent_level);
1425 }
1426
1427 bool is_equal_to (const simple_fcn_handle& fh1, const simple_fcn_handle& fh2)
1428 {
1429 if (fh1.m_name == fh2.m_name)
1430 {
1431 if (fh1.m_fcn.is_defined () && fh2.m_fcn.is_defined ())
1432 return fh1.m_fcn.is_copy_of (fh2.m_fcn);
1433
1434 if (fh1.m_fcn.is_undefined () && fh2.m_fcn.is_undefined ())
1435 return true;
1436 }
1437
1438 return false;
1439 }
1440
1441 scoped_fcn_handle::scoped_fcn_handle (const octave_value& fcn,
1442 const std::string& name,
1443 const std::list<std::string>& parentage)
1444 : base_fcn_handle (name), m_fcn (fcn), m_parentage (parentage)
1445 {
1446 // FIXME: should it be an error if FCN is undefined?
1447
1448 if (m_fcn.is_defined ()) 177 if (m_fcn.is_defined ())
1449 { 178 {
1450 octave_function *oct_fcn = m_fcn.function_value (); 179 octave_function *oct_fcn = m_fcn.function_value ();
1451 180
1452 if (oct_fcn) 181 if (oct_fcn)
1453 m_file = oct_fcn->fcn_file_name (); 182 m_file = oct_fcn->fcn_file_name ();
1454 } 183 }
1455
1456 m_parentage.push_front (name);
1457 } 184 }
1458 185
1459 octave_value_list 186 simple_fcn_handle (const simple_fcn_handle&) = default;
1460 scoped_fcn_handle::call (int nargout, const octave_value_list& args) 187
188 ~simple_fcn_handle (void) = default;
189
190 simple_fcn_handle * clone (void) const
1461 { 191 {
1462 // FIXME: we aren't really using the scope yet. Hmm. 192 return new simple_fcn_handle (*this);
1463
1464 interpreter& interp = __get_interpreter__ ();
1465
1466 if (! m_fcn.is_defined ())
1467 {
1468 // Try to find it?
1469
1470 find_function ();
1471 }
1472
1473 if (! m_fcn.is_defined ())
1474 err_invalid_fcn_handle (m_name);
1475
1476 return interp.feval (m_fcn, args, nargout);
1477 } 193 }
1478 194
1479 octave_scalar_map scoped_fcn_handle::info (void) 195 std::string type (void) const { return "simple"; }
196
197 bool is_simple (void) const { return true; }
198
199 octave_value_list call (int nargout, const octave_value_list& args);
200
201 // FIXME: These must go away. They don't do the right thing for
202 // scoping or overloads.
203 octave_function * function_value (bool);
204
205 octave_user_function * user_function_value (bool);
206
207 octave_value fcn_val (void);
208
209 // Should be const.
210 octave_scalar_map info (void);
211
212 bool save_ascii (std::ostream& os);
213
214 bool load_ascii (std::istream& is);
215
216 bool save_binary (std::ostream& os, bool save_as_floats);
217
218 bool load_binary (std::istream& is, bool swap, mach_info::float_format fmt);
219
220 bool save_hdf5 (octave_hdf5_id loc_hid, const char *name,
221 bool save_as_floats);
222
223 bool load_hdf5 (octave_hdf5_id& group_hid, octave_hdf5_id& space_hid,
224 octave_hdf5_id& type_hid);
225
226 void print_raw (std::ostream& os, bool pr_as_read_syntax,
227 int current_print_indent_level) const;
228
229 friend bool is_equal_to (const simple_fcn_handle& fh1,
230 const simple_fcn_handle& fh2);
231
232 private:
233
234 octave_value m_fcn;
235 };
236
237 class scoped_fcn_handle : public base_fcn_handle
238 {
239 public:
240
241 // FIXME: octaveroot is temporary information used when loading
242 // handles. Can we avoid using it in the constructor?
243
244 scoped_fcn_handle (const std::string& name = "",
245 const std::string& file = "",
246 const std::string& /*octaveroot*/ = "")
247 : base_fcn_handle (name, file)
248 { }
249
250 scoped_fcn_handle (const octave_value& fcn, const std::string& name,
251 const std::list<std::string>& parentage);
252
253 scoped_fcn_handle (const scoped_fcn_handle&) = default;
254
255 ~scoped_fcn_handle (void) = default;
256
257 scoped_fcn_handle * clone (void) const
1480 { 258 {
1481 octave_scalar_map m; 259 return new scoped_fcn_handle (*this);
1482
1483 m.setfield ("function", fcn_name ());
1484 m.setfield ("type", type ());
1485 m.setfield ("file", file ());
1486
1487 m.setfield ("parentage", Cell (m_parentage));
1488
1489 return m;
1490 } 260 }
1491 261
1492 bool scoped_fcn_handle::save_ascii (std::ostream& os) 262 std::string type (void) const { return "scopedfunction"; }
263
264 bool is_scoped (void) const { return true; }
265
266 octave_value_list call (int nargout, const octave_value_list& args);
267
268 // FIXME: These must go away. They don't do the right thing for
269 // scoping or overloads.
270 octave_function * function_value (bool = false)
1493 { 271 {
1494 os << "# octaveroot: " << config::octave_exec_home () << "\n"; 272 return m_fcn.function_value ();
1495
1496 std::string fnm = file ();
1497 if (! fnm.empty ())
1498 os << "# path: " << fnm << "\n";
1499
1500 os << "# subtype: " << type () << "\n";
1501
1502 os << m_name << "\n";
1503
1504 octave_value tmp = Cell (m_parentage);
1505 tmp.save_ascii (os);
1506
1507 return os.good ();
1508 } 273 }
1509 274
1510 bool scoped_fcn_handle::load_ascii (std::istream& is) 275 octave_user_function * user_function_value (bool = false)
1511 { 276 {
1512 octave_cell ov_cell; 277 return m_fcn.user_function_value ();
1513 ov_cell.load_ascii (is);
1514
1515 if (ov_cell.iscellstr ())
1516 {
1517 Array<std::string> cellstr_val = ov_cell.cellstr_value ();
1518
1519 for (octave_idx_type i = 0; i < cellstr_val.numel (); i++)
1520 m_parentage.push_back (cellstr_val(i));
1521 }
1522
1523 return is.good ();
1524 } 278 }
1525 279
1526 bool scoped_fcn_handle::save_binary (std::ostream& os, bool save_as_floats) 280 octave_value fcn_val (void) { return m_fcn; }
281
282 // Should be const.
283 octave_scalar_map info (void);
284
285 bool save_ascii (std::ostream& os);
286
287 bool load_ascii (std::istream& is);
288
289 bool save_binary (std::ostream& os, bool save_as_floats);
290
291 bool load_binary (std::istream& is, bool swap, mach_info::float_format fmt);
292
293 bool save_hdf5 (octave_hdf5_id loc_id, const char *name,
294 bool save_as_floats);
295
296 bool load_hdf5 (octave_hdf5_id& group_hid, octave_hdf5_id& space_hid,
297 octave_hdf5_id& type_hid);
298
299 void print_raw (std::ostream&, bool pr_as_read_syntax,
300 int current_print_indent_level) const;
301
302 friend bool is_equal_to (const scoped_fcn_handle& fh1,
303 const scoped_fcn_handle& fh2);
304
305 protected:
306
307 void find_function (void);
308
309 // The function we are handling.
310 octave_value m_fcn;
311
312 // List of parent function names. The first element is the name of
313 // m_fcn.
314 std::list<std::string> m_parentage;
315 };
316
317 class base_nested_fcn_handle : public base_fcn_handle
318 {
319 public:
320
321 // FIXME: octaveroot is temporary information used when loading
322 // handles. Can we avoid using it in the constructor?
323
324 base_nested_fcn_handle (const std::string& name = "",
325 const std::string& file = "",
326 const std::string& /*octaveroot*/ = "")
327 : base_fcn_handle (name, file)
328 { }
329
330 base_nested_fcn_handle (const octave_value& fcn, const std::string& name)
331 : base_fcn_handle (name), m_fcn (fcn)
332 { }
333
334 std::string type (void) const { return "nested"; }
335
336 using base_fcn_handle::is_nested;
337
338 bool is_nested (void) const { return true; }
339
340 // FIXME: These must go away. They don't do the right thing for
341 // scoping or overloads.
342 octave_function * function_value (bool = false)
1527 { 343 {
1528 std::ostringstream nmbuf; 344 return m_fcn.function_value ();
1529
1530 std::string fnm = file ();
1531
1532 nmbuf << m_name << "@<scopedfunction>\n" << config::octave_exec_home ()
1533 << "\n" << fnm;
1534
1535 std::string buf_str = nmbuf.str ();
1536 int32_t len = buf_str.length ();
1537 os.write (reinterpret_cast<char *> (&len), 4);
1538 os.write (buf_str.c_str (), buf_str.length ());
1539
1540 octave_value tmp = Cell (m_parentage);
1541 tmp.save_binary (os, save_as_floats);
1542
1543 return os.good ();
1544 } 345 }
1545 346
1546 bool scoped_fcn_handle::load_binary (std::istream& is, bool swap, 347 octave_user_function * user_function_value (bool = false)
1547 mach_info::float_format fmt)
1548 { 348 {
1549 octave_cell ov_cell; 349 return m_fcn.user_function_value ();
1550 ov_cell.load_binary (is, swap, fmt);
1551
1552 if (ov_cell.iscellstr ())
1553 {
1554 Array<std::string> cellstr_val = ov_cell.cellstr_value ();
1555
1556 for (octave_idx_type i = 0; i < cellstr_val.numel (); i++)
1557 m_parentage.push_back (cellstr_val(i));
1558 }
1559
1560 return is.good ();
1561 } 350 }
1562 351
1563 bool scoped_fcn_handle::save_hdf5 (octave_hdf5_id loc_id, const char *name, 352 octave_value fcn_val (void) { return m_fcn; }
1564 bool) 353
1565 { 354 virtual octave_value workspace (void) const = 0;
1566 #if defined (HAVE_HDF5) 355
1567 356 // Should be const.
1568 unimplemented ("save", "hdf5"); 357 octave_scalar_map info (void);
1569 358
1570 // FIXME: save parentage. 359 bool save_ascii (std::ostream& os);
1571 360
1572 octave_unused_parameter (loc_id); 361 bool load_ascii (std::istream& is);
1573 octave_unused_parameter (name); 362
1574 363 bool save_binary (std::ostream& os, bool save_as_floats);
1575 return true; 364
1576 365 bool load_binary (std::istream& is, bool swap, mach_info::float_format fmt);
1577 #else 366
1578 367 bool save_hdf5 (octave_hdf5_id loc_id, const char *name,
1579 octave_unused_parameter (loc_id); 368 bool save_as_floats);
1580 octave_unused_parameter (name); 369
1581 370 bool load_hdf5 (octave_hdf5_id& group_hid, octave_hdf5_id& space_hid,
1582 warn_save ("hdf5"); 371 octave_hdf5_id& type_hid);
1583 372
1584 return false; 373 void print_raw (std::ostream&, bool pr_as_read_syntax,
1585 374 int current_print_indent_level) const;
1586 #endif 375
1587 } 376 protected:
1588 377
1589 bool scoped_fcn_handle::load_hdf5 (octave_hdf5_id& group_hid, 378 // The function we are handling.
1590 octave_hdf5_id& space_hid, 379 octave_value m_fcn;
1591 octave_hdf5_id& type_hid) 380 };
1592 { 381
1593 #if defined (HAVE_HDF5) 382 class nested_fcn_handle : public base_nested_fcn_handle
1594 383 {
1595 unimplemented ("load", "hdf5"); 384 public:
1596 385
1597 // FIXME: load parentage. 386 // FIXME: octaveroot is temporary information used when loading
1598 387 // handles. Can we avoid using it in the constructor?
1599 octave_unused_parameter (group_hid); 388
1600 octave_unused_parameter (space_hid); 389 nested_fcn_handle (const std::string& name = "",
1601 octave_unused_parameter (type_hid); 390 const std::string& file = "",
1602 391 const std::string& octaveroot = "")
1603 return true; 392 : base_nested_fcn_handle (name, file, octaveroot)
1604
1605 #else
1606
1607 octave_unused_parameter (group_hid);
1608 octave_unused_parameter (space_hid);
1609 octave_unused_parameter (type_hid);
1610
1611 return false;
1612
1613 #endif
1614 }
1615
1616 void scoped_fcn_handle::print_raw (std::ostream& os,
1617 bool pr_as_read_syntax,
1618 int current_print_indent_level) const
1619 {
1620 octave_print_internal (os, '@' + m_name, pr_as_read_syntax,
1621 current_print_indent_level);
1622 }
1623
1624 bool is_equal_to (const scoped_fcn_handle& fh1, const scoped_fcn_handle& fh2)
1625 {
1626 if (fh1.m_name == fh2.m_name
1627 && fh2.m_parentage == fh2.m_parentage
1628 && fh1.m_fcn.is_defined () && fh2.m_fcn.is_defined ())
1629 return fh1.m_fcn.is_copy_of (fh2.m_fcn);
1630 else
1631 return false;
1632 }
1633
1634 void scoped_fcn_handle::find_function (void)
1635 {
1636 // Since a scoped function is not visible by itself, try to load the
1637 // file named in m_file then find and define the scoped function.
1638 // It is not an error if this fails. We can report later that the
1639 // handle is invalid.
1640
1641 symbol_table& symtab = __get_symbol_table__ ();
1642
1643 if (m_parentage.size () == 1)
1644 {
1645 std::string dir_name = sys::file_ops::dirname (m_file);
1646
1647 std::size_t pos = dir_name.find_last_of (sys::file_ops::dir_sep_chars ());
1648
1649 if (pos != std::string::npos)
1650 dir_name = dir_name.substr (0, pos);
1651 else if (dir_name == "private")
1652 dir_name = ".";
1653
1654 std::string fcn_name = m_parentage.front ();
1655
1656 // FIXME: Does dir_name need to be in the load path for this to work?
1657
1658 m_fcn = symtab.find_private_function (dir_name, m_name);
1659
1660 // FIXME: Verify that it is a private function?
1661 }
1662 else
1663 {
1664 std::string primary_parent_name = m_parentage.back ();
1665
1666 octave_value ov_parent_fcn
1667 = symtab.find_user_function (primary_parent_name);
1668
1669 if (ov_parent_fcn.is_defined ())
1670 {
1671 octave_user_function *fcn = ov_parent_fcn.user_function_value ();
1672
1673 if (fcn)
1674 {
1675 std::string file_name = fcn->fcn_file_name ();
1676
1677 std::string oct_home = config::octave_exec_home ();
1678
1679 if (file_name.substr (0, oct_home.size ()) == oct_home)
1680 file_name = file_name.substr (oct_home.size ());
1681
1682 octave_value subfcn = fcn->find_subfunction (m_name);
1683
1684 if (subfcn.is_defined ())
1685 m_fcn = subfcn;
1686 }
1687 }
1688 }
1689 }
1690
1691 octave_scalar_map base_nested_fcn_handle::info (void)
1692 {
1693 octave_scalar_map m;
1694
1695 m.setfield ("function", fcn_name ());
1696 m.setfield ("type", type ());
1697 m.setfield ("file", "");
1698 m.setfield ("workspace", workspace ());
1699
1700 return m;
1701 }
1702
1703 // FIXME: For save, we need a way to save the (possibly shared)
1704 // workspace. For load, we need a way to load and link to the
1705 // (possibly shared) workspace that was saved.
1706 //
1707 // Since a nested function is not visible by itself, do we need to try
1708 // to load the file named in m_file then find and define the function?
1709 // Is it an error if that fails? Or should this job always be
1710 // deferred until the handle is used?
1711
1712 bool base_nested_fcn_handle::save_ascii (std::ostream& os)
1713 {
1714 unimplemented ("save", "text");
1715
1716 octave_unused_parameter (os);
1717
1718 return true;
1719 }
1720
1721 bool base_nested_fcn_handle::load_ascii (std::istream& is)
1722 {
1723 unimplemented ("load", "text");
1724
1725 octave_unused_parameter (is);
1726
1727 return true;
1728 }
1729
1730 bool base_nested_fcn_handle::save_binary (std::ostream& os,
1731 bool save_as_floats)
1732 {
1733 unimplemented ("save", "binary");
1734
1735 octave_unused_parameter (os);
1736 octave_unused_parameter (save_as_floats);
1737
1738 return true;
1739 }
1740
1741 bool base_nested_fcn_handle::load_binary (std::istream& is, bool swap,
1742 mach_info::float_format fmt)
1743 {
1744 unimplemented ("load", "binary");
1745
1746 octave_unused_parameter (is);
1747 octave_unused_parameter (swap);
1748 octave_unused_parameter (fmt);
1749
1750 return true;
1751 }
1752
1753 bool base_nested_fcn_handle::save_hdf5 (octave_hdf5_id loc_id,
1754 const char *name, bool)
1755 {
1756 #if defined (HAVE_HDF5)
1757
1758 unimplemented ("save", "hdf5");
1759
1760 octave_unused_parameter (loc_id);
1761 octave_unused_parameter (name);
1762
1763 return true;
1764
1765 #else
1766
1767 octave_unused_parameter (loc_id);
1768 octave_unused_parameter (name);
1769
1770 warn_save ("hdf5");
1771
1772 return false;
1773
1774 #endif
1775 }
1776
1777 bool base_nested_fcn_handle::load_hdf5 (octave_hdf5_id& group_hid,
1778 octave_hdf5_id& space_hid,
1779 octave_hdf5_id& type_hid)
1780 {
1781 #if defined (HAVE_HDF5)
1782
1783 unimplemented ("load", "hdf5");
1784
1785 octave_unused_parameter (group_hid);
1786 octave_unused_parameter (space_hid);
1787 octave_unused_parameter (type_hid);
1788
1789 return true;
1790
1791 #else
1792
1793 octave_unused_parameter (group_hid);
1794 octave_unused_parameter (space_hid);
1795 octave_unused_parameter (type_hid);
1796
1797 return false;
1798
1799 #endif
1800 }
1801
1802 void base_nested_fcn_handle::print_raw (std::ostream& os,
1803 bool pr_as_read_syntax,
1804 int current_print_indent_level) const
1805 {
1806 octave_print_internal (os, '@' + m_name, pr_as_read_syntax,
1807 current_print_indent_level);
1808 }
1809
1810 octave_value nested_fcn_handle::make_weak_nested_handle (void) const
1811 {
1812 return octave_value (new octave_fcn_handle
1813 (new weak_nested_fcn_handle (*this)));
1814 }
1815
1816 octave_value_list
1817 nested_fcn_handle::call (int nargout, const octave_value_list& args)
1818 {
1819 tree_evaluator& tw = __get_evaluator__ ();
1820
1821 octave_user_function *oct_usr_fcn = m_fcn.user_function_value ();
1822
1823 tw.push_stack_frame (oct_usr_fcn, m_stack_context);
1824
1825 unwind_action act ([&tw] () { tw.pop_stack_frame (); });
1826
1827 return oct_usr_fcn->execute (tw, nargout, args);
1828 }
1829
1830 octave_value nested_fcn_handle::workspace (void) const
1831 {
1832 return m_stack_context->workspace ();
1833 }
1834
1835 bool is_equal_to (const nested_fcn_handle& fh1, const nested_fcn_handle& fh2)
1836 {
1837 if (fh1.m_name == fh2.m_name
1838 && fh1.m_fcn.is_defined () && fh2.m_fcn.is_defined ())
1839 return fh1.m_fcn.is_copy_of (fh2.m_fcn);
1840 else
1841 return false;
1842 }
1843
1844 octave_value_list
1845 weak_nested_fcn_handle::call (int nargout, const octave_value_list& args)
1846 {
1847 tree_evaluator& tw = __get_evaluator__ ();
1848
1849 octave_user_function *oct_usr_fcn = m_fcn.user_function_value ();
1850
1851 std::shared_ptr<stack_frame> frames = m_stack_context.lock ();
1852
1853 tw.push_stack_frame (oct_usr_fcn, frames);
1854
1855 unwind_action act ([&tw] () { tw.pop_stack_frame (); });
1856
1857 return oct_usr_fcn->execute (tw, nargout, args);
1858 }
1859
1860 octave_value weak_nested_fcn_handle::workspace (void) const
1861 {
1862 std::shared_ptr<stack_frame> frames = m_stack_context.lock ();
1863
1864 return frames ? frames->workspace () : octave_value ();
1865 }
1866
1867 bool is_equal_to (const weak_nested_fcn_handle& fh1,
1868 const weak_nested_fcn_handle& fh2)
1869 {
1870 if (fh1.m_name == fh2.m_name
1871 && fh1.m_fcn.is_defined () && fh2.m_fcn.is_defined ())
1872 return fh1.m_fcn.is_copy_of (fh2.m_fcn);
1873 else
1874 return false;
1875 }
1876
1877 class_simple_fcn_handle::class_simple_fcn_handle (const std::string& class_nm,
1878 const std::string& meth_nm)
1879 : base_fcn_handle (meth_nm), m_obj (), m_fcn (),
1880 m_dispatch_class (class_nm)
1881 { } 393 { }
1882 394
1883 class_simple_fcn_handle::class_simple_fcn_handle (const octave_value& fcn, 395 nested_fcn_handle (const octave_value& fcn, const std::string& name,
1884 const std::string& class_nm, 396 const std::shared_ptr<stack_frame>& stack_context)
1885 const std::string& meth_nm) 397 : base_nested_fcn_handle (fcn, name), m_stack_context (stack_context)
1886 : base_fcn_handle (meth_nm), m_obj (), m_fcn (fcn),
1887 m_dispatch_class (class_nm)
1888 { }
1889
1890 class_simple_fcn_handle::class_simple_fcn_handle (const octave_value& obj,
1891 const octave_value& fcn,
1892 const std::string& class_nm,
1893 const std::string& meth_nm)
1894 : base_fcn_handle (meth_nm), m_obj (obj), m_fcn (fcn),
1895 m_dispatch_class (class_nm)
1896 { }
1897
1898 octave_value_list
1899 class_simple_fcn_handle::call (int nargout, const octave_value_list& args)
1900 {
1901 interpreter& interp = __get_interpreter__ ();
1902
1903 if (m_obj.is_defined ())
1904 {
1905 octave_value_list tmp_args = args;
1906 tmp_args.prepend (m_obj);
1907
1908 return interp.feval (m_fcn, tmp_args, nargout);
1909 }
1910
1911 // FIXME: is this the best approach? Should we be saving current
1912 // dispatch class and restoring that value instead of
1913 // unconditionally setting it to "" when we return from this
1914 // function?
1915
1916 tree_evaluator& tw = interp.get_evaluator ();
1917
1918 unwind_action act ([&tw] () { tw.set_dispatch_class (""); });
1919
1920 tw.set_dispatch_class (m_dispatch_class);
1921
1922 if (m_fcn.is_defined ())
1923 return interp.feval (m_fcn, args, nargout);
1924
1925 return interp.feval (fcn_name (), args, nargout);
1926 }
1927
1928 octave_scalar_map class_simple_fcn_handle::info (void)
1929 {
1930 octave_scalar_map m;
1931
1932 m.setfield ("function", fcn_name ());
1933 m.setfield ("type", type ());
1934 m.setfield ("file", "");
1935 m.setfield ("class", dispatch_class ());
1936
1937 return m;
1938 }
1939
1940 // FIXME: Since a class method is not visible by itself, do we need to
1941 // try to load the file named in m_file then find and define the
1942 // function? Is it an error if that fails? Or should this job always
1943 // be deferred until the handle is used?
1944
1945 bool class_simple_fcn_handle::save_ascii (std::ostream& os)
1946 {
1947 unimplemented ("save", "text");
1948
1949 octave_unused_parameter (os);
1950
1951 return true;
1952 }
1953
1954 bool class_simple_fcn_handle::load_ascii (std::istream& is)
1955 {
1956 unimplemented ("load", "text");
1957
1958 octave_unused_parameter (is);
1959
1960 return true;
1961 }
1962
1963 bool class_simple_fcn_handle::save_binary (std::ostream& os,
1964 bool save_as_floats)
1965 {
1966 unimplemented ("save", "binary");
1967
1968 octave_unused_parameter (os);
1969 octave_unused_parameter (save_as_floats);
1970
1971 return true;
1972 }
1973
1974 bool class_simple_fcn_handle::load_binary (std::istream& is, bool swap,
1975 mach_info::float_format fmt)
1976 {
1977 unimplemented ("load", "binary");
1978
1979 octave_unused_parameter (is);
1980 octave_unused_parameter (swap);
1981 octave_unused_parameter (fmt);
1982
1983 return true;
1984 }
1985
1986 bool class_simple_fcn_handle::save_hdf5 (octave_hdf5_id loc_id,
1987 const char *name, bool)
1988 {
1989 #if defined (HAVE_HDF5)
1990
1991 unimplemented ("save", "hdf5");
1992
1993 octave_unused_parameter (loc_id);
1994 octave_unused_parameter (name);
1995
1996 return true;
1997
1998 #else
1999
2000 octave_unused_parameter (loc_id);
2001 octave_unused_parameter (name);
2002
2003 warn_save ("hdf5");
2004
2005 return false;
2006
2007 #endif
2008 }
2009
2010 bool class_simple_fcn_handle::load_hdf5 (octave_hdf5_id& group_hid,
2011 octave_hdf5_id& space_hid,
2012 octave_hdf5_id& type_hid)
2013 {
2014 #if defined (HAVE_HDF5)
2015
2016 unimplemented ("load", "hdf5");
2017
2018 octave_unused_parameter (group_hid);
2019 octave_unused_parameter (space_hid);
2020 octave_unused_parameter (type_hid);
2021
2022 return true;
2023
2024 #else
2025
2026 octave_unused_parameter (group_hid);
2027 octave_unused_parameter (space_hid);
2028 octave_unused_parameter (type_hid);
2029
2030 return false;
2031
2032 #endif
2033 }
2034
2035 void class_simple_fcn_handle::print_raw (std::ostream& os,
2036 bool pr_as_read_syntax,
2037 int current_print_indent_level) const
2038 {
2039 octave_print_internal (os, '@' + m_name, pr_as_read_syntax,
2040 current_print_indent_level);
2041 }
2042
2043 bool is_equal_to (const class_simple_fcn_handle& fh1,
2044 const class_simple_fcn_handle& fh2)
2045 {
2046 // FIXME: Also need to check object values are equivalent?
2047
2048 if (fh1.m_name == fh2.m_name
2049 && fh1.m_fcn.is_defined () && fh2.m_fcn.is_defined ())
2050 return fh1.m_fcn.is_copy_of (fh2.m_fcn);
2051 else
2052 return false;
2053 }
2054
2055 const std::string base_anonymous_fcn_handle::anonymous ("@<anonymous>");
2056
2057 octave_scalar_map base_anonymous_fcn_handle::info (void)
2058 {
2059 octave_scalar_map m;
2060
2061 std::ostringstream buf;
2062 print_raw (buf, true, 0);
2063 m.setfield ("function", buf.str ());
2064
2065 m.setfield ("type", type ());
2066 m.setfield ("file", "");
2067 m.setfield ("workspace", workspace ());
2068 m.setfield ("within_file_path", "");
2069
2070 return m;
2071 }
2072
2073 bool base_anonymous_fcn_handle::save_ascii (std::ostream& os)
2074 {
2075 // FIXME: can we ensure that m_fcn is always defined?
2076
2077 if (m_fcn.is_undefined ())
2078 return false;
2079
2080 os << m_name << "\n";
2081
2082 print_raw (os, true, 0);
2083 os << "\n";
2084
2085 std::size_t varlen = m_local_vars.size ();
2086
2087 if (varlen > 0)
2088 {
2089 os << "# length: " << varlen << "\n";
2090
2091 for (const auto& nm_val : m_local_vars)
2092 {
2093 if (! save_text_data (os, nm_val.second, nm_val.first, false, 0))
2094 return ! os.fail ();
2095 }
2096 }
2097
2098 return true;
2099 }
2100
2101 bool base_anonymous_fcn_handle::load_ascii (std::istream& is)
2102 {
2103 octave::skip_preceeding_newline (is);
2104
2105 std::string buf;
2106
2107 if (is)
2108 {
2109 // Get a line of text whitespace characters included, leaving
2110 // newline in the stream.
2111
2112 buf = octave::read_until_newline (is, true);
2113 }
2114
2115 std::streampos pos = is.tellg ();
2116
2117 // Set up temporary scope to use for evaluating the text that
2118 // defines the anonymous function.
2119
2120 interpreter& interp = __get_interpreter__ ();
2121
2122 tree_evaluator& tw = interp.get_evaluator ();
2123
2124 tw.push_dummy_scope (buf);
2125 unwind_action_safe restore_scope (&tree_evaluator::pop_scope, &tw);
2126
2127 octave_idx_type len = 0;
2128
2129 if (extract_keyword (is, "length", len, true) && len >= 0)
2130 {
2131 if (len > 0)
2132 {
2133 for (octave_idx_type i = 0; i < len; i++)
2134 {
2135 octave_value t2;
2136 bool dummy;
2137
2138 std::string name = read_text_data (is, "", dummy, t2, i);
2139
2140 if (! is)
2141 error ("load: failed to load anonymous function handle");
2142
2143 m_local_vars[name] = t2;
2144 }
2145 }
2146 }
2147 else
2148 {
2149 is.seekg (pos);
2150 is.clear ();
2151 }
2152
2153 if (is)
2154 return parse (buf);
2155
2156 return false;
2157 }
2158
2159 bool base_anonymous_fcn_handle::save_binary (std::ostream& os,
2160 bool save_as_floats)
2161 {
2162 // FIXME: can we ensure that m_fcn is always defined?
2163
2164 if (m_fcn.is_undefined ())
2165 return false;
2166
2167 std::ostringstream nmbuf;
2168
2169 std::size_t varlen = m_local_vars.size ();
2170
2171 nmbuf << anonymous;
2172 if (varlen > 0)
2173 nmbuf << ' ' << varlen;
2174
2175 std::string buf_str = nmbuf.str ();
2176 int32_t tmp = buf_str.length ();
2177 os.write (reinterpret_cast<char *> (&tmp), 4);
2178 os.write (buf_str.c_str (), buf_str.length ());
2179
2180 std::ostringstream buf;
2181 print_raw (buf, true, 0);
2182 std::string stmp = buf.str ();
2183 tmp = stmp.length ();
2184 os.write (reinterpret_cast<char *> (&tmp), 4);
2185 os.write (stmp.c_str (), stmp.length ());
2186
2187 if (varlen > 0)
2188 {
2189 for (const auto& nm_val : m_local_vars)
2190 {
2191 if (! save_binary_data (os, nm_val.second, nm_val.first,
2192 "", 0, save_as_floats))
2193 return ! os.fail ();
2194 }
2195 }
2196
2197 return true;
2198 }
2199
2200 bool base_anonymous_fcn_handle::load_binary (std::istream& is, bool swap,
2201 mach_info::float_format fmt)
2202 {
2203 // Read extra characters in m_name as the number of local variable
2204 // values in this anonymous function.
2205
2206 octave_idx_type len = 0;
2207 std::size_t anl = anonymous.length ();
2208 if (m_name.length () > anl)
2209 {
2210 std::istringstream nm_is (m_name.substr (anl));
2211 nm_is >> len;
2212
2213 // Anonymous functions don't have names. We just used this
2214 // string as temporary storage to pass the number of local
2215 // variable values.
2216
2217 m_name = "";
2218 }
2219
2220 int32_t tmp;
2221
2222 if (! is.read (reinterpret_cast<char *> (&tmp), 4))
2223 return false;
2224 if (swap)
2225 swap_bytes<4> (&tmp);
2226
2227 OCTAVE_LOCAL_BUFFER (char, ctmp2, tmp+1);
2228 // is.get (ctmp2, tmp+1, 0); caused is.eof () to be true though
2229 // effectively not reading over file end
2230 is.read (ctmp2, tmp);
2231 ctmp2[tmp] = 0;
2232
2233 // Set up temporary scope to use for evaluating the text that
2234 // defines the anonymous function.
2235
2236 interpreter& interp = __get_interpreter__ ();
2237
2238 tree_evaluator& tw = interp.get_evaluator ();
2239
2240 tw.push_dummy_scope (ctmp2);
2241 unwind_action_safe restore_scope (&tree_evaluator::pop_scope, &tw);
2242
2243 if (len > 0)
2244 {
2245 for (octave_idx_type i = 0; i < len; i++)
2246 {
2247 octave_value t2;
2248 bool dummy;
2249 std::string doc;
2250
2251 std::string name
2252 = read_binary_data (is, swap, fmt, "", dummy, t2, doc);
2253
2254 if (! is)
2255 error ("load: failed to load anonymous function handle");
2256
2257 m_local_vars[name] = t2;
2258 }
2259 }
2260
2261 if (is)
2262 return parse (ctmp2);
2263
2264 return false;
2265 }
2266
2267 bool base_anonymous_fcn_handle::save_hdf5 (octave_hdf5_id loc_id,
2268 const char *name,
2269 bool save_as_floats)
2270 {
2271 #if defined (HAVE_HDF5)
2272
2273 bool retval = true;
2274
2275 octave_hdf5_id group_hid = -1;
2276 #if defined (HAVE_HDF5_18)
2277 group_hid = H5Gcreate (loc_id, name, octave_H5P_DEFAULT, octave_H5P_DEFAULT,
2278 octave_H5P_DEFAULT);
2279 #else
2280 group_hid = H5Gcreate (loc_id, name, 0);
2281 #endif
2282 if (group_hid < 0)
2283 return false;
2284
2285 octave_hdf5_id space_hid, data_hid, type_hid;
2286 space_hid = data_hid = type_hid = -1;
2287
2288 // attach the type of the variable
2289 type_hid = H5Tcopy (H5T_C_S1);
2290 H5Tset_size (type_hid, m_name.length () + 1);
2291 if (type_hid < 0)
2292 {
2293 H5Gclose (group_hid);
2294 return false;
2295 }
2296
2297 OCTAVE_LOCAL_BUFFER (hsize_t, hdims, 2);
2298 hdims[0] = 0;
2299 hdims[1] = 0;
2300 space_hid = H5Screate_simple (0, hdims, nullptr);
2301 if (space_hid < 0)
2302 {
2303 H5Tclose (type_hid);
2304 H5Gclose (group_hid);
2305 return false;
2306 }
2307 #if defined (HAVE_HDF5_18)
2308 data_hid = H5Dcreate (group_hid, "nm", type_hid, space_hid,
2309 octave_H5P_DEFAULT, octave_H5P_DEFAULT,
2310 octave_H5P_DEFAULT);
2311 #else
2312 data_hid = H5Dcreate (group_hid, "nm", type_hid, space_hid,
2313 octave_H5P_DEFAULT);
2314 #endif
2315 if (data_hid < 0
2316 || H5Dwrite (data_hid, type_hid, octave_H5S_ALL, octave_H5S_ALL,
2317 octave_H5P_DEFAULT, m_name.c_str ()) < 0)
2318 {
2319 H5Sclose (space_hid);
2320 H5Tclose (type_hid);
2321 H5Gclose (group_hid);
2322 return false;
2323 }
2324 H5Dclose (data_hid);
2325
2326 std::ostringstream buf;
2327 print_raw (buf, true, 0);
2328 std::string stmp = buf.str ();
2329
2330 // attach the type of the variable
2331 H5Tset_size (type_hid, stmp.length () + 1);
2332 if (type_hid < 0)
2333 {
2334 H5Sclose (space_hid);
2335 H5Gclose (group_hid);
2336 return false;
2337 }
2338
2339 #if defined (HAVE_HDF5_18)
2340 data_hid = H5Dcreate (group_hid, "fcn", type_hid, space_hid,
2341 octave_H5P_DEFAULT, octave_H5P_DEFAULT,
2342 octave_H5P_DEFAULT);
2343 #else
2344 data_hid = H5Dcreate (group_hid, "fcn", type_hid, space_hid,
2345 octave_H5P_DEFAULT);
2346 #endif
2347 if (data_hid < 0
2348 || H5Dwrite (data_hid, type_hid, octave_H5S_ALL, octave_H5S_ALL,
2349 octave_H5P_DEFAULT, stmp.c_str ()) < 0)
2350 {
2351 H5Sclose (space_hid);
2352 H5Tclose (type_hid);
2353 H5Gclose (group_hid);
2354 return false;
2355 }
2356
2357 H5Dclose (data_hid);
2358
2359 std::size_t varlen = m_local_vars.size ();
2360
2361 if (varlen > 0)
2362 {
2363 octave_hdf5_id as_id = H5Screate (H5S_SCALAR);
2364
2365 if (as_id >= 0)
2366 {
2367 octave_hdf5_id a_id;
2368 #if defined (HAVE_HDF5_18)
2369 a_id = H5Acreate (group_hid, "SYMBOL_TABLE", H5T_NATIVE_IDX, as_id,
2370 octave_H5P_DEFAULT, octave_H5P_DEFAULT);
2371
2372 #else
2373 a_id = H5Acreate (group_hid, "SYMBOL_TABLE", H5T_NATIVE_IDX, as_id,
2374 octave_H5P_DEFAULT);
2375 #endif
2376
2377 if (a_id >= 0)
2378 {
2379 retval = (H5Awrite (a_id, H5T_NATIVE_IDX, &varlen) >= 0);
2380
2381 H5Aclose (a_id);
2382 }
2383 else
2384 retval = false;
2385
2386 H5Sclose (as_id);
2387 }
2388 else
2389 retval = false;
2390 #if defined (HAVE_HDF5_18)
2391 data_hid = H5Gcreate (group_hid, "symbol table",
2392 octave_H5P_DEFAULT, octave_H5P_DEFAULT,
2393 octave_H5P_DEFAULT);
2394 #else
2395 data_hid = H5Gcreate (group_hid, "symbol table", 0);
2396 #endif
2397 if (data_hid < 0)
2398 {
2399 H5Sclose (space_hid);
2400 H5Tclose (type_hid);
2401 H5Gclose (group_hid);
2402 return false;
2403 }
2404
2405 for (const auto& nm_val : m_local_vars)
2406 {
2407 if (! add_hdf5_data (data_hid, nm_val.second, nm_val.first,
2408 "", false, save_as_floats))
2409 break;
2410 }
2411
2412 H5Gclose (data_hid);
2413 }
2414
2415 H5Sclose (space_hid);
2416 H5Tclose (type_hid);
2417 H5Gclose (group_hid);
2418
2419 return retval;
2420
2421 #else
2422
2423 octave_unused_parameter (loc_id);
2424 octave_unused_parameter (name);
2425 octave_unused_parameter (save_as_floats);
2426
2427 warn_save ("hdf5");
2428
2429 return false;
2430
2431 #endif
2432 }
2433
2434 bool base_anonymous_fcn_handle::load_hdf5 (octave_hdf5_id& group_hid,
2435 octave_hdf5_id& space_hid,
2436 octave_hdf5_id& type_hid)
2437 {
2438 #if defined (HAVE_HDF5)
2439
2440 bool success = true;
2441
2442 #if defined (HAVE_HDF5_18)
2443 octave_hdf5_id data_hid = H5Dopen (group_hid, "fcn", octave_H5P_DEFAULT);
2444 #else
2445 octave_hdf5_id data_hid = H5Dopen (group_hid, "fcn");
2446 #endif
2447
2448 if (data_hid < 0)
2449 {
2450 H5Sclose (space_hid);
2451 H5Tclose (type_hid);
2452 H5Gclose (group_hid);
2453 return false;
2454 }
2455
2456 H5Tclose (type_hid);
2457 type_hid = H5Dget_type (data_hid);
2458 octave_hdf5_id type_class_hid = H5Tget_class (type_hid);
2459
2460 if (type_class_hid != H5T_STRING)
2461 {
2462 H5Sclose (space_hid);
2463 H5Tclose (type_hid);
2464 H5Dclose (data_hid);
2465 H5Gclose (group_hid);
2466 return false;
2467 }
2468
2469 H5Sclose (space_hid);
2470 space_hid = H5Dget_space (data_hid);
2471 hsize_t rank = H5Sget_simple_extent_ndims (space_hid);
2472
2473 if (rank != 0)
2474 {
2475 H5Sclose (space_hid);
2476 H5Tclose (type_hid);
2477 H5Dclose (data_hid);
2478 H5Gclose (group_hid);
2479 return false;
2480 }
2481
2482 int slen = H5Tget_size (type_hid);
2483 if (slen < 0)
2484 {
2485 H5Sclose (space_hid);
2486 H5Tclose (type_hid);
2487 H5Dclose (data_hid);
2488 H5Gclose (group_hid);
2489 return false;
2490 }
2491
2492 OCTAVE_LOCAL_BUFFER (char, fcn_tmp, slen);
2493
2494 // create datatype for (null-terminated) string to read into:
2495 octave_hdf5_id st_id = H5Tcopy (H5T_C_S1);
2496 H5Tset_size (st_id, slen);
2497
2498 if (H5Dread (data_hid, st_id, octave_H5S_ALL, octave_H5S_ALL,
2499 octave_H5P_DEFAULT, fcn_tmp)
2500 < 0)
2501 {
2502 H5Tclose (st_id);
2503 H5Sclose (space_hid);
2504 H5Tclose (type_hid);
2505 H5Dclose (data_hid);
2506 H5Gclose (group_hid);
2507 return false;
2508 }
2509 H5Tclose (st_id);
2510 H5Dclose (data_hid);
2511
2512 octave_idx_type len = 0;
2513
2514 // we have to pull some shenanigans here to make sure
2515 // HDF5 doesn't print out all sorts of error messages if we
2516 // call H5Aopen for a non-existing attribute
2517
2518 H5E_auto_t err_fcn;
2519 void *err_fcn_data;
2520
2521 // turn off error reporting temporarily, but save the error
2522 // reporting function:
2523 #if defined (HAVE_HDF5_18)
2524 H5Eget_auto (octave_H5E_DEFAULT, &err_fcn, &err_fcn_data);
2525 H5Eset_auto (octave_H5E_DEFAULT, nullptr, nullptr);
2526 #else
2527 H5Eget_auto (&err_fcn, &err_fcn_data);
2528 H5Eset_auto (nullptr, nullptr);
2529 #endif
2530
2531 octave_hdf5_id attr_id = H5Aopen_name (group_hid, "SYMBOL_TABLE");
2532
2533 if (attr_id >= 0)
2534 {
2535 if (H5Aread (attr_id, H5T_NATIVE_IDX, &len) < 0)
2536 success = false;
2537
2538 H5Aclose (attr_id);
2539 }
2540
2541 // restore error reporting:
2542 #if defined (HAVE_HDF5_18)
2543 H5Eset_auto (octave_H5E_DEFAULT, err_fcn, err_fcn_data);
2544 #else
2545 H5Eset_auto (err_fcn, err_fcn_data);
2546 #endif
2547
2548 // Set up temporary scope to use for evaluating the text that
2549 // defines the anonymous function.
2550
2551 interpreter& interp = __get_interpreter__ ();
2552
2553 tree_evaluator& tw = interp.get_evaluator ();
2554
2555 tw.push_dummy_scope (fcn_tmp);
2556 unwind_action_safe restore_scope (&tree_evaluator::pop_scope, &tw);
2557
2558 if (len > 0 && success)
2559 {
2560 hsize_t num_obj = 0;
2561 #if defined (HAVE_HDF5_18)
2562 data_hid = H5Gopen (group_hid, "symbol table", octave_H5P_DEFAULT);
2563 #else
2564 data_hid = H5Gopen (group_hid, "symbol table");
2565 #endif
2566 H5Gget_num_objs (data_hid, &num_obj);
2567 H5Gclose (data_hid);
2568
2569 if (num_obj != static_cast<hsize_t> (len))
2570 error ("load: failed to load anonymous function handle");
2571
2572 hdf5_callback_data dsub;
2573 int current_item = 0;
2574 for (octave_idx_type i = 0; i < len; i++)
2575 {
2576 if (hdf5_h5g_iterate (group_hid, "symbol table", &current_item,
2577 &dsub) <= 0)
2578 error ("load: failed to load anonymous function handle");
2579
2580 m_local_vars[dsub.name] = dsub.tc;
2581 }
2582 }
2583
2584 if (success)
2585 return parse (fcn_tmp);
2586
2587 return false;
2588
2589 #else
2590
2591 octave_unused_parameter (group_hid);
2592 octave_unused_parameter (space_hid);
2593 octave_unused_parameter (type_hid);
2594
2595 return false;
2596
2597 #endif
2598 }
2599
2600 void base_anonymous_fcn_handle::print_raw (std::ostream& os, bool, int) const
2601 {
2602 tree_print_code tpc (os);
2603
2604 octave_user_function *f = m_fcn.user_function_value ();
2605
2606 if (! f)
2607 error ("invalid anonymous function handle");
2608
2609 os << "@";
2610
2611 // The parameter list should always be valid for anonymous
2612 // functions, so we should always call accept for it, and it will
2613 // print the parens for us.
2614
2615 tree_parameter_list *p = f->parameter_list ();
2616
2617 if (p)
2618 p->accept (tpc);
2619
2620 os << " ";
2621
2622 tree_statement_list *b = f->body ();
2623
2624 panic_if (b->length () != 1);
2625
2626 tree_statement *s = b->front ();
2627
2628 if (! s)
2629 error ("invalid anonymous function handle");
2630
2631 panic_unless (s->is_expression ());
2632
2633 tree_expression *e = s->expression ();
2634
2635 if (! e)
2636 error ("invalid anonymous function handle");
2637
2638 tpc.print_fcn_handle_body (e);
2639 }
2640
2641 bool base_anonymous_fcn_handle::parse (const std::string& fcn_text)
2642 {
2643 // FIXME: If evaluation of the string gives us an anonymous function
2644 // handle object, then why extract the function and create a new
2645 // anonymous function object? Why not just attach the workspace
2646 // values to the object returned by eval_string? This code is also is
2647 // duplicated in read_mat5_binary_element in ls-mat5.cc.
2648
2649 interpreter& interp = __get_interpreter__ ();
2650
2651 // Set up temporary scope to use for evaluating the text that defines
2652 // the anonymous function so that we don't pick up values of random
2653 // variables that might be in the current scope.
2654
2655 tree_evaluator& tw = interp.get_evaluator ();
2656 tw.push_dummy_scope ("read_mat5_binary_element");
2657
2658 unwind_action act ([&tw] () { tw.pop_scope (); });
2659
2660 int parse_status;
2661 octave_value anonymous_fcn_hdl
2662 = interp.eval_string (fcn_text, true, parse_status);
2663
2664 if (parse_status != 0)
2665 return false;
2666
2667 octave_fcn_handle *fh = anonymous_fcn_hdl.fcn_handle_value ();
2668
2669 if (! fh)
2670 return false;
2671
2672 m_fcn = fh->fcn_val ();
2673
2674 octave_user_function *uf = m_fcn.user_function_value (true);
2675
2676 if (uf)
2677 {
2678 symbol_scope uf_scope = uf->scope ();
2679
2680 if (uf_scope)
2681 uf_scope.cache_name (m_name);
2682 }
2683
2684 return true;
2685 }
2686
2687 anonymous_fcn_handle::anonymous_fcn_handle (const octave_value& fcn,
2688 const stack_frame::local_vars_map& local_vars,
2689 const std::shared_ptr<stack_frame>& stack_context)
2690 : base_anonymous_fcn_handle (fcn, local_vars),
2691 m_stack_context (stack_context)
2692 { 398 {
2693 if (m_stack_context) 399 if (m_stack_context)
2694 m_stack_context->mark_closure_context (); 400 m_stack_context->mark_closure_context ();
2695 } 401 }
2696 402
2697 octave_value anonymous_fcn_handle::make_weak_anonymous_handle (void) const 403 nested_fcn_handle (const nested_fcn_handle&) = default;
404
405 ~nested_fcn_handle (void) = default;
406
407 using base_nested_fcn_handle::is_nested;
408
409 bool is_nested (const std::shared_ptr<stack_frame>& frame) const
2698 { 410 {
2699 return octave_value (new octave_fcn_handle 411 return frame == m_stack_context;
2700 (new weak_anonymous_fcn_handle (*this)));
2701 } 412 }
2702 413
2703 octave_value_list 414 nested_fcn_handle * clone (void) const
2704 anonymous_fcn_handle::call (int nargout, const octave_value_list& args)
2705 { 415 {
2706 tree_evaluator& tw = __get_evaluator__ (); 416 return new nested_fcn_handle (*this);
2707
2708 octave_user_function *oct_usr_fcn = m_fcn.user_function_value ();
2709
2710 tw.push_stack_frame (oct_usr_fcn, m_local_vars, m_stack_context);
2711
2712 unwind_action act ([&tw] () { tw.pop_stack_frame (); });
2713
2714 return oct_usr_fcn->execute (tw, nargout, args);
2715 } 417 }
2716 418
2717 octave_value anonymous_fcn_handle::workspace (void) const 419 octave_value make_weak_nested_handle (void) const;
420
421 octave_value_list call (int nargout, const octave_value_list& args);
422
423 octave_value workspace (void) const;
424
425 friend bool is_equal_to (const nested_fcn_handle& fh1,
426 const nested_fcn_handle& fh2);
427
428 std::shared_ptr<stack_frame> stack_context (void) const
2718 { 429 {
2719 octave_scalar_map local_vars_map; 430 return m_stack_context;
2720 431 }
2721 for (const auto& nm_val : m_local_vars) 432
2722 local_vars_map.assign (nm_val.first, nm_val.second); 433 protected:
2723 434
2724 // FIXME: it would be more convenient if stack_frame::workspace 435 // Pointer to closure stack frames.
2725 // returned a Cell object directly instead of a Cell in an 436 std::shared_ptr<stack_frame> m_stack_context;
2726 // octave_value object. 437 };
2727 438
2728 Cell cell_frames; 439 class weak_nested_fcn_handle : public base_nested_fcn_handle
2729 440 {
2730 if (m_stack_context) 441 public:
442
443 weak_nested_fcn_handle (const nested_fcn_handle& nfh)
444 : base_nested_fcn_handle (nfh), m_stack_context (nfh.stack_context ())
445 { }
446
447 weak_nested_fcn_handle (const weak_nested_fcn_handle&) = default;
448
449 ~weak_nested_fcn_handle (void) = default;
450
451 weak_nested_fcn_handle * clone (void) const
452 {
453 return new weak_nested_fcn_handle (*this);
454 }
455
456 bool is_weak_nested (void) const { return true; }
457
458 octave_value_list call (int nargout, const octave_value_list& args);
459
460 octave_value workspace (void) const;
461
462 friend bool is_equal_to (const weak_nested_fcn_handle& fh1,
463 const weak_nested_fcn_handle& fh2);
464
465 protected:
466
467 // Pointer to closure stack frames.
468 std::weak_ptr<stack_frame> m_stack_context;
469 };
470
471 class class_simple_fcn_handle : public base_fcn_handle
472 {
473 public:
474
475 // FIXME: octaveroot is temporary information used when loading
476 // handles. Can we avoid using it in the constructor?
477
478 class_simple_fcn_handle (const std::string& name,
479 const std::string& file,
480 const std::string& /*octaveroot*/)
481 : base_fcn_handle (name, file)
482 { }
483
484 // FIXME: is the method name supposed to be just the method name or
485 // also contain the object name?
486
487 class_simple_fcn_handle (const std::string& class_nm,
488 const std::string& meth_nm);
489
490 class_simple_fcn_handle (const octave_value& fcn,
491 const std::string& class_nm,
492 const std::string& meth_nm);
493
494 class_simple_fcn_handle (const octave_value& obj, const octave_value& fcn,
495 const std::string& class_nm,
496 const std::string& meth_nm);
497
498 class_simple_fcn_handle (const class_simple_fcn_handle&) = default;
499
500 ~class_simple_fcn_handle (void) = default;
501
502 class_simple_fcn_handle * clone (void) const
503 {
504 return new class_simple_fcn_handle (*this);
505 }
506
507 std::string type (void) const { return "classsimple"; }
508
509 bool is_class_simple (void) const { return true; }
510
511 octave_value_list call (int nargout, const octave_value_list& args);
512
513 // FIXME: These must go away. They don't do the right thing for
514 // scoping or overloads.
515 octave_function * function_value (bool = false)
516 {
517 // FIXME: Shouldn't the lookup rules here match those used in the
518 // call method?
519
520 if (m_fcn.is_defined ())
521 return m_fcn.function_value ();
522
523 symbol_table& symtab = __get_symbol_table__ ();
524
525 // FIXME: is caching the correct thing to do?
526 // Cache this value so that the pointer will be valid as long as the
527 // function handle object is valid.
528
529 // FIXME: This should probably dispatch to the respective class method.
530 // But that breaks if a function handle is used in a class method with
531 // e.g. bsxfun with arguments of a different class (see bug #59661).
532 // m_fcn = symtab.find_method (m_name, m_dispatch_class);
533 m_fcn = symtab.find_function (m_name, octave_value_list ());
534
535 return m_fcn.is_defined () ? m_fcn.function_value () : nullptr;
536 }
537
538 octave_user_function * user_function_value (bool = false)
539 {
540 return m_fcn.user_function_value ();
541 }
542
543 octave_value fcn_val (void) { return m_fcn; }
544
545 // Should be const.
546 octave_scalar_map info (void);
547
548 std::string dispatch_class (void) const { return m_dispatch_class; }
549
550 bool save_ascii (std::ostream& os);
551
552 bool load_ascii (std::istream& is);
553
554 bool save_binary (std::ostream& os, bool save_as_floats);
555
556 bool load_binary (std::istream& is, bool swap, mach_info::float_format fmt);
557
558 bool save_hdf5 (octave_hdf5_id loc_id, const char *name,
559 bool save_as_floats);
560
561 bool load_hdf5 (octave_hdf5_id& group_hid, octave_hdf5_id& space_hid,
562 octave_hdf5_id& type_hid);
563
564 void print_raw (std::ostream&, bool pr_as_read_syntax,
565 int current_print_indent_level) const;
566
567 friend bool is_equal_to (const class_simple_fcn_handle& fh1,
568 const class_simple_fcn_handle& fh2);
569
570 protected:
571
572 // The object containing the method we are handing.
573 octave_value m_obj;
574
575 // The method we are handling.
576 octave_value m_fcn;
577
578 // Name of the class that m_fcn belongs to.
579 std::string m_dispatch_class;
580 };
581
582 // Handles to anonymous functions are similar to handles to nested
583 // functions. If they are created in a context that contains nested
584 // functions, then they store a link to the parent call stack frames
585 // that are active when they are created. These call stack frames
586 // (closure frames) provide access to variables needed by any nested
587 // functions that are called from the anonymous function. Anonymous
588 // functions also store a list of values from their parent scope
589 // corresponding to the symbols in the anonymous function. This list
590 // of values captures the variable values that are visible in the
591 // scope where they are created.
592 //
593 // Note that because handles to anonymous and nested functions capture
594 // call stack frames when they are created, they will cause deletion
595 // of the values in those frames to be deferred until the handles to
596 // the anonymous or nested functions are deleted.
597 //
598 // Would it be possible to avoid storing the closure frames for
599 // handles to anonymous functions if we can determine that the
600 // anonymous function has no unbound variables (or parameters, which
601 // could be handles to nested functions?) or if it is not created in a
602 // context that contains nested functions?
603 //
604 // Would it be possible to define anonymous functions as a special
605 // type of nested function object that also has an variable
606 // initialization list associated with it?
607
608 class base_anonymous_fcn_handle : public base_fcn_handle
609 {
610 public:
611
612 static const std::string anonymous;
613
614 // Setting NAME here is a bit of a kluge to cope with a bad choice
615 // made to append the number of local variables to the @<anonymous>
616 // tag in the binary file format. See also the save_binary and
617 // load_binary functions.
618
619 base_anonymous_fcn_handle (const std::string& name = "")
620 : base_fcn_handle (name)
621 { }
622
623 base_anonymous_fcn_handle (const octave_value& fcn,
624 const stack_frame::local_vars_map& local_vars)
625 : base_fcn_handle (anonymous), m_fcn (fcn), m_local_vars (local_vars)
626 { }
627
628 base_anonymous_fcn_handle (const base_anonymous_fcn_handle&) = default;
629
630 ~base_anonymous_fcn_handle (void) = default;
631
632 std::string type (void) const { return "anonymous"; }
633
634 bool is_anonymous (void) const { return true; }
635
636 // FIXME: These must go away. They don't do the right thing for
637 // scoping or overloads.
638 octave_function * function_value (bool = false)
639 {
640 return m_fcn.function_value ();
641 }
642
643 octave_user_function * user_function_value (bool = false)
644 {
645 return m_fcn.user_function_value ();
646 }
647
648 octave_value fcn_val (void) { return m_fcn; }
649
650 virtual octave_value workspace (void) const = 0;
651
652 // Should be const.
653 octave_scalar_map info (void);
654
655 bool save_ascii (std::ostream& os);
656
657 bool load_ascii (std::istream& is);
658
659 bool save_binary (std::ostream& os, bool save_as_floats);
660
661 bool load_binary (std::istream& is, bool swap, mach_info::float_format fmt);
662
663 bool save_hdf5 (octave_hdf5_id loc_id, const char *name,
664 bool save_as_floats);
665
666 bool load_hdf5 (octave_hdf5_id& group_hid, octave_hdf5_id& space_hid,
667 octave_hdf5_id& type_hid);
668
669 void print_raw (std::ostream&, bool pr_as_read_syntax,
670 int current_print_indent_level) const;
671
672 // Anonymous function handles are printed without a newline.
673 bool print_as_scalar (void) const { return false; }
674
675 bool parse (const std::string& fcn_text);
676
677 protected:
678
679 // The function we are handling.
680 octave_value m_fcn;
681
682 // List of captured variable values for anonymous fucntions.
683 stack_frame::local_vars_map m_local_vars;
684 };
685
686 class anonymous_fcn_handle : public base_anonymous_fcn_handle
687 {
688 public:
689
690 using base_anonymous_fcn_handle::anonymous;
691
692 // Setting NAME here is a bit of a kluge to cope with a bad choice
693 // made to append the number of local variables to the @<anonymous>
694 // tag in the binary file format. See also the save_binary and
695 // load_binary functions.
696
697 anonymous_fcn_handle (const std::string& name = "")
698 : base_anonymous_fcn_handle (name), m_stack_context ()
699 { }
700
701 anonymous_fcn_handle (const octave_value& fcn,
702 const stack_frame::local_vars_map& local_vars,
703 const std::shared_ptr<stack_frame>& stack_context = std::shared_ptr<stack_frame> ());
704
705 anonymous_fcn_handle (const anonymous_fcn_handle&) = default;
706
707 ~anonymous_fcn_handle (void) = default;
708
709 anonymous_fcn_handle * clone (void) const
710 {
711 return new anonymous_fcn_handle (*this);
712 }
713
714 octave_value make_weak_anonymous_handle (void) const;
715
716 octave_value_list call (int nargout, const octave_value_list& args);
717
718 octave_value workspace (void) const;
719
720 friend bool is_equal_to (const anonymous_fcn_handle& fh1,
721 const anonymous_fcn_handle& fh2);
722
723 std::shared_ptr<stack_frame> stack_context (void) const
724 {
725 return m_stack_context;
726 }
727
728 protected:
729
730 // Pointer to closure stack frames.
731 std::shared_ptr<stack_frame> m_stack_context;
732 };
733
734 class weak_anonymous_fcn_handle : public base_anonymous_fcn_handle
735 {
736 public:
737
738 using base_anonymous_fcn_handle::anonymous;
739
740 weak_anonymous_fcn_handle (const anonymous_fcn_handle& afh)
741 : base_anonymous_fcn_handle (afh), m_stack_context (afh.stack_context ())
742 { }
743
744 weak_anonymous_fcn_handle (const weak_anonymous_fcn_handle&) = default;
745
746 ~weak_anonymous_fcn_handle (void) = default;
747
748 weak_anonymous_fcn_handle * clone (void) const
749 {
750 return new weak_anonymous_fcn_handle (*this);
751 }
752
753 bool is_weak_anonymous (void) const { return true; }
754
755 octave_value_list call (int nargout, const octave_value_list& args);
756
757 octave_value workspace (void) const;
758
759 friend bool is_equal_to (const weak_anonymous_fcn_handle& fh1,
760 const weak_anonymous_fcn_handle& fh2);
761
762 protected:
763
764 // Pointer to closure stack frames.
765 std::weak_ptr<stack_frame> m_stack_context;
766 };
767
768 extern bool is_equal_to (const anonymous_fcn_handle& fh1,
769 const anonymous_fcn_handle& fh2);
770
771 static void err_invalid_fcn_handle (const std::string& name)
772 {
773 error ("invalid function handle, unable to find function for @%s",
774 name.c_str ());
775 }
776
777 octave_value base_fcn_handle::make_weak_nested_handle (void) const
778 {
779 std::string type_str = type ();
780 error ("invalid conversion from %s handle to weak nestead handle",
781 type_str.c_str ());
782 }
783
784 octave_value base_fcn_handle::make_weak_anonymous_handle (void) const
785 {
786 std::string type_str = type ();
787 error ("invalid conversion from %s handle to weak anonymous handle",
788 type_str.c_str ());
789 }
790
791 octave_value_list
792 base_fcn_handle::subsref (const std::string& type,
793 const std::list<octave_value_list>& idx,
794 int nargout)
795 {
796 octave_value_list retval;
797
798 switch (type[0])
799 {
800 case '(':
2731 { 801 {
2732 octave_value ov_frames = m_stack_context->workspace (); 802 int tmp_nargout = (type.length () > 1 && nargout == 0) ? 1 : nargout;
2733 cell_frames = ov_frames.cell_value (); 803
804 retval = call (tmp_nargout, idx.front ());
2734 } 805 }
2735 806 break;
2736 octave_idx_type num_frames = cell_frames.numel (); 807
2737 // FIXME: It seems there should be a simple way to concatenate cells... 808 case '{':
2738 Cell retval = Cell (num_frames+1, 1); 809 case '.':
2739 retval(0) = m_local_vars; 810 error ("function handle cannot be indexed with %c", type[0]);
2740 for (octave_idx_type i = 0; i < num_frames; i++) 811
2741 retval(i+1) = cell_frames(i); 812 default:
2742 813 panic_impossible ();
2743 return retval; 814 }
2744 } 815
2745 816 // FIXME: perhaps there should be an
2746 bool is_equal_to (const anonymous_fcn_handle& fh1, 817 // octave_value_list::next_subsref member function? See also
2747 const anonymous_fcn_handle& fh2) 818 // octave_builtin::subsref.
2748 { 819
2749 if (fh1.m_fcn.is_defined () && fh2.m_fcn.is_defined ()) 820 if (idx.size () > 1)
2750 return fh1.m_fcn.is_copy_of (fh2.m_fcn); 821 retval = retval(0).next_subsref (nargout, type, idx);
2751 else 822
823 return retval;
824 }
825
826 octave_value
827 base_fcn_handle::convert_to_str_internal (bool, bool, char type) const
828 {
829 std::ostringstream buf;
830 print_raw (buf, true, 0);
831 return octave_value (buf.str (), type);
832 }
833
834 bool
835 base_fcn_handle::save_ascii (std::ostream&)
836 {
837 unimplemented ("save", "text");
838
839 return true;
840 }
841
842 bool
843 base_fcn_handle::load_ascii (std::istream&)
844 {
845 unimplemented ("load", "text");
846
847 return true;
848 }
849
850 bool
851 base_fcn_handle::save_binary (std::ostream&, bool)
852 {
853 unimplemented ("save", "binary");
854
855 return true;
856 }
857
858 bool
859 base_fcn_handle::load_binary (std::istream&, bool, mach_info::float_format)
860 {
861 unimplemented ("load", "binary");
862
863 return true;
864 }
865
866 bool
867 base_fcn_handle::save_hdf5 (octave_hdf5_id, const char *, bool)
868 {
869 unimplemented ("save", "hdf5");
870
871 return true;
872 }
873
874 bool
875 base_fcn_handle::load_hdf5 (octave_hdf5_id&, octave_hdf5_id&, octave_hdf5_id&)
876 {
877 unimplemented ("load", "hdf5");
878
879 return true;
880 }
881
882 void base_fcn_handle::warn_load (const char *file_type) const
883 {
884 std::string obj_type = type ();
885
886 warning_with_id
887 ("Octave:load-save-unavailable",
888 "%s: loading %s files not available in this version of Octave",
889 obj_type.c_str (), file_type);
890 }
891
892 void base_fcn_handle::warn_save (const char *file_type) const
893 {
894 std::string obj_type = type ();
895
896 warning_with_id
897 ("Octave:load-save-unavailable",
898 "%s: saving %s files not available in this version of Octave",
899 obj_type.c_str (), file_type);
900 }
901
902 void base_fcn_handle::unimplemented (const char *op, const char *fmt) const
903 {
904 std::string htype = type ();
905
906 warning ("%s for %s handles with %s format is not implemented",
907 op, htype.c_str (), fmt);
908 }
909
910 octave_value_list
911 invalid_fcn_handle::call (int, const octave_value_list&)
912 {
913 error ("invalid call to invalid function handle");
914 }
915
916 octave_value_list
917 internal_fcn_handle::call (int nargout, const octave_value_list& args)
918 {
919 interpreter& interp = __get_interpreter__ ();
920
921 return interp.feval (m_fcn, args, nargout);
922 }
923
924 octave_scalar_map internal_fcn_handle::info (void)
925 {
926 octave_scalar_map m;
927
928 m.setfield ("function", fcn_name ());
929 m.setfield ("type", type ());
930 m.setfield ("file", "");
931
932 return m;
933 }
934
935 bool is_equal_to (const internal_fcn_handle& fh1,
936 const internal_fcn_handle& fh2)
937 {
938 if (fh1.m_name == fh2.m_name
939 && fh1.m_fcn.is_defined () && fh2.m_fcn.is_defined ())
940 return fh1.m_fcn.is_copy_of (fh2.m_fcn);
941 else
942 return false;
943 }
944
945 octave_value_list
946 simple_fcn_handle::call (int nargout, const octave_value_list& args)
947 {
948 // FIXME: if m_name has a '.' in the name, lookup first component. If
949 // it is a classdef meta object, then build TYPE and IDX arguments and
950 // make a subsref call using them.
951
952 interpreter& interp = __get_interpreter__ ();
953
954 octave_value fcn_to_call;
955
956 // The following code is similar to part of
957 // tree_evaluator::visit_index_expression but simpler because it
958 // handles a more restricted case.
959
960 symbol_table& symtab = interp.get_symbol_table ();
961
962 std::size_t pos = m_name.find ('.');
963
964 if (pos != std::string::npos)
965 {
966 // FIXME: check to see which of these cases actually work in
967 // Octave and Matlab. For the last two, assume handle is
968 // created before object is defined as an object.
969 //
970 // We can have one of
971 //
972 // pkg-list . fcn (args)
973 // pkg-list . cls . meth (args)
974 // class-name . method (args)
975 // class-name . static-method (args)
976 // object . method (args)
977 // object . static-method (args)
978
979 // Evaluate package elements until we find a function,
980 // classdef object, or classdef_meta object that is not a
981 // package. An object may only appear as the first element,
982 // then it must be followed directly by a function name.
983
984 std::size_t beg = 0;
985 std::size_t end = pos;
986
987 std::vector<std::string> idx_elts;
988
989 while (true)
990 {
991 end = m_name.find ('.', beg);
992
993 idx_elts.push_back (m_name.substr (beg, end-beg));
994
995 if (end == std::string::npos)
996 break;
997
998 beg = end+1;
999 }
1000
1001 std::size_t n_elts = idx_elts.size ();
1002
1003 bool have_object = false;
1004 octave_value partial_expr_val;
1005
1006 // Lazy evaluation. The first element was not known to be defined
1007 // as an object in the scope where the handle was created. See if
1008 // there is a definition in the current scope.
1009
1010 partial_expr_val = interp.varval (idx_elts[0]);
1011
1012 if (partial_expr_val.is_defined ())
1013 {
1014 if (! partial_expr_val.is_classdef_object () || n_elts != 2)
1015 err_invalid_fcn_handle (m_name);
1016
1017 have_object = true;
1018 }
1019 else
1020 partial_expr_val = symtab.find_function (idx_elts[0], ovl ());
1021
1022 std::string type;
1023 std::list<octave_value_list> arg_list;
1024
1025 for (std::size_t i = 1; i < n_elts; i++)
1026 {
1027 if (partial_expr_val.is_package ())
1028 {
1029 if (have_object)
1030 err_invalid_fcn_handle (m_name);
1031
1032 type = ".";
1033 arg_list.push_back (ovl (idx_elts[i]));
1034
1035 try
1036 {
1037 // Silently ignore extra output values.
1038
1039 octave_value_list tmp_list
1040 = partial_expr_val.subsref (type, arg_list, 0);
1041
1042 partial_expr_val
1043 = tmp_list.length () ? tmp_list(0) : octave_value ();
1044
1045 if (partial_expr_val.is_cs_list ())
1046 err_invalid_fcn_handle (m_name);
1047
1048 arg_list.clear ();
1049 }
1050 catch (const index_exception&)
1051 {
1052 err_invalid_fcn_handle (m_name);
1053 }
1054 }
1055 else if (have_object || partial_expr_val.is_classdef_meta ())
1056 {
1057 // Object or class name must be the next to the last
1058 // element (it was the previous one, so if this is the
1059 // final element, it should be a classdef method,
1060 // but we'll let the classdef or classdef_meta subsref
1061 // function sort that out.
1062
1063 if (i != n_elts-1)
1064 err_invalid_fcn_handle (m_name);
1065
1066 type = ".(";
1067 arg_list.push_back (ovl (idx_elts[i]));
1068 arg_list.push_back (args);
1069
1070 return partial_expr_val.subsref (type, arg_list, nargout);
1071 }
1072 else
1073 err_invalid_fcn_handle (m_name);
1074 }
1075
1076 // If we get here, we must have a function to call.
1077
1078 if (! partial_expr_val.is_function ())
1079 err_invalid_fcn_handle (m_name);
1080
1081 fcn_to_call = partial_expr_val;
1082 }
1083 else
1084 {
1085 // No "." in the name.
1086
1087 // Perform function lookup given current arguments. We'll need
1088 // to do this regardless of whether a function was found when
1089 // the handle was created.
1090
1091 octave_value ov_fcn = symtab.find_function (m_name, args);
1092
1093 if (m_fcn.is_defined ())
1094 {
1095 // A simple function was found when the handle was created.
1096 // Use that unless we find a class method to override it.
1097
1098 fcn_to_call = m_fcn;
1099
1100 if (ov_fcn.is_defined ())
1101 {
1102 octave_function *fcn = ov_fcn.function_value ();
1103
1104 std::string dispatch_class = fcn->dispatch_class ();
1105
1106 if (fcn->is_class_method ())
1107 {
1108 // Function found through lookup is a class method
1109 // so use it instead of the simple one found when
1110 // the handle was created.
1111
1112 fcn_to_call = ov_fcn;
1113 }
1114 }
1115 }
1116 else
1117 {
1118 // There was no simple function found when the handle was
1119 // created so use the one found here (if any).
1120
1121 fcn_to_call = ov_fcn;
1122 }
1123 }
1124
1125 if (! fcn_to_call.is_defined ())
1126 err_invalid_fcn_handle (m_name);
1127
1128 return interp.feval (fcn_to_call, args, nargout);
1129 }
1130
1131 octave_function *simple_fcn_handle::function_value (bool)
1132 {
1133 // FIXME: Shouldn't the lookup rules here match those used in the
1134 // call method?
1135
1136 if (m_fcn.is_defined ())
1137 return m_fcn.function_value ();
1138
1139 symbol_table& symtab = __get_symbol_table__ ();
1140
1141 // FIXME: is caching the correct thing to do?
1142 // Cache this value so that the pointer will be valid as long as the
1143 // function handle object is valid.
1144
1145 m_fcn = symtab.find_function (m_name, octave_value_list ());
1146
1147 return m_fcn.is_defined () ? m_fcn.function_value () : nullptr;
1148 }
1149
1150 octave_user_function *simple_fcn_handle::user_function_value (bool)
1151 {
1152 // FIXME: Shouldn't the lookup rules here match those used in the
1153 // call method?
1154
1155 if (m_fcn.is_defined ())
1156 return m_fcn.user_function_value ();
1157
1158 symbol_table& symtab = __get_symbol_table__ ();
1159
1160 // FIXME: is caching the correct thing to do?
1161 // Cache this value so that the pointer will be valid as long as the
1162 // function handle object is valid.
1163
1164 m_fcn = symtab.find_user_function (m_name);
1165
1166 return m_fcn.is_defined () ? m_fcn.user_function_value () : nullptr;
1167 }
1168
1169 octave_value simple_fcn_handle::fcn_val (void)
1170 {
1171 if (m_fcn.is_defined ())
1172 return m_fcn;
1173
1174 symbol_table& symtab = __get_symbol_table__ ();
1175
1176 // FIXME: is caching the correct thing to do?
1177 // Cache this value so that the pointer will be valid as long as the
1178 // function handle object is valid.
1179
1180 m_fcn = symtab.find_user_function (m_name);
1181
1182 return m_fcn;
1183 }
1184
1185 octave_scalar_map simple_fcn_handle::info (void)
1186 {
1187 octave_scalar_map m;
1188
1189 m.setfield ("function", fcn_name ());
1190 m.setfield ("type", type ());
1191 // When is FILE defined for simple function handles?
1192 m.setfield ("file", file ());
1193
1194 return m;
1195 }
1196
1197 bool simple_fcn_handle::save_ascii (std::ostream& os)
1198 {
1199 os << "# octaveroot: " << config::octave_exec_home () << "\n";
1200
1201 std::string fnm = file ();
1202 if (! fnm.empty ())
1203 os << "# path: " << fnm << "\n";
1204
1205 os << "# subtype: " << type () << "\n";
1206
1207 os << m_name << "\n";
1208
1209 return true;
1210 }
1211
1212 bool simple_fcn_handle::load_ascii (std::istream& is)
1213 {
1214 // FIXME: If m_file is not empty, try to load the file and define
1215 // the function? Is it an error if that fails? Or should this job
1216 // always be deferred until the handle is used?
1217
1218 return is.good ();
1219 }
1220
1221 bool simple_fcn_handle::save_binary (std::ostream& os, bool)
1222 {
1223 std::ostringstream nmbuf;
1224
1225 // When is FILE defined for simple function handles?
1226 std::string fnm;
1227
1228 nmbuf << m_name << "@<simple>\n" << config::octave_exec_home ()
1229 << "\n" << fnm;
1230
1231 std::string buf_str = nmbuf.str ();
1232 int32_t tmp = buf_str.length ();
1233 os.write (reinterpret_cast<char *> (&tmp), 4);
1234 os.write (buf_str.c_str (), buf_str.length ());
1235
1236 return true;
1237 }
1238
1239 bool simple_fcn_handle::load_binary (std::istream& is, bool,
1240 mach_info::float_format)
1241 {
1242 return is.good ();
1243 }
1244
1245 bool simple_fcn_handle::save_hdf5 (octave_hdf5_id loc_id, const char *name,
1246 bool)
1247 {
1248 #if defined (HAVE_HDF5)
1249
1250 bool retval = true;
1251
1252 octave_hdf5_id group_hid = -1;
1253 #if defined (HAVE_HDF5_18)
1254 group_hid = H5Gcreate (loc_id, name, octave_H5P_DEFAULT, octave_H5P_DEFAULT,
1255 octave_H5P_DEFAULT);
1256 #else
1257 group_hid = H5Gcreate (loc_id, name, 0);
1258 #endif
1259 if (group_hid < 0)
1260 return false;
1261
1262 octave_hdf5_id space_hid, data_hid, type_hid;
1263 space_hid = data_hid = type_hid = -1;
1264
1265 // attach the type of the variable
1266 type_hid = H5Tcopy (H5T_C_S1);
1267 H5Tset_size (type_hid, m_name.length () + 1);
1268 if (type_hid < 0)
1269 {
1270 H5Gclose (group_hid);
2752 return false; 1271 return false;
2753 } 1272 }
2754 1273
2755 octave_value_list 1274 OCTAVE_LOCAL_BUFFER (hsize_t, hdims, 2);
2756 weak_anonymous_fcn_handle::call (int nargout, const octave_value_list& args) 1275 hdims[0] = 0;
2757 { 1276 hdims[1] = 0;
2758 tree_evaluator& tw = __get_evaluator__ (); 1277 space_hid = H5Screate_simple (0, hdims, nullptr);
2759 1278 if (space_hid < 0)
2760 octave_user_function *oct_usr_fcn = m_fcn.user_function_value (); 1279 {
2761 1280 H5Tclose (type_hid);
2762 std::shared_ptr<stack_frame> frames = m_stack_context.lock (); 1281 H5Gclose (group_hid);
2763
2764 tw.push_stack_frame (oct_usr_fcn, m_local_vars, frames);
2765
2766 unwind_action act ([&tw] () { tw.pop_stack_frame (); });
2767
2768 return oct_usr_fcn->execute (tw, nargout, args);
2769 }
2770
2771 octave_value weak_anonymous_fcn_handle::workspace (void) const
2772 {
2773 octave_scalar_map local_vars_map;
2774
2775 for (const auto& nm_val : m_local_vars)
2776 local_vars_map.assign (nm_val.first, nm_val.second);
2777
2778 // FIXME: it would be more convenient if stack_frame::workspace
2779 // returned a Cell object directly instead of a Cell in an
2780 // octave_value object.
2781
2782 std::shared_ptr<stack_frame> frames = m_stack_context.lock ();
2783
2784 Cell cell_frames;
2785
2786 if (frames)
2787 {
2788 octave_value ov_frames = frames->workspace ();
2789 cell_frames = ov_frames.cell_value ();
2790 }
2791
2792 octave_idx_type num_frames = cell_frames.numel ();
2793
2794 // FIXME: It seems there should be a simple way to concatenate
2795 // cells...
2796 Cell retval = Cell (num_frames+1, 1);
2797 retval(0) = m_local_vars;
2798 for (octave_idx_type i = 0; i < num_frames; i++)
2799 retval(i+1) = cell_frames(i);
2800
2801 return retval;
2802 }
2803
2804 bool is_equal_to (const weak_anonymous_fcn_handle& fh1,
2805 const weak_anonymous_fcn_handle& fh2)
2806 {
2807 if (fh1.m_name == fh2.m_name
2808 && fh1.m_fcn.is_defined () && fh2.m_fcn.is_defined ())
2809 return fh1.m_fcn.is_copy_of (fh2.m_fcn);
2810 else
2811 return false; 1282 return false;
2812 } 1283 }
1284 #if defined (HAVE_HDF5_18)
1285 data_hid = H5Dcreate (group_hid, "nm", type_hid, space_hid,
1286 octave_H5P_DEFAULT, octave_H5P_DEFAULT,
1287 octave_H5P_DEFAULT);
1288 #else
1289 data_hid = H5Dcreate (group_hid, "nm", type_hid, space_hid,
1290 octave_H5P_DEFAULT);
1291 #endif
1292 if (data_hid < 0
1293 || H5Dwrite (data_hid, type_hid, octave_H5S_ALL, octave_H5S_ALL,
1294 octave_H5P_DEFAULT, m_name.c_str ()) < 0)
1295 {
1296 H5Sclose (space_hid);
1297 H5Tclose (type_hid);
1298 H5Gclose (group_hid);
1299 return false;
1300 }
1301 H5Dclose (data_hid);
1302
1303 std::string octaveroot = config::octave_exec_home ();
1304
1305 // When is FILE defined for simple fucntion handles?
1306 std::string fpath;
1307
1308 H5Sclose (space_hid);
1309 hdims[0] = 1;
1310 hdims[1] = octaveroot.length ();
1311 space_hid = H5Screate_simple (0, hdims, nullptr);
1312 if (space_hid < 0)
1313 {
1314 H5Tclose (type_hid);
1315 H5Gclose (group_hid);
1316 return false;
1317 }
1318
1319 H5Tclose (type_hid);
1320 type_hid = H5Tcopy (H5T_C_S1);
1321 H5Tset_size (type_hid, octaveroot.length () + 1);
1322 octave_hdf5_id a_id;
1323 #if defined (HAVE_HDF5_18)
1324 a_id = H5Acreate (group_hid, "OCTAVEROOT", type_hid, space_hid,
1325 octave_H5P_DEFAULT, octave_H5P_DEFAULT);
1326 #else
1327 a_id = H5Acreate (group_hid, "OCTAVEROOT", type_hid, space_hid,
1328 octave_H5P_DEFAULT);
1329 #endif
1330
1331 if (a_id >= 0)
1332 {
1333 retval = (H5Awrite (a_id, type_hid, octaveroot.c_str ()) >= 0);
1334
1335 H5Aclose (a_id);
1336 }
1337 else
1338 {
1339 H5Sclose (space_hid);
1340 H5Tclose (type_hid);
1341 H5Gclose (group_hid);
1342 return false;
1343 }
1344
1345 H5Sclose (space_hid);
1346 hdims[0] = 1;
1347 hdims[1] = fpath.length ();
1348 space_hid = H5Screate_simple (0, hdims, nullptr);
1349 if (space_hid < 0)
1350 {
1351 H5Tclose (type_hid);
1352 H5Gclose (group_hid);
1353 return false;
1354 }
1355
1356 H5Tclose (type_hid);
1357 type_hid = H5Tcopy (H5T_C_S1);
1358 H5Tset_size (type_hid, fpath.length () + 1);
1359
1360 #if defined (HAVE_HDF5_18)
1361 a_id = H5Acreate (group_hid, "FILE", type_hid, space_hid,
1362 octave_H5P_DEFAULT, octave_H5P_DEFAULT);
1363 #else
1364 a_id = H5Acreate (group_hid, "FILE", type_hid, space_hid,
1365 octave_H5P_DEFAULT);
1366 #endif
1367
1368 if (a_id >= 0)
1369 {
1370 retval = (H5Awrite (a_id, type_hid, fpath.c_str ()) >= 0);
1371
1372 H5Aclose (a_id);
1373 }
1374 else
1375 retval = false;
1376
1377 H5Sclose (space_hid);
1378 H5Tclose (type_hid);
1379 H5Gclose (group_hid);
1380
1381 return retval;
1382
1383 #else
1384
1385 octave_unused_parameter (loc_id);
1386 octave_unused_parameter (name);
1387
1388 warn_save ("hdf5");
1389
1390 return false;
1391
1392 #endif
1393 }
1394
1395 bool simple_fcn_handle::load_hdf5 (octave_hdf5_id& group_hid,
1396 octave_hdf5_id& space_hid,
1397 octave_hdf5_id& type_hid)
1398 {
1399 #if defined (HAVE_HDF5)
1400
1401 unimplemented ("load", "hdf5");
1402
1403 octave_unused_parameter (group_hid);
1404 octave_unused_parameter (space_hid);
1405 octave_unused_parameter (type_hid);
1406
1407 return true;
1408
1409 #else
1410
1411 octave_unused_parameter (group_hid);
1412 octave_unused_parameter (space_hid);
1413 octave_unused_parameter (type_hid);
1414
1415 return false;
1416
1417 #endif
1418 }
1419
1420 void simple_fcn_handle::print_raw (std::ostream& os, bool pr_as_read_syntax,
1421 int current_print_indent_level) const
1422 {
1423 octave_print_internal (os, '@' + m_name, pr_as_read_syntax,
1424 current_print_indent_level);
1425 }
1426
1427 bool is_equal_to (const simple_fcn_handle& fh1, const simple_fcn_handle& fh2)
1428 {
1429 if (fh1.m_name == fh2.m_name)
1430 {
1431 if (fh1.m_fcn.is_defined () && fh2.m_fcn.is_defined ())
1432 return fh1.m_fcn.is_copy_of (fh2.m_fcn);
1433
1434 if (fh1.m_fcn.is_undefined () && fh2.m_fcn.is_undefined ())
1435 return true;
1436 }
1437
1438 return false;
1439 }
1440
1441 scoped_fcn_handle::scoped_fcn_handle (const octave_value& fcn,
1442 const std::string& name,
1443 const std::list<std::string>& parentage)
1444 : base_fcn_handle (name), m_fcn (fcn), m_parentage (parentage)
1445 {
1446 // FIXME: should it be an error if FCN is undefined?
1447
1448 if (m_fcn.is_defined ())
1449 {
1450 octave_function *oct_fcn = m_fcn.function_value ();
1451
1452 if (oct_fcn)
1453 m_file = oct_fcn->fcn_file_name ();
1454 }
1455
1456 m_parentage.push_front (name);
1457 }
1458
1459 octave_value_list
1460 scoped_fcn_handle::call (int nargout, const octave_value_list& args)
1461 {
1462 // FIXME: we aren't really using the scope yet. Hmm.
1463
1464 interpreter& interp = __get_interpreter__ ();
1465
1466 if (! m_fcn.is_defined ())
1467 {
1468 // Try to find it?
1469
1470 find_function ();
1471 }
1472
1473 if (! m_fcn.is_defined ())
1474 err_invalid_fcn_handle (m_name);
1475
1476 return interp.feval (m_fcn, args, nargout);
1477 }
1478
1479 octave_scalar_map scoped_fcn_handle::info (void)
1480 {
1481 octave_scalar_map m;
1482
1483 m.setfield ("function", fcn_name ());
1484 m.setfield ("type", type ());
1485 m.setfield ("file", file ());
1486
1487 m.setfield ("parentage", Cell (m_parentage));
1488
1489 return m;
1490 }
1491
1492 bool scoped_fcn_handle::save_ascii (std::ostream& os)
1493 {
1494 os << "# octaveroot: " << config::octave_exec_home () << "\n";
1495
1496 std::string fnm = file ();
1497 if (! fnm.empty ())
1498 os << "# path: " << fnm << "\n";
1499
1500 os << "# subtype: " << type () << "\n";
1501
1502 os << m_name << "\n";
1503
1504 octave_value tmp = Cell (m_parentage);
1505 tmp.save_ascii (os);
1506
1507 return os.good ();
1508 }
1509
1510 bool scoped_fcn_handle::load_ascii (std::istream& is)
1511 {
1512 octave_cell ov_cell;
1513 ov_cell.load_ascii (is);
1514
1515 if (ov_cell.iscellstr ())
1516 {
1517 Array<std::string> cellstr_val = ov_cell.cellstr_value ();
1518
1519 for (octave_idx_type i = 0; i < cellstr_val.numel (); i++)
1520 m_parentage.push_back (cellstr_val(i));
1521 }
1522
1523 return is.good ();
1524 }
1525
1526 bool scoped_fcn_handle::save_binary (std::ostream& os, bool save_as_floats)
1527 {
1528 std::ostringstream nmbuf;
1529
1530 std::string fnm = file ();
1531
1532 nmbuf << m_name << "@<scopedfunction>\n" << config::octave_exec_home ()
1533 << "\n" << fnm;
1534
1535 std::string buf_str = nmbuf.str ();
1536 int32_t len = buf_str.length ();
1537 os.write (reinterpret_cast<char *> (&len), 4);
1538 os.write (buf_str.c_str (), buf_str.length ());
1539
1540 octave_value tmp = Cell (m_parentage);
1541 tmp.save_binary (os, save_as_floats);
1542
1543 return os.good ();
1544 }
1545
1546 bool scoped_fcn_handle::load_binary (std::istream& is, bool swap,
1547 mach_info::float_format fmt)
1548 {
1549 octave_cell ov_cell;
1550 ov_cell.load_binary (is, swap, fmt);
1551
1552 if (ov_cell.iscellstr ())
1553 {
1554 Array<std::string> cellstr_val = ov_cell.cellstr_value ();
1555
1556 for (octave_idx_type i = 0; i < cellstr_val.numel (); i++)
1557 m_parentage.push_back (cellstr_val(i));
1558 }
1559
1560 return is.good ();
1561 }
1562
1563 bool scoped_fcn_handle::save_hdf5 (octave_hdf5_id loc_id, const char *name,
1564 bool)
1565 {
1566 #if defined (HAVE_HDF5)
1567
1568 unimplemented ("save", "hdf5");
1569
1570 // FIXME: save parentage.
1571
1572 octave_unused_parameter (loc_id);
1573 octave_unused_parameter (name);
1574
1575 return true;
1576
1577 #else
1578
1579 octave_unused_parameter (loc_id);
1580 octave_unused_parameter (name);
1581
1582 warn_save ("hdf5");
1583
1584 return false;
1585
1586 #endif
1587 }
1588
1589 bool scoped_fcn_handle::load_hdf5 (octave_hdf5_id& group_hid,
1590 octave_hdf5_id& space_hid,
1591 octave_hdf5_id& type_hid)
1592 {
1593 #if defined (HAVE_HDF5)
1594
1595 unimplemented ("load", "hdf5");
1596
1597 // FIXME: load parentage.
1598
1599 octave_unused_parameter (group_hid);
1600 octave_unused_parameter (space_hid);
1601 octave_unused_parameter (type_hid);
1602
1603 return true;
1604
1605 #else
1606
1607 octave_unused_parameter (group_hid);
1608 octave_unused_parameter (space_hid);
1609 octave_unused_parameter (type_hid);
1610
1611 return false;
1612
1613 #endif
1614 }
1615
1616 void scoped_fcn_handle::print_raw (std::ostream& os,
1617 bool pr_as_read_syntax,
1618 int current_print_indent_level) const
1619 {
1620 octave_print_internal (os, '@' + m_name, pr_as_read_syntax,
1621 current_print_indent_level);
1622 }
1623
1624 bool is_equal_to (const scoped_fcn_handle& fh1, const scoped_fcn_handle& fh2)
1625 {
1626 if (fh1.m_name == fh2.m_name
1627 && fh2.m_parentage == fh2.m_parentage
1628 && fh1.m_fcn.is_defined () && fh2.m_fcn.is_defined ())
1629 return fh1.m_fcn.is_copy_of (fh2.m_fcn);
1630 else
1631 return false;
1632 }
1633
1634 void scoped_fcn_handle::find_function (void)
1635 {
1636 // Since a scoped function is not visible by itself, try to load the
1637 // file named in m_file then find and define the scoped function.
1638 // It is not an error if this fails. We can report later that the
1639 // handle is invalid.
1640
1641 symbol_table& symtab = __get_symbol_table__ ();
1642
1643 if (m_parentage.size () == 1)
1644 {
1645 std::string dir_name = sys::file_ops::dirname (m_file);
1646
1647 std::size_t pos = dir_name.find_last_of (sys::file_ops::dir_sep_chars ());
1648
1649 if (pos != std::string::npos)
1650 dir_name = dir_name.substr (0, pos);
1651 else if (dir_name == "private")
1652 dir_name = ".";
1653
1654 std::string fcn_name = m_parentage.front ();
1655
1656 // FIXME: Does dir_name need to be in the load path for this to work?
1657
1658 m_fcn = symtab.find_private_function (dir_name, m_name);
1659
1660 // FIXME: Verify that it is a private function?
1661 }
1662 else
1663 {
1664 std::string primary_parent_name = m_parentage.back ();
1665
1666 octave_value ov_parent_fcn
1667 = symtab.find_user_function (primary_parent_name);
1668
1669 if (ov_parent_fcn.is_defined ())
1670 {
1671 octave_user_function *fcn = ov_parent_fcn.user_function_value ();
1672
1673 if (fcn)
1674 {
1675 std::string file_name = fcn->fcn_file_name ();
1676
1677 std::string oct_home = config::octave_exec_home ();
1678
1679 if (file_name.substr (0, oct_home.size ()) == oct_home)
1680 file_name = file_name.substr (oct_home.size ());
1681
1682 octave_value subfcn = fcn->find_subfunction (m_name);
1683
1684 if (subfcn.is_defined ())
1685 m_fcn = subfcn;
1686 }
1687 }
1688 }
1689 }
1690
1691 octave_scalar_map base_nested_fcn_handle::info (void)
1692 {
1693 octave_scalar_map m;
1694
1695 m.setfield ("function", fcn_name ());
1696 m.setfield ("type", type ());
1697 m.setfield ("file", "");
1698 m.setfield ("workspace", workspace ());
1699
1700 return m;
1701 }
1702
1703 // FIXME: For save, we need a way to save the (possibly shared)
1704 // workspace. For load, we need a way to load and link to the
1705 // (possibly shared) workspace that was saved.
1706 //
1707 // Since a nested function is not visible by itself, do we need to try
1708 // to load the file named in m_file then find and define the function?
1709 // Is it an error if that fails? Or should this job always be
1710 // deferred until the handle is used?
1711
1712 bool base_nested_fcn_handle::save_ascii (std::ostream& os)
1713 {
1714 unimplemented ("save", "text");
1715
1716 octave_unused_parameter (os);
1717
1718 return true;
1719 }
1720
1721 bool base_nested_fcn_handle::load_ascii (std::istream& is)
1722 {
1723 unimplemented ("load", "text");
1724
1725 octave_unused_parameter (is);
1726
1727 return true;
1728 }
1729
1730 bool base_nested_fcn_handle::save_binary (std::ostream& os,
1731 bool save_as_floats)
1732 {
1733 unimplemented ("save", "binary");
1734
1735 octave_unused_parameter (os);
1736 octave_unused_parameter (save_as_floats);
1737
1738 return true;
1739 }
1740
1741 bool base_nested_fcn_handle::load_binary (std::istream& is, bool swap,
1742 mach_info::float_format fmt)
1743 {
1744 unimplemented ("load", "binary");
1745
1746 octave_unused_parameter (is);
1747 octave_unused_parameter (swap);
1748 octave_unused_parameter (fmt);
1749
1750 return true;
1751 }
1752
1753 bool base_nested_fcn_handle::save_hdf5 (octave_hdf5_id loc_id,
1754 const char *name, bool)
1755 {
1756 #if defined (HAVE_HDF5)
1757
1758 unimplemented ("save", "hdf5");
1759
1760 octave_unused_parameter (loc_id);
1761 octave_unused_parameter (name);
1762
1763 return true;
1764
1765 #else
1766
1767 octave_unused_parameter (loc_id);
1768 octave_unused_parameter (name);
1769
1770 warn_save ("hdf5");
1771
1772 return false;
1773
1774 #endif
1775 }
1776
1777 bool base_nested_fcn_handle::load_hdf5 (octave_hdf5_id& group_hid,
1778 octave_hdf5_id& space_hid,
1779 octave_hdf5_id& type_hid)
1780 {
1781 #if defined (HAVE_HDF5)
1782
1783 unimplemented ("load", "hdf5");
1784
1785 octave_unused_parameter (group_hid);
1786 octave_unused_parameter (space_hid);
1787 octave_unused_parameter (type_hid);
1788
1789 return true;
1790
1791 #else
1792
1793 octave_unused_parameter (group_hid);
1794 octave_unused_parameter (space_hid);
1795 octave_unused_parameter (type_hid);
1796
1797 return false;
1798
1799 #endif
1800 }
1801
1802 void base_nested_fcn_handle::print_raw (std::ostream& os,
1803 bool pr_as_read_syntax,
1804 int current_print_indent_level) const
1805 {
1806 octave_print_internal (os, '@' + m_name, pr_as_read_syntax,
1807 current_print_indent_level);
1808 }
1809
1810 octave_value nested_fcn_handle::make_weak_nested_handle (void) const
1811 {
1812 return octave_value (new octave_fcn_handle
1813 (new weak_nested_fcn_handle (*this)));
1814 }
1815
1816 octave_value_list
1817 nested_fcn_handle::call (int nargout, const octave_value_list& args)
1818 {
1819 tree_evaluator& tw = __get_evaluator__ ();
1820
1821 octave_user_function *oct_usr_fcn = m_fcn.user_function_value ();
1822
1823 tw.push_stack_frame (oct_usr_fcn, m_stack_context);
1824
1825 unwind_action act ([&tw] () { tw.pop_stack_frame (); });
1826
1827 return oct_usr_fcn->execute (tw, nargout, args);
1828 }
1829
1830 octave_value nested_fcn_handle::workspace (void) const
1831 {
1832 return m_stack_context->workspace ();
1833 }
1834
1835 bool is_equal_to (const nested_fcn_handle& fh1, const nested_fcn_handle& fh2)
1836 {
1837 if (fh1.m_name == fh2.m_name
1838 && fh1.m_fcn.is_defined () && fh2.m_fcn.is_defined ())
1839 return fh1.m_fcn.is_copy_of (fh2.m_fcn);
1840 else
1841 return false;
1842 }
1843
1844 octave_value_list
1845 weak_nested_fcn_handle::call (int nargout, const octave_value_list& args)
1846 {
1847 tree_evaluator& tw = __get_evaluator__ ();
1848
1849 octave_user_function *oct_usr_fcn = m_fcn.user_function_value ();
1850
1851 std::shared_ptr<stack_frame> frames = m_stack_context.lock ();
1852
1853 tw.push_stack_frame (oct_usr_fcn, frames);
1854
1855 unwind_action act ([&tw] () { tw.pop_stack_frame (); });
1856
1857 return oct_usr_fcn->execute (tw, nargout, args);
1858 }
1859
1860 octave_value weak_nested_fcn_handle::workspace (void) const
1861 {
1862 std::shared_ptr<stack_frame> frames = m_stack_context.lock ();
1863
1864 return frames ? frames->workspace () : octave_value ();
1865 }
1866
1867 bool is_equal_to (const weak_nested_fcn_handle& fh1,
1868 const weak_nested_fcn_handle& fh2)
1869 {
1870 if (fh1.m_name == fh2.m_name
1871 && fh1.m_fcn.is_defined () && fh2.m_fcn.is_defined ())
1872 return fh1.m_fcn.is_copy_of (fh2.m_fcn);
1873 else
1874 return false;
1875 }
1876
1877 class_simple_fcn_handle::class_simple_fcn_handle (const std::string& class_nm,
1878 const std::string& meth_nm)
1879 : base_fcn_handle (meth_nm), m_obj (), m_fcn (),
1880 m_dispatch_class (class_nm)
1881 { }
1882
1883 class_simple_fcn_handle::class_simple_fcn_handle (const octave_value& fcn,
1884 const std::string& class_nm,
1885 const std::string& meth_nm)
1886 : base_fcn_handle (meth_nm), m_obj (), m_fcn (fcn),
1887 m_dispatch_class (class_nm)
1888 { }
1889
1890 class_simple_fcn_handle::class_simple_fcn_handle (const octave_value& obj,
1891 const octave_value& fcn,
1892 const std::string& class_nm,
1893 const std::string& meth_nm)
1894 : base_fcn_handle (meth_nm), m_obj (obj), m_fcn (fcn),
1895 m_dispatch_class (class_nm)
1896 { }
1897
1898 octave_value_list
1899 class_simple_fcn_handle::call (int nargout, const octave_value_list& args)
1900 {
1901 interpreter& interp = __get_interpreter__ ();
1902
1903 if (m_obj.is_defined ())
1904 {
1905 octave_value_list tmp_args = args;
1906 tmp_args.prepend (m_obj);
1907
1908 return interp.feval (m_fcn, tmp_args, nargout);
1909 }
1910
1911 // FIXME: is this the best approach? Should we be saving current
1912 // dispatch class and restoring that value instead of
1913 // unconditionally setting it to "" when we return from this
1914 // function?
1915
1916 tree_evaluator& tw = interp.get_evaluator ();
1917
1918 unwind_action act ([&tw] () { tw.set_dispatch_class (""); });
1919
1920 tw.set_dispatch_class (m_dispatch_class);
1921
1922 if (m_fcn.is_defined ())
1923 return interp.feval (m_fcn, args, nargout);
1924
1925 return interp.feval (fcn_name (), args, nargout);
1926 }
1927
1928 octave_scalar_map class_simple_fcn_handle::info (void)
1929 {
1930 octave_scalar_map m;
1931
1932 m.setfield ("function", fcn_name ());
1933 m.setfield ("type", type ());
1934 m.setfield ("file", "");
1935 m.setfield ("class", dispatch_class ());
1936
1937 return m;
1938 }
1939
1940 // FIXME: Since a class method is not visible by itself, do we need to
1941 // try to load the file named in m_file then find and define the
1942 // function? Is it an error if that fails? Or should this job always
1943 // be deferred until the handle is used?
1944
1945 bool class_simple_fcn_handle::save_ascii (std::ostream& os)
1946 {
1947 unimplemented ("save", "text");
1948
1949 octave_unused_parameter (os);
1950
1951 return true;
1952 }
1953
1954 bool class_simple_fcn_handle::load_ascii (std::istream& is)
1955 {
1956 unimplemented ("load", "text");
1957
1958 octave_unused_parameter (is);
1959
1960 return true;
1961 }
1962
1963 bool class_simple_fcn_handle::save_binary (std::ostream& os,
1964 bool save_as_floats)
1965 {
1966 unimplemented ("save", "binary");
1967
1968 octave_unused_parameter (os);
1969 octave_unused_parameter (save_as_floats);
1970
1971 return true;
1972 }
1973
1974 bool class_simple_fcn_handle::load_binary (std::istream& is, bool swap,
1975 mach_info::float_format fmt)
1976 {
1977 unimplemented ("load", "binary");
1978
1979 octave_unused_parameter (is);
1980 octave_unused_parameter (swap);
1981 octave_unused_parameter (fmt);
1982
1983 return true;
1984 }
1985
1986 bool class_simple_fcn_handle::save_hdf5 (octave_hdf5_id loc_id,
1987 const char *name, bool)
1988 {
1989 #if defined (HAVE_HDF5)
1990
1991 unimplemented ("save", "hdf5");
1992
1993 octave_unused_parameter (loc_id);
1994 octave_unused_parameter (name);
1995
1996 return true;
1997
1998 #else
1999
2000 octave_unused_parameter (loc_id);
2001 octave_unused_parameter (name);
2002
2003 warn_save ("hdf5");
2004
2005 return false;
2006
2007 #endif
2008 }
2009
2010 bool class_simple_fcn_handle::load_hdf5 (octave_hdf5_id& group_hid,
2011 octave_hdf5_id& space_hid,
2012 octave_hdf5_id& type_hid)
2013 {
2014 #if defined (HAVE_HDF5)
2015
2016 unimplemented ("load", "hdf5");
2017
2018 octave_unused_parameter (group_hid);
2019 octave_unused_parameter (space_hid);
2020 octave_unused_parameter (type_hid);
2021
2022 return true;
2023
2024 #else
2025
2026 octave_unused_parameter (group_hid);
2027 octave_unused_parameter (space_hid);
2028 octave_unused_parameter (type_hid);
2029
2030 return false;
2031
2032 #endif
2033 }
2034
2035 void class_simple_fcn_handle::print_raw (std::ostream& os,
2036 bool pr_as_read_syntax,
2037 int current_print_indent_level) const
2038 {
2039 octave_print_internal (os, '@' + m_name, pr_as_read_syntax,
2040 current_print_indent_level);
2041 }
2042
2043 bool is_equal_to (const class_simple_fcn_handle& fh1,
2044 const class_simple_fcn_handle& fh2)
2045 {
2046 // FIXME: Also need to check object values are equivalent?
2047
2048 if (fh1.m_name == fh2.m_name
2049 && fh1.m_fcn.is_defined () && fh2.m_fcn.is_defined ())
2050 return fh1.m_fcn.is_copy_of (fh2.m_fcn);
2051 else
2052 return false;
2053 }
2054
2055 const std::string base_anonymous_fcn_handle::anonymous ("@<anonymous>");
2056
2057 octave_scalar_map base_anonymous_fcn_handle::info (void)
2058 {
2059 octave_scalar_map m;
2060
2061 std::ostringstream buf;
2062 print_raw (buf, true, 0);
2063 m.setfield ("function", buf.str ());
2064
2065 m.setfield ("type", type ());
2066 m.setfield ("file", "");
2067 m.setfield ("workspace", workspace ());
2068 m.setfield ("within_file_path", "");
2069
2070 return m;
2071 }
2072
2073 bool base_anonymous_fcn_handle::save_ascii (std::ostream& os)
2074 {
2075 // FIXME: can we ensure that m_fcn is always defined?
2076
2077 if (m_fcn.is_undefined ())
2078 return false;
2079
2080 os << m_name << "\n";
2081
2082 print_raw (os, true, 0);
2083 os << "\n";
2084
2085 std::size_t varlen = m_local_vars.size ();
2086
2087 if (varlen > 0)
2088 {
2089 os << "# length: " << varlen << "\n";
2090
2091 for (const auto& nm_val : m_local_vars)
2092 {
2093 if (! save_text_data (os, nm_val.second, nm_val.first, false, 0))
2094 return ! os.fail ();
2095 }
2096 }
2097
2098 return true;
2099 }
2100
2101 bool base_anonymous_fcn_handle::load_ascii (std::istream& is)
2102 {
2103 octave::skip_preceeding_newline (is);
2104
2105 std::string buf;
2106
2107 if (is)
2108 {
2109 // Get a line of text whitespace characters included, leaving
2110 // newline in the stream.
2111
2112 buf = octave::read_until_newline (is, true);
2113 }
2114
2115 std::streampos pos = is.tellg ();
2116
2117 // Set up temporary scope to use for evaluating the text that
2118 // defines the anonymous function.
2119
2120 interpreter& interp = __get_interpreter__ ();
2121
2122 tree_evaluator& tw = interp.get_evaluator ();
2123
2124 tw.push_dummy_scope (buf);
2125 unwind_action_safe restore_scope (&tree_evaluator::pop_scope, &tw);
2126
2127 octave_idx_type len = 0;
2128
2129 if (extract_keyword (is, "length", len, true) && len >= 0)
2130 {
2131 if (len > 0)
2132 {
2133 for (octave_idx_type i = 0; i < len; i++)
2134 {
2135 octave_value t2;
2136 bool dummy;
2137
2138 std::string name = read_text_data (is, "", dummy, t2, i);
2139
2140 if (! is)
2141 error ("load: failed to load anonymous function handle");
2142
2143 m_local_vars[name] = t2;
2144 }
2145 }
2146 }
2147 else
2148 {
2149 is.seekg (pos);
2150 is.clear ();
2151 }
2152
2153 if (is)
2154 return parse (buf);
2155
2156 return false;
2157 }
2158
2159 bool base_anonymous_fcn_handle::save_binary (std::ostream& os,
2160 bool save_as_floats)
2161 {
2162 // FIXME: can we ensure that m_fcn is always defined?
2163
2164 if (m_fcn.is_undefined ())
2165 return false;
2166
2167 std::ostringstream nmbuf;
2168
2169 std::size_t varlen = m_local_vars.size ();
2170
2171 nmbuf << anonymous;
2172 if (varlen > 0)
2173 nmbuf << ' ' << varlen;
2174
2175 std::string buf_str = nmbuf.str ();
2176 int32_t tmp = buf_str.length ();
2177 os.write (reinterpret_cast<char *> (&tmp), 4);
2178 os.write (buf_str.c_str (), buf_str.length ());
2179
2180 std::ostringstream buf;
2181 print_raw (buf, true, 0);
2182 std::string stmp = buf.str ();
2183 tmp = stmp.length ();
2184 os.write (reinterpret_cast<char *> (&tmp), 4);
2185 os.write (stmp.c_str (), stmp.length ());
2186
2187 if (varlen > 0)
2188 {
2189 for (const auto& nm_val : m_local_vars)
2190 {
2191 if (! save_binary_data (os, nm_val.second, nm_val.first,
2192 "", 0, save_as_floats))
2193 return ! os.fail ();
2194 }
2195 }
2196
2197 return true;
2198 }
2199
2200 bool base_anonymous_fcn_handle::load_binary (std::istream& is, bool swap,
2201 mach_info::float_format fmt)
2202 {
2203 // Read extra characters in m_name as the number of local variable
2204 // values in this anonymous function.
2205
2206 octave_idx_type len = 0;
2207 std::size_t anl = anonymous.length ();
2208 if (m_name.length () > anl)
2209 {
2210 std::istringstream nm_is (m_name.substr (anl));
2211 nm_is >> len;
2212
2213 // Anonymous functions don't have names. We just used this
2214 // string as temporary storage to pass the number of local
2215 // variable values.
2216
2217 m_name = "";
2218 }
2219
2220 int32_t tmp;
2221
2222 if (! is.read (reinterpret_cast<char *> (&tmp), 4))
2223 return false;
2224 if (swap)
2225 swap_bytes<4> (&tmp);
2226
2227 OCTAVE_LOCAL_BUFFER (char, ctmp2, tmp+1);
2228 // is.get (ctmp2, tmp+1, 0); caused is.eof () to be true though
2229 // effectively not reading over file end
2230 is.read (ctmp2, tmp);
2231 ctmp2[tmp] = 0;
2232
2233 // Set up temporary scope to use for evaluating the text that
2234 // defines the anonymous function.
2235
2236 interpreter& interp = __get_interpreter__ ();
2237
2238 tree_evaluator& tw = interp.get_evaluator ();
2239
2240 tw.push_dummy_scope (ctmp2);
2241 unwind_action_safe restore_scope (&tree_evaluator::pop_scope, &tw);
2242
2243 if (len > 0)
2244 {
2245 for (octave_idx_type i = 0; i < len; i++)
2246 {
2247 octave_value t2;
2248 bool dummy;
2249 std::string doc;
2250
2251 std::string name
2252 = read_binary_data (is, swap, fmt, "", dummy, t2, doc);
2253
2254 if (! is)
2255 error ("load: failed to load anonymous function handle");
2256
2257 m_local_vars[name] = t2;
2258 }
2259 }
2260
2261 if (is)
2262 return parse (ctmp2);
2263
2264 return false;
2265 }
2266
2267 bool base_anonymous_fcn_handle::save_hdf5 (octave_hdf5_id loc_id,
2268 const char *name,
2269 bool save_as_floats)
2270 {
2271 #if defined (HAVE_HDF5)
2272
2273 bool retval = true;
2274
2275 octave_hdf5_id group_hid = -1;
2276 #if defined (HAVE_HDF5_18)
2277 group_hid = H5Gcreate (loc_id, name, octave_H5P_DEFAULT, octave_H5P_DEFAULT,
2278 octave_H5P_DEFAULT);
2279 #else
2280 group_hid = H5Gcreate (loc_id, name, 0);
2281 #endif
2282 if (group_hid < 0)
2283 return false;
2284
2285 octave_hdf5_id space_hid, data_hid, type_hid;
2286 space_hid = data_hid = type_hid = -1;
2287
2288 // attach the type of the variable
2289 type_hid = H5Tcopy (H5T_C_S1);
2290 H5Tset_size (type_hid, m_name.length () + 1);
2291 if (type_hid < 0)
2292 {
2293 H5Gclose (group_hid);
2294 return false;
2295 }
2296
2297 OCTAVE_LOCAL_BUFFER (hsize_t, hdims, 2);
2298 hdims[0] = 0;
2299 hdims[1] = 0;
2300 space_hid = H5Screate_simple (0, hdims, nullptr);
2301 if (space_hid < 0)
2302 {
2303 H5Tclose (type_hid);
2304 H5Gclose (group_hid);
2305 return false;
2306 }
2307 #if defined (HAVE_HDF5_18)
2308 data_hid = H5Dcreate (group_hid, "nm", type_hid, space_hid,
2309 octave_H5P_DEFAULT, octave_H5P_DEFAULT,
2310 octave_H5P_DEFAULT);
2311 #else
2312 data_hid = H5Dcreate (group_hid, "nm", type_hid, space_hid,
2313 octave_H5P_DEFAULT);
2314 #endif
2315 if (data_hid < 0
2316 || H5Dwrite (data_hid, type_hid, octave_H5S_ALL, octave_H5S_ALL,
2317 octave_H5P_DEFAULT, m_name.c_str ()) < 0)
2318 {
2319 H5Sclose (space_hid);
2320 H5Tclose (type_hid);
2321 H5Gclose (group_hid);
2322 return false;
2323 }
2324 H5Dclose (data_hid);
2325
2326 std::ostringstream buf;
2327 print_raw (buf, true, 0);
2328 std::string stmp = buf.str ();
2329
2330 // attach the type of the variable
2331 H5Tset_size (type_hid, stmp.length () + 1);
2332 if (type_hid < 0)
2333 {
2334 H5Sclose (space_hid);
2335 H5Gclose (group_hid);
2336 return false;
2337 }
2338
2339 #if defined (HAVE_HDF5_18)
2340 data_hid = H5Dcreate (group_hid, "fcn", type_hid, space_hid,
2341 octave_H5P_DEFAULT, octave_H5P_DEFAULT,
2342 octave_H5P_DEFAULT);
2343 #else
2344 data_hid = H5Dcreate (group_hid, "fcn", type_hid, space_hid,
2345 octave_H5P_DEFAULT);
2346 #endif
2347 if (data_hid < 0
2348 || H5Dwrite (data_hid, type_hid, octave_H5S_ALL, octave_H5S_ALL,
2349 octave_H5P_DEFAULT, stmp.c_str ()) < 0)
2350 {
2351 H5Sclose (space_hid);
2352 H5Tclose (type_hid);
2353 H5Gclose (group_hid);
2354 return false;
2355 }
2356
2357 H5Dclose (data_hid);
2358
2359 std::size_t varlen = m_local_vars.size ();
2360
2361 if (varlen > 0)
2362 {
2363 octave_hdf5_id as_id = H5Screate (H5S_SCALAR);
2364
2365 if (as_id >= 0)
2366 {
2367 octave_hdf5_id a_id;
2368 #if defined (HAVE_HDF5_18)
2369 a_id = H5Acreate (group_hid, "SYMBOL_TABLE", H5T_NATIVE_IDX, as_id,
2370 octave_H5P_DEFAULT, octave_H5P_DEFAULT);
2371
2372 #else
2373 a_id = H5Acreate (group_hid, "SYMBOL_TABLE", H5T_NATIVE_IDX, as_id,
2374 octave_H5P_DEFAULT);
2375 #endif
2376
2377 if (a_id >= 0)
2378 {
2379 retval = (H5Awrite (a_id, H5T_NATIVE_IDX, &varlen) >= 0);
2380
2381 H5Aclose (a_id);
2382 }
2383 else
2384 retval = false;
2385
2386 H5Sclose (as_id);
2387 }
2388 else
2389 retval = false;
2390 #if defined (HAVE_HDF5_18)
2391 data_hid = H5Gcreate (group_hid, "symbol table",
2392 octave_H5P_DEFAULT, octave_H5P_DEFAULT,
2393 octave_H5P_DEFAULT);
2394 #else
2395 data_hid = H5Gcreate (group_hid, "symbol table", 0);
2396 #endif
2397 if (data_hid < 0)
2398 {
2399 H5Sclose (space_hid);
2400 H5Tclose (type_hid);
2401 H5Gclose (group_hid);
2402 return false;
2403 }
2404
2405 for (const auto& nm_val : m_local_vars)
2406 {
2407 if (! add_hdf5_data (data_hid, nm_val.second, nm_val.first,
2408 "", false, save_as_floats))
2409 break;
2410 }
2411
2412 H5Gclose (data_hid);
2413 }
2414
2415 H5Sclose (space_hid);
2416 H5Tclose (type_hid);
2417 H5Gclose (group_hid);
2418
2419 return retval;
2420
2421 #else
2422
2423 octave_unused_parameter (loc_id);
2424 octave_unused_parameter (name);
2425 octave_unused_parameter (save_as_floats);
2426
2427 warn_save ("hdf5");
2428
2429 return false;
2430
2431 #endif
2432 }
2433
2434 bool base_anonymous_fcn_handle::load_hdf5 (octave_hdf5_id& group_hid,
2435 octave_hdf5_id& space_hid,
2436 octave_hdf5_id& type_hid)
2437 {
2438 #if defined (HAVE_HDF5)
2439
2440 bool success = true;
2441
2442 #if defined (HAVE_HDF5_18)
2443 octave_hdf5_id data_hid = H5Dopen (group_hid, "fcn", octave_H5P_DEFAULT);
2444 #else
2445 octave_hdf5_id data_hid = H5Dopen (group_hid, "fcn");
2446 #endif
2447
2448 if (data_hid < 0)
2449 {
2450 H5Sclose (space_hid);
2451 H5Tclose (type_hid);
2452 H5Gclose (group_hid);
2453 return false;
2454 }
2455
2456 H5Tclose (type_hid);
2457 type_hid = H5Dget_type (data_hid);
2458 octave_hdf5_id type_class_hid = H5Tget_class (type_hid);
2459
2460 if (type_class_hid != H5T_STRING)
2461 {
2462 H5Sclose (space_hid);
2463 H5Tclose (type_hid);
2464 H5Dclose (data_hid);
2465 H5Gclose (group_hid);
2466 return false;
2467 }
2468
2469 H5Sclose (space_hid);
2470 space_hid = H5Dget_space (data_hid);
2471 hsize_t rank = H5Sget_simple_extent_ndims (space_hid);
2472
2473 if (rank != 0)
2474 {
2475 H5Sclose (space_hid);
2476 H5Tclose (type_hid);
2477 H5Dclose (data_hid);
2478 H5Gclose (group_hid);
2479 return false;
2480 }
2481
2482 int slen = H5Tget_size (type_hid);
2483 if (slen < 0)
2484 {
2485 H5Sclose (space_hid);
2486 H5Tclose (type_hid);
2487 H5Dclose (data_hid);
2488 H5Gclose (group_hid);
2489 return false;
2490 }
2491
2492 OCTAVE_LOCAL_BUFFER (char, fcn_tmp, slen);
2493
2494 // create datatype for (null-terminated) string to read into:
2495 octave_hdf5_id st_id = H5Tcopy (H5T_C_S1);
2496 H5Tset_size (st_id, slen);
2497
2498 if (H5Dread (data_hid, st_id, octave_H5S_ALL, octave_H5S_ALL,
2499 octave_H5P_DEFAULT, fcn_tmp)
2500 < 0)
2501 {
2502 H5Tclose (st_id);
2503 H5Sclose (space_hid);
2504 H5Tclose (type_hid);
2505 H5Dclose (data_hid);
2506 H5Gclose (group_hid);
2507 return false;
2508 }
2509 H5Tclose (st_id);
2510 H5Dclose (data_hid);
2511
2512 octave_idx_type len = 0;
2513
2514 // we have to pull some shenanigans here to make sure
2515 // HDF5 doesn't print out all sorts of error messages if we
2516 // call H5Aopen for a non-existing attribute
2517
2518 H5E_auto_t err_fcn;
2519 void *err_fcn_data;
2520
2521 // turn off error reporting temporarily, but save the error
2522 // reporting function:
2523 #if defined (HAVE_HDF5_18)
2524 H5Eget_auto (octave_H5E_DEFAULT, &err_fcn, &err_fcn_data);
2525 H5Eset_auto (octave_H5E_DEFAULT, nullptr, nullptr);
2526 #else
2527 H5Eget_auto (&err_fcn, &err_fcn_data);
2528 H5Eset_auto (nullptr, nullptr);
2529 #endif
2530
2531 octave_hdf5_id attr_id = H5Aopen_name (group_hid, "SYMBOL_TABLE");
2532
2533 if (attr_id >= 0)
2534 {
2535 if (H5Aread (attr_id, H5T_NATIVE_IDX, &len) < 0)
2536 success = false;
2537
2538 H5Aclose (attr_id);
2539 }
2540
2541 // restore error reporting:
2542 #if defined (HAVE_HDF5_18)
2543 H5Eset_auto (octave_H5E_DEFAULT, err_fcn, err_fcn_data);
2544 #else
2545 H5Eset_auto (err_fcn, err_fcn_data);
2546 #endif
2547
2548 // Set up temporary scope to use for evaluating the text that
2549 // defines the anonymous function.
2550
2551 interpreter& interp = __get_interpreter__ ();
2552
2553 tree_evaluator& tw = interp.get_evaluator ();
2554
2555 tw.push_dummy_scope (fcn_tmp);
2556 unwind_action_safe restore_scope (&tree_evaluator::pop_scope, &tw);
2557
2558 if (len > 0 && success)
2559 {
2560 hsize_t num_obj = 0;
2561 #if defined (HAVE_HDF5_18)
2562 data_hid = H5Gopen (group_hid, "symbol table", octave_H5P_DEFAULT);
2563 #else
2564 data_hid = H5Gopen (group_hid, "symbol table");
2565 #endif
2566 H5Gget_num_objs (data_hid, &num_obj);
2567 H5Gclose (data_hid);
2568
2569 if (num_obj != static_cast<hsize_t> (len))
2570 error ("load: failed to load anonymous function handle");
2571
2572 hdf5_callback_data dsub;
2573 int current_item = 0;
2574 for (octave_idx_type i = 0; i < len; i++)
2575 {
2576 if (hdf5_h5g_iterate (group_hid, "symbol table", &current_item,
2577 &dsub) <= 0)
2578 error ("load: failed to load anonymous function handle");
2579
2580 m_local_vars[dsub.name] = dsub.tc;
2581 }
2582 }
2583
2584 if (success)
2585 return parse (fcn_tmp);
2586
2587 return false;
2588
2589 #else
2590
2591 octave_unused_parameter (group_hid);
2592 octave_unused_parameter (space_hid);
2593 octave_unused_parameter (type_hid);
2594
2595 return false;
2596
2597 #endif
2598 }
2599
2600 void base_anonymous_fcn_handle::print_raw (std::ostream& os, bool, int) const
2601 {
2602 tree_print_code tpc (os);
2603
2604 octave_user_function *f = m_fcn.user_function_value ();
2605
2606 if (! f)
2607 error ("invalid anonymous function handle");
2608
2609 os << "@";
2610
2611 // The parameter list should always be valid for anonymous
2612 // functions, so we should always call accept for it, and it will
2613 // print the parens for us.
2614
2615 tree_parameter_list *p = f->parameter_list ();
2616
2617 if (p)
2618 p->accept (tpc);
2619
2620 os << " ";
2621
2622 tree_statement_list *b = f->body ();
2623
2624 panic_if (b->length () != 1);
2625
2626 tree_statement *s = b->front ();
2627
2628 if (! s)
2629 error ("invalid anonymous function handle");
2630
2631 panic_unless (s->is_expression ());
2632
2633 tree_expression *e = s->expression ();
2634
2635 if (! e)
2636 error ("invalid anonymous function handle");
2637
2638 tpc.print_fcn_handle_body (e);
2639 }
2640
2641 bool base_anonymous_fcn_handle::parse (const std::string& fcn_text)
2642 {
2643 // FIXME: If evaluation of the string gives us an anonymous function
2644 // handle object, then why extract the function and create a new
2645 // anonymous function object? Why not just attach the workspace
2646 // values to the object returned by eval_string? This code is also is
2647 // duplicated in read_mat5_binary_element in ls-mat5.cc.
2648
2649 interpreter& interp = __get_interpreter__ ();
2650
2651 // Set up temporary scope to use for evaluating the text that defines
2652 // the anonymous function so that we don't pick up values of random
2653 // variables that might be in the current scope.
2654
2655 tree_evaluator& tw = interp.get_evaluator ();
2656 tw.push_dummy_scope ("read_mat5_binary_element");
2657
2658 unwind_action act ([&tw] () { tw.pop_scope (); });
2659
2660 int parse_status;
2661 octave_value anonymous_fcn_hdl
2662 = interp.eval_string (fcn_text, true, parse_status);
2663
2664 if (parse_status != 0)
2665 return false;
2666
2667 octave_fcn_handle *fh = anonymous_fcn_hdl.fcn_handle_value ();
2668
2669 if (! fh)
2670 return false;
2671
2672 m_fcn = fh->fcn_val ();
2673
2674 octave_user_function *uf = m_fcn.user_function_value (true);
2675
2676 if (uf)
2677 {
2678 symbol_scope uf_scope = uf->scope ();
2679
2680 if (uf_scope)
2681 uf_scope.cache_name (m_name);
2682 }
2683
2684 return true;
2685 }
2686
2687 anonymous_fcn_handle::anonymous_fcn_handle (const octave_value& fcn,
2688 const stack_frame::local_vars_map& local_vars,
2689 const std::shared_ptr<stack_frame>& stack_context)
2690 : base_anonymous_fcn_handle (fcn, local_vars),
2691 m_stack_context (stack_context)
2692 {
2693 if (m_stack_context)
2694 m_stack_context->mark_closure_context ();
2695 }
2696
2697 octave_value anonymous_fcn_handle::make_weak_anonymous_handle (void) const
2698 {
2699 return octave_value (new octave_fcn_handle
2700 (new weak_anonymous_fcn_handle (*this)));
2701 }
2702
2703 octave_value_list
2704 anonymous_fcn_handle::call (int nargout, const octave_value_list& args)
2705 {
2706 tree_evaluator& tw = __get_evaluator__ ();
2707
2708 octave_user_function *oct_usr_fcn = m_fcn.user_function_value ();
2709
2710 tw.push_stack_frame (oct_usr_fcn, m_local_vars, m_stack_context);
2711
2712 unwind_action act ([&tw] () { tw.pop_stack_frame (); });
2713
2714 return oct_usr_fcn->execute (tw, nargout, args);
2715 }
2716
2717 octave_value anonymous_fcn_handle::workspace (void) const
2718 {
2719 octave_scalar_map local_vars_map;
2720
2721 for (const auto& nm_val : m_local_vars)
2722 local_vars_map.assign (nm_val.first, nm_val.second);
2723
2724 // FIXME: it would be more convenient if stack_frame::workspace
2725 // returned a Cell object directly instead of a Cell in an
2726 // octave_value object.
2727
2728 Cell cell_frames;
2729
2730 if (m_stack_context)
2731 {
2732 octave_value ov_frames = m_stack_context->workspace ();
2733 cell_frames = ov_frames.cell_value ();
2734 }
2735
2736 octave_idx_type num_frames = cell_frames.numel ();
2737 // FIXME: It seems there should be a simple way to concatenate cells...
2738 Cell retval = Cell (num_frames+1, 1);
2739 retval(0) = m_local_vars;
2740 for (octave_idx_type i = 0; i < num_frames; i++)
2741 retval(i+1) = cell_frames(i);
2742
2743 return retval;
2744 }
2745
2746 bool is_equal_to (const anonymous_fcn_handle& fh1,
2747 const anonymous_fcn_handle& fh2)
2748 {
2749 if (fh1.m_fcn.is_defined () && fh2.m_fcn.is_defined ())
2750 return fh1.m_fcn.is_copy_of (fh2.m_fcn);
2751 else
2752 return false;
2753 }
2754
2755 octave_value_list
2756 weak_anonymous_fcn_handle::call (int nargout, const octave_value_list& args)
2757 {
2758 tree_evaluator& tw = __get_evaluator__ ();
2759
2760 octave_user_function *oct_usr_fcn = m_fcn.user_function_value ();
2761
2762 std::shared_ptr<stack_frame> frames = m_stack_context.lock ();
2763
2764 tw.push_stack_frame (oct_usr_fcn, m_local_vars, frames);
2765
2766 unwind_action act ([&tw] () { tw.pop_stack_frame (); });
2767
2768 return oct_usr_fcn->execute (tw, nargout, args);
2769 }
2770
2771 octave_value weak_anonymous_fcn_handle::workspace (void) const
2772 {
2773 octave_scalar_map local_vars_map;
2774
2775 for (const auto& nm_val : m_local_vars)
2776 local_vars_map.assign (nm_val.first, nm_val.second);
2777
2778 // FIXME: it would be more convenient if stack_frame::workspace
2779 // returned a Cell object directly instead of a Cell in an
2780 // octave_value object.
2781
2782 std::shared_ptr<stack_frame> frames = m_stack_context.lock ();
2783
2784 Cell cell_frames;
2785
2786 if (frames)
2787 {
2788 octave_value ov_frames = frames->workspace ();
2789 cell_frames = ov_frames.cell_value ();
2790 }
2791
2792 octave_idx_type num_frames = cell_frames.numel ();
2793
2794 // FIXME: It seems there should be a simple way to concatenate
2795 // cells...
2796 Cell retval = Cell (num_frames+1, 1);
2797 retval(0) = m_local_vars;
2798 for (octave_idx_type i = 0; i < num_frames; i++)
2799 retval(i+1) = cell_frames(i);
2800
2801 return retval;
2802 }
2803
2804 bool is_equal_to (const weak_anonymous_fcn_handle& fh1,
2805 const weak_anonymous_fcn_handle& fh2)
2806 {
2807 if (fh1.m_name == fh2.m_name
2808 && fh1.m_fcn.is_defined () && fh2.m_fcn.is_defined ())
2809 return fh1.m_fcn.is_copy_of (fh2.m_fcn);
2810 else
2811 return false;
2812 }
2813 2813
2814 OCTAVE_END_NAMESPACE(octave) 2814 OCTAVE_END_NAMESPACE(octave)
2815 2815
2816 octave_fcn_handle::octave_fcn_handle (void) 2816 octave_fcn_handle::octave_fcn_handle (void)
2817 : octave_base_value (), m_rep (new octave::invalid_fcn_handle ()) 2817 : octave_base_value (), m_rep (new octave::invalid_fcn_handle ())
2955 { 2955 {
2956 std::string name; 2956 std::string name;
2957 is >> name; 2957 is >> name;
2958 2958
2959 new_rep.reset (new octave::simple_fcn_handle (name, fpath, 2959 new_rep.reset (new octave::simple_fcn_handle (name, fpath,
2960 octaveroot)); 2960 octaveroot));
2961 } 2961 }
2962 else if (subtype == "scopedfunction") 2962 else if (subtype == "scopedfunction")
2963 { 2963 {
2964 std::string name; 2964 std::string name;
2965 is >> name; 2965 is >> name;
2966 2966
2967 new_rep.reset (new octave::scoped_fcn_handle (name, fpath, 2967 new_rep.reset (new octave::scoped_fcn_handle (name, fpath,
2968 octaveroot)); 2968 octaveroot));
2969 } 2969 }
2970 else if (subtype == "anonymous") 2970 else if (subtype == "anonymous")
2971 new_rep.reset (new octave::anonymous_fcn_handle ()); 2971 new_rep.reset (new octave::anonymous_fcn_handle ());
2972 else if (subtype == "nested") 2972 else if (subtype == "nested")
2973 { 2973 {
2974 std::string name; 2974 std::string name;
2975 is >> name; 2975 is >> name;
2976 2976
2977 new_rep.reset (new octave::nested_fcn_handle (name, fpath, 2977 new_rep.reset (new octave::nested_fcn_handle (name, fpath,
2978 octaveroot)); 2978 octaveroot));
2979 } 2979 }
2980 else if (subtype == "classsimple") 2980 else if (subtype == "classsimple")
2981 { 2981 {
2982 std::string name; 2982 std::string name;
2983 is >> name; 2983 is >> name;
2984 2984
2985 new_rep.reset (new octave::class_simple_fcn_handle (name, fpath, 2985 new_rep.reset (new octave::class_simple_fcn_handle (name, fpath,
2986 octaveroot)); 2986 octaveroot));
2987 } 2987 }
2988 } 2988 }
2989 2989
2990 if (! new_rep) 2990 if (! new_rep)
2991 return false; 2991 return false;
3082 new_rep.reset (new octave::scoped_fcn_handle (name, fpath, octaveroot)); 3082 new_rep.reset (new octave::scoped_fcn_handle (name, fpath, octaveroot));
3083 else if (subtype == "nested") 3083 else if (subtype == "nested")
3084 new_rep.reset (new octave::nested_fcn_handle (name, fpath, octaveroot)); 3084 new_rep.reset (new octave::nested_fcn_handle (name, fpath, octaveroot));
3085 else if (subtype == "classsimple") 3085 else if (subtype == "classsimple")
3086 new_rep.reset (new octave::class_simple_fcn_handle (name, fpath, 3086 new_rep.reset (new octave::class_simple_fcn_handle (name, fpath,
3087 octaveroot)); 3087 octaveroot));
3088 } 3088 }
3089 3089
3090 if (! new_rep) 3090 if (! new_rep)
3091 return false; 3091 return false;
3092 3092
3236 new_rep.reset (new octave::scoped_fcn_handle (name, fpath, octaveroot)); 3236 new_rep.reset (new octave::scoped_fcn_handle (name, fpath, octaveroot));
3237 else if (subtype == "nested") 3237 else if (subtype == "nested")
3238 new_rep.reset (new octave::nested_fcn_handle (name, fpath, octaveroot)); 3238 new_rep.reset (new octave::nested_fcn_handle (name, fpath, octaveroot));
3239 else if (subtype == "classsimple") 3239 else if (subtype == "classsimple")
3240 new_rep.reset (new octave::class_simple_fcn_handle (name, fpath, 3240 new_rep.reset (new octave::class_simple_fcn_handle (name, fpath,
3241 octaveroot)); 3241 octaveroot));
3242 } 3242 }
3243 3243
3244 bool status = false; 3244 bool status = false;
3245 3245
3246 if (new_rep && new_rep->load_hdf5 (group_hid, space_hid, type_hid)) 3246 if (new_rep && new_rep->load_hdf5 (group_hid, space_hid, type_hid))
3457 @end deftypefn */) 3457 @end deftypefn */)
3458 { 3458 {
3459 if (args.length () != 1) 3459 if (args.length () != 1)
3460 print_usage (); 3460 print_usage ();
3461 3461
3462 octave_fcn_handle *fh = args(0).xfcn_handle_value ("functions: FCN_HANDLE argument must be a function handle object"); 3462 octave_fcn_handle *fh = args(
3463 0).xfcn_handle_value ("functions: FCN_HANDLE argument must be a function handle object");
3463 3464
3464 return ovl (fh->info ()); 3465 return ovl (fh->info ());
3465 } 3466 }
3466 3467
3467 DEFUN (func2str, args, , 3468 DEFUN (func2str, args, ,
3473 @end deftypefn */) 3474 @end deftypefn */)
3474 { 3475 {
3475 if (args.length () != 1) 3476 if (args.length () != 1)
3476 print_usage (); 3477 print_usage ();
3477 3478
3478 octave_fcn_handle *fh = args(0).xfcn_handle_value ("func2str: FCN_HANDLE argument must be a function handle object"); 3479 octave_fcn_handle *fh = args(
3480 0).xfcn_handle_value ("func2str: FCN_HANDLE argument must be a function handle object");
3479 3481
3480 if (! fh) 3482 if (! fh)
3481 error ("func2str: FCN_HANDLE must be a valid function handle"); 3483 error ("func2str: FCN_HANDLE must be a valid function handle");
3482 3484
3483 octave_value retval; 3485 octave_value retval;