Mercurial > hg > octave-lyh
comparison src/ov-class.cc @ 9522:e79470be3ecb
implement subsasgn this-arg optimization
author | Jaroslav Hajek <highegg@gmail.com> |
---|---|
date | Thu, 13 Aug 2009 15:51:57 +0200 |
parents | e08d72bb988e |
children | 8e5009334661 |
comparison
equal
deleted
inserted
replaced
9521:e08d72bb988e | 9522:e79470be3ecb |
---|---|
1 /* | 1 /* |
2 | 2 |
3 Copyright (C) 2007, 2008, 2009 John W. Eaton | 3 Copyright (C) 2007, 2008, 2009 John W. Eaton |
4 Copyright (C) 2009 VZLU Prague | |
4 | 5 |
5 This file is part of Octave. | 6 This file is part of Octave. |
6 | 7 |
7 Octave is free software; you can redistribute it and/or modify it | 8 Octave is free software; you can redistribute it and/or modify it |
8 under the terms of the GNU General Public License as published by the | 9 under the terms of the GNU General Public License as published by the |
42 #include "ls-oct-binary.h" | 43 #include "ls-oct-binary.h" |
43 #include "ls-utils.h" | 44 #include "ls-utils.h" |
44 #include "oct-lvalue.h" | 45 #include "oct-lvalue.h" |
45 #include "ov-class.h" | 46 #include "ov-class.h" |
46 #include "ov-fcn.h" | 47 #include "ov-fcn.h" |
48 #include "ov-usr-fcn.h" | |
47 #include "pager.h" | 49 #include "pager.h" |
48 #include "parse.h" | 50 #include "parse.h" |
49 #include "pr-output.h" | 51 #include "pr-output.h" |
50 #include "toplev.h" | 52 #include "toplev.h" |
51 #include "unwind-prot.h" | 53 #include "unwind-prot.h" |
64 (octave_class::t_name, "<unknown>", octave_value (new octave_class ())); | 66 (octave_class::t_name, "<unknown>", octave_value (new octave_class ())); |
65 } | 67 } |
66 | 68 |
67 octave_class::octave_class (const Octave_map& m, const std::string& id, | 69 octave_class::octave_class (const Octave_map& m, const std::string& id, |
68 const octave_value_list& parents) | 70 const octave_value_list& parents) |
69 : octave_base_value (), map (m), c_name (id) | 71 : octave_struct (m), c_name (id), obsolete_copies (0) |
70 { | 72 { |
71 octave_idx_type n = parents.length (); | 73 octave_idx_type n = parents.length (); |
72 | 74 |
73 for (octave_idx_type idx = 0; idx < n; idx++) | 75 for (octave_idx_type idx = 0; idx < n; idx++) |
74 { | 76 { |
93 | 95 |
94 if (! error_state) | 96 if (! error_state) |
95 load_path::add_to_parent_map (id, parent_list); | 97 load_path::add_to_parent_map (id, parent_list); |
96 } | 98 } |
97 | 99 |
100 octave_base_value * | |
101 octave_class::unique_clone (void) | |
102 { | |
103 if (count == obsolete_copies) | |
104 { | |
105 // All remaining copies are obsolete. We don't actually need to clone. | |
106 count++; | |
107 return this; | |
108 } | |
109 else | |
110 { | |
111 // In theory, this shouldn't be happening, but it's here just in case. | |
112 if (count < obsolete_copies) | |
113 obsolete_copies = 0; | |
114 | |
115 return clone (); | |
116 } | |
117 } | |
118 | |
98 static std::string | 119 static std::string |
99 get_current_method_class (void) | 120 get_current_method_class (void) |
100 { | 121 { |
101 std::string retval; | 122 std::string retval; |
102 | 123 |
113 | 134 |
114 static void | 135 static void |
115 gripe_invalid_index (void) | 136 gripe_invalid_index (void) |
116 { | 137 { |
117 error ("invalid index for class"); | 138 error ("invalid index for class"); |
118 } | |
119 | |
120 static void | |
121 gripe_invalid_index_for_assignment (void) | |
122 { | |
123 error ("invalid index for class assignment"); | |
124 } | |
125 | |
126 static void | |
127 gripe_invalid_index_type (const std::string& nm, char t) | |
128 { | |
129 error ("%s cannot be indexed with %c", nm.c_str (), t); | |
130 } | |
131 | |
132 static void | |
133 gripe_failed_assignment (void) | |
134 { | |
135 error ("assignment to class element failed"); | |
136 } | 139 } |
137 | 140 |
138 static inline octave_value_list | 141 static inline octave_value_list |
139 sanitize (const octave_value_list& ovl) | 142 sanitize (const octave_value_list& ovl) |
140 { | 143 { |
366 int nargout) | 369 int nargout) |
367 { | 370 { |
368 octave_value_list retval; | 371 octave_value_list retval; |
369 | 372 |
370 if (in_class_method () || called_from_builtin ()) | 373 if (in_class_method () || called_from_builtin ()) |
371 { | 374 retval = octave_struct::subsref (type, idx, nargout); |
372 // FIXME -- this block of code is the same as the body of | |
373 // octave_struct::subsref. Maybe it could be shared instead of | |
374 // duplicated. | |
375 | |
376 int skip = 1; | |
377 | |
378 switch (type[0]) | |
379 { | |
380 case '(': | |
381 { | |
382 if (type.length () > 1 && type[1] == '.') | |
383 { | |
384 std::list<octave_value_list>::const_iterator p = idx.begin (); | |
385 octave_value_list key_idx = *++p; | |
386 | |
387 Cell tmp = dotref (key_idx); | |
388 | |
389 if (! error_state) | |
390 { | |
391 Cell t = tmp.index (idx.front ()); | |
392 | |
393 retval(0) = (t.length () == 1) ? t(0) : octave_value (t, true); | |
394 | |
395 // We handled two index elements, so tell | |
396 // next_subsref to skip both of them. | |
397 | |
398 skip++; | |
399 } | |
400 } | |
401 else | |
402 retval(0) = octave_value (map.index (idx.front ()), | |
403 class_name ()); | |
404 } | |
405 break; | |
406 | |
407 case '.': | |
408 { | |
409 if (map.numel() > 0) | |
410 { | |
411 Cell t = dotref (idx.front ()); | |
412 | |
413 retval(0) = (t.length () == 1) ? t(0) : octave_value (t, true); | |
414 } | |
415 } | |
416 break; | |
417 | |
418 case '{': | |
419 gripe_invalid_index_type (type_name (), type[0]); | |
420 break; | |
421 | |
422 default: | |
423 panic_impossible (); | |
424 } | |
425 | |
426 // FIXME -- perhaps there should be an | |
427 // octave_value_list::next_subsref member function? See also | |
428 // octave_user_function::subsref. | |
429 | |
430 if (idx.size () > 1) | |
431 retval = retval(0).next_subsref (nargout, type, idx, skip); | |
432 } | |
433 else | 375 else |
434 { | 376 { |
435 octave_value meth = symbol_table::find_method ("subsref", class_name ()); | 377 octave_value meth = symbol_table::find_method ("subsref", class_name ()); |
436 | 378 |
437 if (meth.is_defined ()) | 379 if (meth.is_defined ()) |
478 } | 420 } |
479 | 421 |
480 return retval; | 422 return retval; |
481 } | 423 } |
482 | 424 |
483 octave_value | 425 octave_value |
484 octave_class::numeric_conv (const Cell& val, const std::string& type) | 426 octave_class::subsref (const std::string& type, |
427 const std::list<octave_value_list>& idx, | |
428 bool auto_add) | |
429 { | |
430 if (in_class_method () || called_from_builtin ()) | |
431 return octave_struct::subsref (type, idx, auto_add); | |
432 else | |
433 return subsref (type, idx); | |
434 | |
435 } | |
436 | |
437 void | |
438 octave_class::gripe_failed_assignment (void) | |
439 { | |
440 error ("assignment to class element failed"); | |
441 } | |
442 | |
443 octave_value | |
444 octave_class::dotasgn (const octave_value_list& idx, const octave_value& rhs) | |
485 { | 445 { |
486 octave_value retval; | 446 octave_value retval; |
487 | 447 |
488 if (val.length () == 1) | 448 // Find the class in which this method resides before |
489 { | 449 // attempting to access the requested field. |
490 retval = val(0); | 450 |
491 | 451 std::string method_class = get_current_method_class (); |
492 if (type.length () > 0 && type[0] == '.' && ! retval.is_map ()) | 452 |
493 retval = Octave_map (); | 453 octave_base_value *obvp = find_parent_class (method_class); |
494 } | 454 |
495 else | 455 if (obvp) |
496 gripe_invalid_index_for_assignment (); | 456 { |
457 assert (idx.length () == 1); | |
458 | |
459 std::string key = idx(0).string_value (); | |
460 | |
461 if (! error_state) | |
462 { | |
463 obvp->assign (key, rhs); | |
464 | |
465 if (! error_state) | |
466 { | |
467 count++; | |
468 retval = octave_value (this); | |
469 } | |
470 else | |
471 gripe_failed_assignment (); | |
472 } | |
473 else | |
474 gripe_failed_assignment (); | |
475 } | |
476 else | |
477 error ("malformed class"); | |
497 | 478 |
498 return retval; | 479 return retval; |
499 } | 480 } |
500 | 481 |
501 octave_value | 482 octave_value |
502 octave_class::subsasgn (const std::string& type, | 483 octave_class::subsasgn (const std::string& type, |
503 const std::list<octave_value_list>& idx, | 484 const std::list<octave_value_list>& idx, |
504 const octave_value& rhs) | 485 const octave_value& rhs) |
505 { | 486 { |
506 octave_value retval; | |
507 | |
508 if (! (in_class_method () || called_from_builtin ())) | 487 if (! (in_class_method () || called_from_builtin ())) |
509 { | 488 { |
510 octave_value meth = symbol_table::find_method ("subsasgn", class_name ()); | 489 octave_value meth = symbol_table::find_method ("subsasgn", class_name ()); |
511 | 490 |
512 if (meth.is_defined ()) | 491 if (meth.is_defined ()) |
513 { | 492 { |
493 octave_value retval; | |
494 | |
514 octave_value_list args; | 495 octave_value_list args; |
515 | 496 |
516 if (rhs.is_cs_list ()) | 497 if (rhs.is_cs_list ()) |
517 { | 498 { |
518 octave_value_list lrhs = rhs.list_value (); | 499 octave_value_list lrhs = rhs.list_value (); |
529 return octave_value_list (); | 510 return octave_value_list (); |
530 | 511 |
531 count++; | 512 count++; |
532 args(0) = octave_value (this); | 513 args(0) = octave_value (this); |
533 | 514 |
534 octave_value_list tmp = feval (meth.function_value (), args); | 515 // Now comes the magic. Count copies with me: |
516 // 1. myself (obsolete) | |
517 // 2. the copy inside args (obsolete) | |
518 // 3. the copy in method's symbol table (working) | |
519 // ... possibly more (not obsolete). | |
520 // | |
521 // So we mark 2 copies as obsolete and hold our fingers crossed. | |
522 // But prior to doing that, check whether the routine is amenable | |
523 // to the optimization. | |
524 // It is essential that the handling function doesn't store extra | |
525 // copies anywhere. If it does, things will not break but the | |
526 // optimization won't work. | |
527 | |
528 octave_value_list tmp; | |
529 | |
530 if (obsolete_copies == 0 && meth.is_user_function () | |
531 && meth.user_function_value ()->subsasgn_optimization_ok ()) | |
532 { | |
533 unwind_protect::protect_var (obsolete_copies); | |
534 obsolete_copies = 2; | |
535 | |
536 tmp = feval (meth.function_value (), args); | |
537 | |
538 unwind_protect::run (); | |
539 } | |
540 else | |
541 tmp = feval (meth.function_value (), args); | |
535 | 542 |
536 // FIXME -- should the subsasgn method be able to return | 543 // FIXME -- should the subsasgn method be able to return |
537 // more than one value? | 544 // more than one value? |
538 | 545 |
539 if (tmp.length () > 1) | 546 if (tmp.length () > 1) |
545 | 552 |
546 return retval; | 553 return retval; |
547 } | 554 } |
548 } | 555 } |
549 | 556 |
550 // FIXME -- this block of code is the same as the body of | 557 return octave_struct::subsasgn (type, idx, rhs); |
551 // octave_struct::subsasgn. Maybe it could be shared instead of | |
552 // duplicated. | |
553 | |
554 int n = type.length (); | |
555 | |
556 octave_value t_rhs = rhs; | |
557 | |
558 if (n > 1 && ! (type.length () == 2 && type[0] == '(' && type[1] == '.')) | |
559 { | |
560 switch (type[0]) | |
561 { | |
562 case '(': | |
563 { | |
564 if (type.length () > 1 && type[1] == '.') | |
565 { | |
566 std::list<octave_value_list>::const_iterator p = idx.begin (); | |
567 octave_value_list t_idx = *p; | |
568 | |
569 octave_value_list key_idx = *++p; | |
570 | |
571 assert (key_idx.length () == 1); | |
572 | |
573 std::string key = key_idx(0).string_value (); | |
574 | |
575 if (! error_state) | |
576 { | |
577 octave_value u; | |
578 | |
579 if (! map.contains (key)) | |
580 u = octave_value::empty_conv (type.substr (2), rhs); | |
581 else | |
582 { | |
583 Cell map_val = map.contents (key); | |
584 | |
585 Cell map_elt = map_val.index (idx.front (), true); | |
586 | |
587 u = numeric_conv (map_elt, type.substr (2)); | |
588 } | |
589 | |
590 if (! error_state) | |
591 { | |
592 std::list<octave_value_list> next_idx (idx); | |
593 | |
594 // We handled two index elements, so subsasgn to | |
595 // needs to skip both of them. | |
596 | |
597 next_idx.erase (next_idx.begin ()); | |
598 next_idx.erase (next_idx.begin ()); | |
599 | |
600 u.make_unique (); | |
601 | |
602 t_rhs = u.subsasgn (type.substr (2), next_idx, rhs); | |
603 } | |
604 } | |
605 else | |
606 gripe_invalid_index_for_assignment (); | |
607 } | |
608 else | |
609 gripe_invalid_index_for_assignment (); | |
610 } | |
611 break; | |
612 | |
613 case '.': | |
614 { | |
615 octave_value_list key_idx = idx.front (); | |
616 | |
617 assert (key_idx.length () == 1); | |
618 | |
619 std::string key = key_idx(0).string_value (); | |
620 | |
621 if (! error_state) | |
622 { | |
623 octave_value u; | |
624 | |
625 if (! map.contains (key)) | |
626 u = octave_value::empty_conv (type.substr (1), rhs); | |
627 else | |
628 { | |
629 Cell map_val = map.contents (key); | |
630 | |
631 u = numeric_conv (map_val, type.substr (1)); | |
632 } | |
633 | |
634 if (! error_state) | |
635 { | |
636 std::list<octave_value_list> next_idx (idx); | |
637 | |
638 next_idx.erase (next_idx.begin ()); | |
639 | |
640 u.make_unique (); | |
641 | |
642 t_rhs = u.subsasgn (type.substr (1), next_idx, rhs); | |
643 } | |
644 } | |
645 else | |
646 gripe_invalid_index_for_assignment (); | |
647 } | |
648 break; | |
649 | |
650 case '{': | |
651 gripe_invalid_index_type (type_name (), type[0]); | |
652 break; | |
653 | |
654 default: | |
655 panic_impossible (); | |
656 } | |
657 } | |
658 | |
659 if (! error_state) | |
660 { | |
661 switch (type[0]) | |
662 { | |
663 case '(': | |
664 { | |
665 if (n > 1 && type[1] == '.') | |
666 { | |
667 std::list<octave_value_list>::const_iterator p = idx.begin (); | |
668 octave_value_list key_idx = *++p; | |
669 | |
670 assert (key_idx.length () == 1); | |
671 | |
672 std::string key = key_idx(0).string_value (); | |
673 | |
674 if (! error_state) | |
675 { | |
676 map.assign (idx.front (), key, t_rhs); | |
677 | |
678 if (! error_state) | |
679 { | |
680 count++; | |
681 retval = octave_value (this); | |
682 } | |
683 else | |
684 gripe_failed_assignment (); | |
685 } | |
686 else | |
687 gripe_failed_assignment (); | |
688 } | |
689 else | |
690 { | |
691 if (t_rhs.is_object () || t_rhs.is_map ()) | |
692 { | |
693 Octave_map rhs_map = t_rhs.map_value (); | |
694 | |
695 if (! error_state) | |
696 { | |
697 map.assign (idx.front (), rhs_map); | |
698 | |
699 if (! error_state) | |
700 { | |
701 count++; | |
702 retval = octave_value (this); | |
703 } | |
704 else | |
705 gripe_failed_assignment (); | |
706 } | |
707 else | |
708 error ("invalid class assignment"); | |
709 } | |
710 else | |
711 { | |
712 if (t_rhs.is_empty ()) | |
713 { | |
714 map.maybe_delete_elements (idx.front()); | |
715 | |
716 if (! error_state) | |
717 { | |
718 count++; | |
719 retval = octave_value (this); | |
720 } | |
721 else | |
722 gripe_failed_assignment (); | |
723 } | |
724 else | |
725 error ("invalid class assignment"); | |
726 } | |
727 } | |
728 } | |
729 break; | |
730 | |
731 case '.': | |
732 { | |
733 // Find the class in which this method resides before | |
734 // attempting to access the requested field. | |
735 | |
736 std::string method_class = get_current_method_class (); | |
737 | |
738 octave_base_value *obvp = find_parent_class (method_class); | |
739 | |
740 if (obvp) | |
741 { | |
742 octave_value_list key_idx = idx.front (); | |
743 | |
744 assert (key_idx.length () == 1); | |
745 | |
746 std::string key = key_idx(0).string_value (); | |
747 | |
748 if (! error_state) | |
749 { | |
750 obvp->assign (key, t_rhs); | |
751 | |
752 if (! error_state) | |
753 { | |
754 count++; | |
755 retval = octave_value (this); | |
756 } | |
757 else | |
758 gripe_failed_assignment (); | |
759 } | |
760 else | |
761 gripe_failed_assignment (); | |
762 } | |
763 else | |
764 error ("malformed class"); | |
765 } | |
766 break; | |
767 | |
768 case '{': | |
769 gripe_invalid_index_type (type_name (), type[0]); | |
770 break; | |
771 | |
772 default: | |
773 panic_impossible (); | |
774 } | |
775 } | |
776 else | |
777 gripe_failed_assignment (); | |
778 | |
779 return retval; | |
780 } | 558 } |
781 | 559 |
782 idx_vector | 560 idx_vector |
783 octave_class::index_vector (void) const | 561 octave_class::index_vector (void) const |
784 { | 562 { |
807 } | 585 } |
808 } | 586 } |
809 else | 587 else |
810 error ("no subsindex method defined for class %s", | 588 error ("no subsindex method defined for class %s", |
811 class_name().c_str ()); | 589 class_name().c_str ()); |
812 | |
813 return retval; | |
814 } | |
815 | |
816 size_t | |
817 octave_class::byte_size (void) const | |
818 { | |
819 // Neglect the size of the fieldnames. | |
820 | |
821 size_t retval = 0; | |
822 | |
823 for (Octave_map::const_iterator p = map.begin (); p != map.end (); p++) | |
824 { | |
825 std::string key = map.key (p); | |
826 | |
827 octave_value val = octave_value (map.contents (p)); | |
828 | |
829 retval += val.byte_size (); | |
830 } | |
831 | 590 |
832 return retval; | 591 return retval; |
833 } | 592 } |
834 | 593 |
835 string_vector | 594 string_vector |