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