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