1 //////////////////////////////////////////////////////////////////////// 2 // 3 // Copyright (C) 2012-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 <algorithm> 31 #include <iomanip> 32 33 #include "cdef-class.h" 34 #include "cdef-manager.h" 35 #include "cdef-method.h" 36 #include "cdef-package.h" 37 #include "cdef-property.h" 38 #include "cdef-utils.h" 39 #include "errwarn.h" 40 #include "interpreter-private.h" 41 #include "interpreter.h" 42 #include "load-path.h" 43 #include "ov-builtin.h" 44 #include "ov-classdef.h" 45 #include "ov-fcn-handle.h" 46 #include "ov-usr-fcn.h" 47 #include "parse.h" 48 #include "pt-assign.h" 49 #include "pt-classdef.h" 50 #include "pt-eval.h" 51 #include "pt-idx.h" 52 #include "pt-misc.h" 53 #include "pt-stmt.h" 54 #include "pt-walk.h" 55 #include "unwind-prot.h" 56 57 // Define to 1 to enable debugging statements. 58 #define DEBUG_TRACE 0 59 #if DEBUG_TRACE 60 # include <iostream> 61 #endif 62 63 namespace octave 64 { 65 static octave_value make_fcn_handle(const octave_value & fcn,const std::string & meth_name,const std::string & class_name)66 make_fcn_handle (const octave_value& fcn, const std::string& meth_name, 67 const std::string& class_name) 68 { 69 octave_value retval; 70 71 if (fcn.is_defined ()) 72 { 73 // FCN_HANDLE: METHOD 74 octave_fcn_handle *fh 75 = new octave_fcn_handle (fcn, class_name, meth_name); 76 77 retval = octave_value (fh); 78 } 79 80 return retval; 81 } 82 cdef_class_rep(const std::list<cdef_class> & superclasses)83 cdef_class::cdef_class_rep::cdef_class_rep (const std::list<cdef_class>& superclasses) 84 : cdef_meta_object_rep (), member_count (0), handle_class (false), 85 meta (false) 86 { 87 put ("SuperClasses", to_ov (superclasses)); 88 implicit_ctor_list = superclasses; 89 } 90 91 cdef_method find_method(const std::string & nm,bool local)92 cdef_class::cdef_class_rep::find_method (const std::string& nm, bool local) 93 { 94 auto it = method_map.find (nm); 95 96 if (it == method_map.end ()) 97 { 98 // FIXME: look into class directory 99 } 100 else 101 { 102 cdef_method& meth = it->second; 103 104 // FIXME: check if method reload needed 105 106 if (meth.ok ()) 107 return meth; 108 } 109 110 if (! local) 111 { 112 // Look into superclasses 113 114 Cell super_classes = get ("SuperClasses").cell_value (); 115 116 for (int i = 0; i < super_classes.numel (); i++) 117 { 118 cdef_class cls = lookup_class (super_classes(i)); 119 120 cdef_method meth = cls.find_method (nm); 121 122 if (meth.ok ()) 123 return meth; 124 } 125 } 126 127 return cdef_method (); 128 } 129 130 class ctor_analyzer : public tree_walker 131 { 132 public: 133 134 ctor_analyzer (void) = delete; 135 ctor_analyzer(const std::string & ctor,const std::string & obj)136 ctor_analyzer (const std::string& ctor, const std::string& obj) 137 : tree_walker (), who (ctor), obj_name (obj) { } 138 139 ctor_analyzer (const ctor_analyzer&) = delete; 140 141 ctor_analyzer& operator = (const ctor_analyzer&) = delete; 142 143 ~ctor_analyzer (void) = default; 144 visit_statement(tree_statement & t)145 void visit_statement (tree_statement& t) 146 { 147 if (t.is_expression ()) 148 t.expression ()->accept (*this); 149 } 150 visit_simple_assignment(tree_simple_assignment & t)151 void visit_simple_assignment (tree_simple_assignment& t) 152 { 153 t.right_hand_side ()->accept (*this); 154 } 155 visit_multi_assignment(tree_multi_assignment & t)156 void visit_multi_assignment (tree_multi_assignment& t) 157 { 158 t.right_hand_side ()->accept (*this); 159 } 160 visit_index_expression(tree_index_expression & t)161 void visit_index_expression (tree_index_expression& t) 162 { 163 t.expression ()->accept (*this); 164 } 165 get_constructor_list(void) const166 std::list<cdef_class> get_constructor_list (void) const 167 { return ctor_list; } 168 169 // NO-OP 170 visit_anon_fcn_handle(tree_anon_fcn_handle &)171 void visit_anon_fcn_handle (tree_anon_fcn_handle&) { } visit_argument_list(tree_argument_list &)172 void visit_argument_list (tree_argument_list&) { } visit_binary_expression(tree_binary_expression &)173 void visit_binary_expression (tree_binary_expression&) { } visit_break_command(tree_break_command &)174 void visit_break_command (tree_break_command&) { } visit_colon_expression(tree_colon_expression &)175 void visit_colon_expression (tree_colon_expression&) { } visit_continue_command(tree_continue_command &)176 void visit_continue_command (tree_continue_command&) { } visit_decl_command(tree_decl_command &)177 void visit_decl_command (tree_decl_command&) { } visit_decl_init_list(tree_decl_init_list &)178 void visit_decl_init_list (tree_decl_init_list&) { } visit_decl_elt(tree_decl_elt &)179 void visit_decl_elt (tree_decl_elt&) { } visit_simple_for_command(tree_simple_for_command &)180 void visit_simple_for_command (tree_simple_for_command&) { } visit_complex_for_command(tree_complex_for_command &)181 void visit_complex_for_command (tree_complex_for_command&) { } visit_octave_user_script(octave_user_script &)182 void visit_octave_user_script (octave_user_script&) { } visit_octave_user_function(octave_user_function &)183 void visit_octave_user_function (octave_user_function&) { } visit_function_def(tree_function_def &)184 void visit_function_def (tree_function_def&) { } visit_identifier(tree_identifier &)185 void visit_identifier (tree_identifier&) { } visit_if_clause(tree_if_clause &)186 void visit_if_clause (tree_if_clause&) { } visit_if_command(tree_if_command &)187 void visit_if_command (tree_if_command&) { } visit_if_command_list(tree_if_command_list &)188 void visit_if_command_list (tree_if_command_list&) { } visit_switch_case(tree_switch_case &)189 void visit_switch_case (tree_switch_case&) { } visit_switch_case_list(tree_switch_case_list &)190 void visit_switch_case_list (tree_switch_case_list&) { } visit_switch_command(tree_switch_command &)191 void visit_switch_command (tree_switch_command&) { } visit_matrix(tree_matrix &)192 void visit_matrix (tree_matrix&) { } visit_cell(tree_cell &)193 void visit_cell (tree_cell&) { } visit_no_op_command(tree_no_op_command &)194 void visit_no_op_command (tree_no_op_command&) { } visit_constant(tree_constant &)195 void visit_constant (tree_constant&) { } visit_fcn_handle(tree_fcn_handle &)196 void visit_fcn_handle (tree_fcn_handle&) { } visit_parameter_list(tree_parameter_list &)197 void visit_parameter_list (tree_parameter_list&) { } visit_postfix_expression(tree_postfix_expression &)198 void visit_postfix_expression (tree_postfix_expression&) { } visit_prefix_expression(tree_prefix_expression &)199 void visit_prefix_expression (tree_prefix_expression&) { } visit_return_command(tree_return_command &)200 void visit_return_command (tree_return_command&) { } visit_try_catch_command(tree_try_catch_command &)201 void visit_try_catch_command (tree_try_catch_command&) { } visit_unwind_protect_command(tree_unwind_protect_command &)202 void visit_unwind_protect_command (tree_unwind_protect_command&) { } visit_while_command(tree_while_command &)203 void visit_while_command (tree_while_command&) { } visit_do_until_command(tree_do_until_command &)204 void visit_do_until_command (tree_do_until_command&) { } 205 visit_superclass_ref(tree_superclass_ref & t)206 void visit_superclass_ref (tree_superclass_ref& t) 207 { 208 if (t.method_name () == obj_name) 209 { 210 std::string class_name = t.class_name (); 211 212 cdef_class cls = lookup_class (class_name, false); 213 214 if (cls.ok ()) 215 ctor_list.push_back (cls); 216 } 217 } 218 219 private: 220 221 // The name of the constructor being analyzed. 222 std::string who; 223 224 // The name of the first output argument of the constructor. 225 std::string obj_name; 226 227 // The list of superclass constructors that are explicitly called. 228 std::list<cdef_class> ctor_list; 229 }; 230 231 void install_method(const cdef_method & meth)232 cdef_class::cdef_class_rep::install_method (const cdef_method& meth) 233 { 234 method_map[meth.get_name ()] = meth; 235 236 member_count++; 237 238 if (meth.is_constructor ()) 239 { 240 // Analyze the constructor code to determine what superclass 241 // constructors are called explicitly. 242 243 octave_value ov_fcn = meth.get_function (); 244 245 if (ov_fcn.is_defined ()) 246 { 247 octave_user_function *uf = ov_fcn.user_function_value (true); 248 249 if (uf) 250 { 251 tree_parameter_list *ret_list = uf->return_list (); 252 tree_statement_list *body = uf->body (); 253 254 if (! ret_list || ret_list->size () != 1) 255 error ("%s: invalid constructor output arguments", 256 meth.get_name ().c_str ()); 257 258 std::string obj_name = ret_list->front ()->name (); 259 ctor_analyzer a (meth.get_name (), obj_name); 260 261 body->accept (a); 262 263 std::list<cdef_class> explicit_ctor_list 264 = a.get_constructor_list (); 265 266 for (const auto& cdef_cls : explicit_ctor_list) 267 { 268 #if DEBUG_TRACE 269 std::cerr << "explicit superclass constructor: " 270 << cdef_cls.get_name () << std::endl; 271 #endif 272 273 implicit_ctor_list.remove (cdef_cls); 274 } 275 } 276 } 277 } 278 } 279 280 void load_all_methods(void)281 cdef_class::cdef_class_rep::load_all_methods (void) 282 { 283 // FIXME: re-scan class directory 284 } 285 286 Cell get_methods(bool include_ctor)287 cdef_class::cdef_class_rep::get_methods (bool include_ctor) 288 { 289 std::map<std::string,cdef_method> meths; 290 291 find_methods (meths, false, include_ctor); 292 293 Cell c (meths.size (), 1); 294 295 int idx = 0; 296 297 for (const auto& nm_mthd : meths) 298 c(idx++, 0) = to_ov (nm_mthd.second); 299 300 return c; 301 } 302 303 std::map<std::string, cdef_method> get_method_map(bool only_inherited,bool include_ctor)304 cdef_class::cdef_class_rep::get_method_map (bool only_inherited, 305 bool include_ctor) 306 { 307 std::map<std::string, cdef_method> methods; 308 309 find_methods (methods, only_inherited, include_ctor); 310 311 return methods; 312 } 313 314 void find_methods(std::map<std::string,cdef_method> & meths,bool only_inherited,bool include_ctor)315 cdef_class::cdef_class_rep::find_methods (std::map<std::string, cdef_method>& meths, 316 bool only_inherited, 317 bool include_ctor) 318 { 319 load_all_methods (); 320 321 method_const_iterator it; 322 323 for (it = method_map.begin (); it != method_map.end (); ++it) 324 { 325 if (include_ctor || ! it->second.is_constructor ()) 326 { 327 std::string nm = it->second.get_name (); 328 329 if (meths.find (nm) == meths.end ()) 330 { 331 if (only_inherited) 332 { 333 octave_value acc = it->second.get ("Access"); 334 335 if (! acc.is_string () 336 || acc.string_value () == "private") 337 continue; 338 } 339 340 meths[nm] = it->second; 341 } 342 } 343 } 344 345 // Look into superclasses 346 347 Cell super_classes = get ("SuperClasses").cell_value (); 348 349 for (int i = 0; i < super_classes.numel (); i++) 350 { 351 cdef_class cls = lookup_class (super_classes(i)); 352 353 cls.get_rep ()->find_methods (meths, true, false); 354 } 355 } 356 357 cdef_property find_property(const std::string & nm)358 cdef_class::cdef_class_rep::find_property (const std::string& nm) 359 { 360 auto it = property_map.find (nm); 361 362 if (it != property_map.end ()) 363 { 364 cdef_property& prop = it->second; 365 366 if (prop.ok ()) 367 return prop; 368 } 369 370 // Look into superclasses 371 372 Cell super_classes = get ("SuperClasses").cell_value (); 373 374 for (int i = 0; i < super_classes.numel (); i++) 375 { 376 cdef_class cls = lookup_class (super_classes(i)); 377 378 cdef_property prop = cls.find_property (nm); 379 380 if (prop.ok ()) 381 return prop; 382 } 383 384 return cdef_property (); 385 } 386 387 void install_property(const cdef_property & prop)388 cdef_class::cdef_class_rep::install_property (const cdef_property& prop) 389 { 390 property_map[prop.get_name ()] = prop; 391 392 member_count++; 393 } 394 395 Cell get_properties(int mode)396 cdef_class::cdef_class_rep::get_properties (int mode) 397 { 398 std::map<std::string,cdef_property> props; 399 400 props = get_property_map (mode); 401 402 Cell c (props.size (), 1); 403 404 int idx = 0; 405 406 for (const auto& pname_prop : props) 407 c(idx++, 0) = to_ov (pname_prop.second); 408 409 return c; 410 } 411 412 std::map<std::string, cdef_property> get_property_map(int mode)413 cdef_class::cdef_class_rep::get_property_map (int mode) 414 { 415 std::map<std::string,cdef_property> props; 416 417 find_properties (props, mode); 418 419 return props; 420 } 421 422 void find_properties(std::map<std::string,cdef_property> & props,int mode)423 cdef_class::cdef_class_rep::find_properties (std::map<std::string, 424 cdef_property>& props, 425 int mode) 426 { 427 property_const_iterator it; 428 429 for (it = property_map.begin (); it != property_map.end (); ++it) 430 { 431 std::string nm = it->second.get_name (); 432 433 if (props.find (nm) == props.end ()) 434 { 435 if (mode == property_inherited) 436 { 437 octave_value acc = it->second.get ("GetAccess"); 438 439 if (! acc.is_string () 440 || acc.string_value () == "private") 441 continue; 442 } 443 444 props[nm] = it->second; 445 } 446 } 447 448 // Look into superclasses 449 450 Cell super_classes = get ("SuperClasses").cell_value (); 451 452 for (int i = 0; i < super_classes.numel (); i++) 453 { 454 cdef_class cls = lookup_class (super_classes(i)); 455 456 cls.get_rep ()->find_properties (props, 457 (mode == property_all 458 ? property_all 459 : property_inherited)); 460 } 461 } 462 463 void find_names(std::set<std::string> & names,bool all)464 cdef_class::cdef_class_rep::find_names (std::set<std::string>& names, 465 bool all) 466 { 467 load_all_methods (); 468 469 for (const auto& cls_fnmap : method_map) 470 { 471 if (! cls_fnmap.second.is_constructor ()) 472 { 473 std::string nm = cls_fnmap.second.get_name (); 474 475 if (! all) 476 { 477 octave_value acc = cls_fnmap.second.get ("Access"); 478 479 if (! acc.is_string() 480 || acc.string_value () != "public") 481 continue; 482 } 483 484 names.insert (nm); 485 } 486 } 487 488 for (const auto& pname_prop : property_map) 489 { 490 std::string nm = pname_prop.second.get_name (); 491 492 if (! all) 493 { 494 octave_value acc = pname_prop.second.get ("GetAccess"); 495 496 if (! acc.is_string() 497 || acc.string_value () != "public") 498 continue; 499 } 500 501 names.insert (nm); 502 } 503 504 // Look into superclasses 505 506 Cell super_classes = get ("SuperClasses").cell_value (); 507 508 for (int i = 0; i < super_classes.numel (); i++) 509 { 510 cdef_class cls = lookup_class (super_classes(i)); 511 512 cls.get_rep ()->find_names (names, all); 513 } 514 } 515 516 string_vector get_names(void)517 cdef_class::cdef_class_rep::get_names (void) 518 { 519 std::set<std::string> names; 520 521 find_names (names, false); 522 523 string_vector v (names); 524 525 return v.sort (true); 526 } 527 528 void delete_object(const cdef_object & obj)529 cdef_class::cdef_class_rep::delete_object (const cdef_object& obj) 530 { 531 cdef_method dtor = find_method ("delete"); 532 533 // FIXME: would it be better to tell find_method above to not find 534 // overloaded functions? 535 536 if (dtor.ok () && dtor.is_defined_in_class (get_name ())) 537 dtor.execute (obj, octave_value_list (), 0, true, "destructor"); 538 539 // FIXME: should we destroy corresponding properties here? 540 541 // Call "delete" in super classes 542 543 Cell super_classes = get ("SuperClasses").cell_value (); 544 545 for (int i = 0; i < super_classes.numel (); i++) 546 { 547 cdef_class cls = lookup_class (super_classes(i)); 548 549 if (cls.get_name () != "handle") 550 cls.delete_object (obj); 551 } 552 } 553 554 octave_value_list meta_subsref(const std::string & type,const std::list<octave_value_list> & idx,int nargout)555 cdef_class::cdef_class_rep::meta_subsref (const std::string& type, 556 const std::list<octave_value_list>& idx, 557 int nargout) 558 { 559 std::size_t skip = 1; 560 561 octave_value_list retval; 562 563 switch (type[0]) 564 { 565 case '(': 566 // Constructor call 567 568 #if DEBUG_TRACE 569 std::cerr << "constructor" << std::endl; 570 #endif 571 572 retval(0) = construct (idx.front ()); 573 break; 574 575 case '.': 576 { 577 // Static method, constant (or property?) 578 579 #if DEBUG_TRACE 580 std::cerr << "static method/property" << std::endl; 581 #endif 582 583 if (idx.front ().length () != 1) 584 error ("invalid meta.class indexing"); 585 586 std::string nm = idx.front ()(0).xstring_value ("invalid meta.class indexing, expected a method or property name"); 587 588 cdef_method meth = find_method (nm); 589 590 if (meth.ok ()) 591 { 592 if (! meth.is_static ()) 593 error ("method '%s' is not static", nm.c_str ()); 594 595 octave_value_list args; 596 597 if (type.length () > 1 && idx.size () > 1 && type[1] == '(') 598 { 599 args = *(++(idx.begin ())); 600 skip++; 601 } 602 603 retval = meth.execute (args, (type.length () > skip 604 ? 1 : nargout), true, 605 "meta.class"); 606 } 607 else 608 { 609 cdef_property prop = find_property (nm); 610 611 if (! prop.ok ()) 612 error ("no such method or property '%s'", nm.c_str ()); 613 614 if (! prop.is_constant ()) 615 error ("property '%s' is not constant", nm.c_str ()); 616 617 retval(0) = prop.get_value (true, "meta.class"); 618 } 619 } 620 break; 621 622 default: 623 error ("invalid meta.class indexing"); 624 break; 625 } 626 627 if (type.length () > skip && idx.size () > skip && ! retval.empty ()) 628 retval = retval(0).next_subsref (nargout, type, idx, skip); 629 630 return retval; 631 } 632 633 void meta_release(void)634 cdef_class::cdef_class_rep::meta_release (void) 635 { 636 cdef_manager& cdm 637 = __get_cdef_manager__ ("cdef_class::cdef_class_rep::meta_release"); 638 639 cdm.unregister_class (wrap ()); 640 } 641 642 void initialize_object(cdef_object & obj)643 cdef_class::cdef_class_rep::initialize_object (cdef_object& obj) 644 { 645 // Populate the object with default property values 646 647 std::list<cdef_class> super_classes 648 = lookup_classes (get ("SuperClasses").cell_value ()); 649 650 for (auto& cls : super_classes) 651 cls.initialize_object (obj); 652 653 for (const auto& pname_prop : property_map) 654 { 655 if (! pname_prop.second.get ("Dependent").bool_value ()) 656 { 657 octave_value pvalue = pname_prop.second.get ("DefaultValue"); 658 659 if (pvalue.is_defined ()) 660 obj.put (pname_prop.first, pvalue); 661 else 662 obj.put (pname_prop.first, octave_value (Matrix ())); 663 } 664 } 665 666 m_count++; 667 obj.mark_for_construction (cdef_class (this)); 668 } 669 670 void run_constructor(cdef_object & obj,const octave_value_list & args)671 cdef_class::cdef_class_rep::run_constructor (cdef_object& obj, 672 const octave_value_list& args) 673 { 674 octave_value_list empty_args; 675 676 for (const auto& cls : implicit_ctor_list) 677 { 678 cdef_class supcls = lookup_class (cls); 679 680 supcls.run_constructor (obj, empty_args); 681 } 682 683 std::string cls_name = get_name (); 684 std::string ctor_name = get_base_name (cls_name); 685 686 cdef_method ctor = find_method (ctor_name); 687 688 if (ctor.ok ()) 689 { 690 octave_value_list ctor_args (args); 691 octave_value_list ctor_retval; 692 693 ctor_args.prepend (to_ov (obj)); 694 ctor_retval = ctor.execute (ctor_args, 1, true, "constructor"); 695 696 if (ctor_retval.length () != 1) 697 error ("%s: invalid number of output arguments for classdef constructor", 698 ctor_name.c_str ()); 699 700 obj = to_cdef (ctor_retval(0)); 701 } 702 703 obj.mark_as_constructed (wrap ()); 704 } 705 706 octave_value get_method(const std::string & name) const707 cdef_class::cdef_class_rep::get_method (const std::string& name) const 708 { 709 auto p = method_map.find (name); 710 711 if (p == method_map.end ()) 712 return octave_value (); 713 714 return p->second.get_function (); 715 } 716 717 718 octave_value construct(const octave_value_list & args)719 cdef_class::cdef_class_rep::construct (const octave_value_list& args) 720 { 721 cdef_object obj = construct_object (args); 722 723 if (obj.ok ()) 724 return to_ov (obj); 725 726 return octave_value (); 727 } 728 729 cdef_object construct_object(const octave_value_list & args)730 cdef_class::cdef_class_rep::construct_object (const octave_value_list& args) 731 { 732 if (is_abstract ()) 733 error ("cannot instantiate object for abstract class '%s'", 734 get_name ().c_str ()); 735 736 cdef_object obj; 737 738 if (is_meta_class ()) 739 { 740 // This code path is only used to create empty meta objects 741 // as filler for the empty values within a meta object array. 742 743 cdef_class this_cls = wrap (); 744 745 static cdef_object empty_class; 746 747 cdef_manager& cdm 748 = __get_cdef_manager__ ("cdef_class::cdef_class_rep::construct_object"); 749 750 if (this_cls == cdm.meta_class ()) 751 { 752 if (! empty_class.ok ()) 753 empty_class = cdm.make_class ("", std::list<cdef_class> ()); 754 obj = empty_class; 755 } 756 else if (this_cls == cdm.meta_property ()) 757 { 758 static cdef_property empty_property; 759 760 if (! empty_class.ok ()) 761 empty_class = cdm.make_class ("", std::list<cdef_class> ()); 762 if (! empty_property.ok ()) 763 empty_property = cdm.make_property (empty_class, ""); 764 obj = empty_property; 765 } 766 else if (this_cls == cdm.meta_method ()) 767 { 768 static cdef_method empty_method; 769 770 if (! empty_class.ok ()) 771 empty_class = cdm.make_class ("", std::list<cdef_class> ()); 772 if (! empty_method.ok ()) 773 empty_method = cdm.make_method (empty_class, "", octave_value ()); 774 obj = empty_method; 775 } 776 else if (this_cls == cdm.meta_package ()) 777 { 778 static cdef_package empty_package; 779 780 if (! empty_package.ok ()) 781 empty_package = cdm.make_package (""); 782 obj = empty_package; 783 } 784 else 785 panic_impossible (); 786 787 return obj; 788 } 789 else 790 { 791 if (is_handle_class ()) 792 obj = cdef_object (new handle_cdef_object ()); 793 else 794 obj = cdef_object (new value_cdef_object ()); 795 obj.set_class (wrap ()); 796 797 initialize_object (obj); 798 799 run_constructor (obj, args); 800 801 return obj; 802 } 803 804 return cdef_object (); 805 } 806 807 static octave_value compute_attribute_value(tree_evaluator & tw,tree_classdef_attribute * t)808 compute_attribute_value (tree_evaluator& tw, 809 tree_classdef_attribute *t) 810 { 811 tree_expression *expr = t->expression (); 812 813 if (expr) 814 { 815 if (expr->is_identifier ()) 816 { 817 std::string s = expr->name (); 818 819 if (s == "public") 820 return std::string ("public"); 821 else if (s == "protected") 822 return std::string ("protected"); 823 else if (s == "private") 824 return std::string ("private"); 825 } 826 827 return expr->evaluate (tw); 828 } 829 else 830 return octave_value (true); 831 } 832 833 template <typename T> 834 static std::string attribute_value_to_string(T * t,octave_value v)835 attribute_value_to_string (T *t, octave_value v) 836 { 837 if (v.is_string ()) 838 return v.string_value (); 839 else if (t->expression ()) 840 return t->expression ()->original_text (); 841 else 842 return "true"; 843 } 844 845 cdef_class make_meta_class(interpreter & interp,tree_classdef * t,bool is_at_folder)846 cdef_class::make_meta_class (interpreter& interp, 847 tree_classdef *t, bool is_at_folder) 848 { 849 cdef_class retval; 850 std::string class_name, full_class_name; 851 852 // Class creation 853 854 class_name = full_class_name = t->ident ()->name (); 855 if (! t->package_name ().empty ()) 856 full_class_name = t->package_name () + '.' + full_class_name; 857 858 #if DEBUG_TRACE 859 std::cerr << "class: " << full_class_name << std::endl; 860 #endif 861 862 // Push a dummy scope frame on the call stack that corresponds to 863 // the scope that was used when parsing classdef object. Without 864 // this, we may pick up stray values from the current scope when 865 // evaluating expressions found in things like attribute lists. 866 867 unwind_protect frame; 868 869 tree_evaluator& tw = interp.get_evaluator (); 870 871 tw.push_dummy_scope (full_class_name); 872 frame.add_method (tw, &octave::tree_evaluator::pop_scope); 873 874 std::list<cdef_class> slist; 875 876 if (t->superclass_list ()) 877 { 878 for (auto& scls : (*t->superclass_list ())) 879 { 880 std::string sclass_name = (scls)->class_name (); 881 882 #if DEBUG_TRACE 883 std::cerr << "superclass: " << sclass_name << std::endl; 884 #endif 885 886 cdef_class sclass = lookup_class (sclass_name); 887 888 if (sclass.get ("Sealed").bool_value ()) 889 error ("'%s' cannot inherit from '%s', because it is sealed", 890 full_class_name.c_str (), sclass_name.c_str ()); 891 892 slist.push_back (sclass); 893 } 894 } 895 896 cdef_manager& cdm = __get_cdef_manager__ ("cdef_class::make_meta_class"); 897 898 retval = cdm.make_class (full_class_name, slist); 899 900 retval.doc_string (t->doc_string ()); 901 902 // Package owning this class 903 904 if (! t->package_name ().empty ()) 905 { 906 cdef_package pack = cdm.find_package (t->package_name ()); 907 908 if (pack.ok ()) 909 retval.put ("ContainingPackage", to_ov (pack)); 910 } 911 912 // Class attributes 913 914 if (t->attribute_list ()) 915 { 916 for (const auto& attr : (*t->attribute_list ())) 917 { 918 std::string aname = attr->ident ()->name (); 919 octave_value avalue = compute_attribute_value (tw, attr); 920 921 #if DEBUG_TRACE 922 std::cerr << "class attribute: " << aname << " = " 923 << attribute_value_to_string (attr, avalue) << std::endl; 924 #endif 925 926 retval.put (aname, avalue); 927 } 928 } 929 930 tree_classdef_body *b = t->body (); 931 932 if (b) 933 { 934 // Keep track of the get/set accessor methods. They will be used 935 // later on when creating properties. 936 937 std::map<std::string, octave_value> get_methods; 938 std::map<std::string, octave_value> set_methods; 939 940 // Method blocks 941 942 std::list<tree_classdef_methods_block *> mb_list = b->methods_list (); 943 944 load_path& lp = interp.get_load_path (); 945 946 for (auto& mb_p : mb_list) 947 { 948 std::map<std::string, octave_value> amap; 949 950 #if DEBUG_TRACE 951 std::cerr << "method block" << std::endl; 952 #endif 953 954 // Method attributes 955 956 if (mb_p->attribute_list ()) 957 { 958 for (auto& attr_p : *mb_p->attribute_list ()) 959 { 960 std::string aname = attr_p->ident ()->name (); 961 octave_value avalue = compute_attribute_value (tw, attr_p); 962 963 #if DEBUG_TRACE 964 std::cerr << "method attribute: " << aname << " = " 965 << attribute_value_to_string (attr_p, avalue) 966 << std::endl; 967 #endif 968 969 amap[aname] = avalue; 970 } 971 } 972 973 // Methods 974 975 if (mb_p->element_list ()) 976 { 977 for (auto& mtd : *mb_p->element_list ()) 978 { 979 std::string mname = mtd.function_value ()->name (); 980 std::string mprefix = mname.substr (0, 4); 981 982 if (mprefix == "get.") 983 get_methods[mname.substr (4)] 984 = make_fcn_handle (mtd, mname, full_class_name); 985 else if (mprefix == "set.") 986 set_methods[mname.substr (4)] 987 = make_fcn_handle (mtd, mname, full_class_name); 988 else 989 { 990 cdef_method meth = cdm.make_method (retval, mname, mtd); 991 992 #if DEBUG_TRACE 993 std::cerr << (mname == class_name ? "constructor" 994 : "method") 995 << ": " << mname << std::endl; 996 #endif 997 998 for (auto& attrnm_val : amap) 999 meth.put (attrnm_val.first, attrnm_val.second); 1000 1001 retval.install_method (meth); 1002 } 1003 } 1004 } 1005 } 1006 1007 if (is_at_folder) 1008 { 1009 // Look for all external methods visible on octave path at the 1010 // time of loading of the class. 1011 // 1012 // FIXME: This is an "extension" to Matlab behavior, which only looks 1013 // in the @-folder containing the original classdef file. However, 1014 // this is easier to implement it that way at the moment. 1015 1016 std::list<std::string> external_methods 1017 = lp.methods (full_class_name); 1018 1019 for (const auto& mtdnm : external_methods) 1020 { 1021 // FIXME: should we issue a warning if the method is already 1022 // defined in the classdef file? 1023 1024 if (mtdnm != class_name 1025 && ! retval.find_method (mtdnm, true).ok ()) 1026 { 1027 // Create a dummy method that is used until the actual 1028 // method is loaded. 1029 octave_user_function *fcn = new octave_user_function (); 1030 1031 fcn->stash_function_name (mtdnm); 1032 1033 cdef_method meth 1034 = cdm.make_method (retval, mtdnm, octave_value (fcn)); 1035 1036 retval.install_method (meth); 1037 } 1038 } 1039 } 1040 1041 // Property blocks 1042 1043 // FIXME: default property expression should be able to call static 1044 // methods of the class being constructed. A restricted CLASSNAME 1045 // symbol should be added to the scope before evaluating default 1046 // value expressions. 1047 1048 std::list<tree_classdef_properties_block *> pb_list 1049 = b->properties_list (); 1050 1051 for (auto& pb_p : pb_list) 1052 { 1053 std::map<std::string, octave_value> amap; 1054 1055 #if DEBUG_TRACE 1056 std::cerr << "property block" << std::endl; 1057 #endif 1058 1059 // Property attributes 1060 1061 if (pb_p->attribute_list ()) 1062 { 1063 for (auto& attr_p : *pb_p->attribute_list ()) 1064 { 1065 std::string aname = attr_p->ident ()->name (); 1066 octave_value avalue = compute_attribute_value (tw, attr_p); 1067 1068 #if DEBUG_TRACE 1069 std::cerr << "property attribute: " << aname << " = " 1070 << attribute_value_to_string (attr_p, avalue) 1071 << std::endl; 1072 #endif 1073 1074 if (aname == "Access") 1075 { 1076 amap["GetAccess"] = avalue; 1077 amap["SetAccess"] = avalue; 1078 } 1079 else 1080 amap[aname] = avalue; 1081 } 1082 } 1083 1084 // Properties 1085 1086 if (pb_p->element_list ()) 1087 { 1088 for (auto& prop_p : *pb_p->element_list ()) 1089 { 1090 std::string prop_name = prop_p->ident ()->name (); 1091 1092 cdef_property prop = cdm.make_property (retval, prop_name); 1093 1094 #if DEBUG_TRACE 1095 std::cerr << "property: " << prop_p->ident ()->name () 1096 << std::endl; 1097 #endif 1098 1099 tree_expression *expr = prop_p->expression (); 1100 if (expr) 1101 { 1102 octave_value pvalue = expr->evaluate (tw); 1103 1104 #if DEBUG_TRACE 1105 std::cerr << "property default: " 1106 << attribute_value_to_string (prop_p, pvalue) 1107 << std::endl; 1108 #endif 1109 1110 prop.put ("DefaultValue", pvalue); 1111 } 1112 1113 // Install property attributes. This is done before assigning 1114 // the property accessors so we can do validation by using 1115 // cdef_property methods. 1116 1117 for (auto& attrnm_val : amap) 1118 prop.put (attrnm_val.first, attrnm_val.second); 1119 1120 // Install property access methods, if any. Remove the 1121 // accessor methods from the temporary storage map, so we can 1122 // detect which ones are invalid and do not correspond to a 1123 // defined property. 1124 1125 auto git = get_methods.find (prop_name); 1126 1127 if (git != get_methods.end ()) 1128 { 1129 make_function_of_class (retval, git->second); 1130 prop.put ("GetMethod", git->second); 1131 get_methods.erase (git); 1132 } 1133 1134 auto sit = set_methods.find (prop_name); 1135 1136 if (sit != set_methods.end ()) 1137 { 1138 make_function_of_class (retval, sit->second); 1139 prop.put ("SetMethod", sit->second); 1140 set_methods.erase (sit); 1141 } 1142 1143 retval.install_property (prop); 1144 } 1145 } 1146 } 1147 } 1148 1149 return retval; 1150 } 1151 1152 octave_value get_method_function(const std::string &)1153 cdef_class::get_method_function (const std::string& /* nm */) 1154 { 1155 return octave_value (new octave_classdef_meta (*this)); 1156 } 1157 } 1158