comparison libinterp/octave-value/ov-classdef.cc @ 16698:13b3b92ea99c classdef

Implement property accessors. * libinterp/parse-tree/oct-parse.in.yy: Prepend "get." or "set." to property accessor methods names. * libinterp/octave-value/ov-classdef.h (cdef_property_rep::is_recursive_set): Delete method. * libinterp/octave-value/ov-classdef.cc (cdef_property::cdef_property_rep::is_recursive_set): Delete method. (is_method_executing): New static utility function. (cdef_property::cdef_property_rep::get_value, cdef_property::cdef_property_rep::set)value): Use it to check whether the accessor method is already executing for the same object. (cdef_class::make_meta_class): Associate property accessors to their corresponding properties. (make_fcn_handle(octave_value, string)): New static overload.
author Michael Goffioul <michael.goffioul@gmail.com>
date Fri, 24 May 2013 15:41:52 -0400
parents 665fa0f621cc
children c4f5c781c3ca
comparison
equal deleted inserted replaced
16696:665fa0f621cc 16698:13b3b92ea99c
129 octave_value fcn_handle (new octave_fcn_handle (fcn, nm)); 129 octave_value fcn_handle (new octave_fcn_handle (fcn, nm));
130 130
131 return fcn_handle; 131 return fcn_handle;
132 } 132 }
133 133
134 static octave_value
135 make_fcn_handle (const octave_value& fcn, const std::string& nm)
136 {
137 octave_value retval;
138
139 if (fcn.is_defined ())
140 retval = octave_value (new octave_fcn_handle (fcn, nm));
141
142 return retval;
143 }
144
134 inline octave_value_list 145 inline octave_value_list
135 execute_ov (octave_value val, const octave_value_list& args, int nargout) 146 execute_ov (octave_value val, const octave_value_list& args, int nargout)
136 { 147 {
137 std::list<octave_value_list> idx (1, args); 148 std::list<octave_value_list> idx (1, args);
138 149
344 } 355 }
345 else 356 else
346 error ("invalid property/method access in class `%s'", 357 error ("invalid property/method access in class `%s'",
347 cls.get_name ().c_str ()); 358 cls.get_name ().c_str ());
348 359
360 return false;
361 }
362
363 bool
364 is_method_executing (const octave_value& ov, const cdef_object& obj)
365 {
366 octave_function* stack_fcn = octave_call_stack::current ();
367
368 octave_function* method_fcn = ov.function_value (true);
369
370 // Does the top of the call stack match our target function?
371
372 if (stack_fcn && stack_fcn == method_fcn)
373 {
374 octave_user_function* uf = method_fcn->user_function_value (true);
375
376 // We can only check the context object for user-function (not builtin),
377 // where we have access to the parameters (arguments and return values).
378 // That's ok as there's no need to call this function for builtin
379 // methods.
380
381 if (uf)
382 {
383 // At this point, the method is executing, but we still need to
384 // check the context object for which the method is executing. For
385 // methods, it's the first argument of the function; for ctors, it
386 // is the first return value.
387
388 tree_parameter_list* pl = uf->is_classdef_constructor ()
389 ? uf->return_list () : uf->parameter_list ();
390
391 if (pl && pl->size () > 0)
392 {
393 octave_value arg0 = pl->front ()->lvalue ().value ();
394
395 if (arg0.is_defined () && arg0.type_name () == "object")
396 {
397 cdef_object arg0_obj = to_cdef (arg0);
398
399 return obj.is (arg0_obj);
400 }
401 }
402 }
403 }
404
349 return false; 405 return false;
350 } 406 }
351 407
352 static octave_value_list 408 static octave_value_list
353 class_get_methods (const octave_value_list& args, int /* nargout */) 409 class_get_methods (const octave_value_list& args, int /* nargout */)
2476 2532
2477 tree_classdef_body* b = t->body (); 2533 tree_classdef_body* b = t->body ();
2478 2534
2479 if (b) 2535 if (b)
2480 { 2536 {
2537 // Keep track of the get/set accessor methods. They will be used
2538 // later on when creating properties.
2539
2540 std::map<std::string, octave_value> get_methods;
2541 std::map<std::string, octave_value> set_methods;
2542
2481 // Method blocks 2543 // Method blocks
2482 2544
2483 std::list<tree_classdef_methods_block *> mb_list = b->methods_list (); 2545 std::list<tree_classdef_methods_block *> mb_list = b->methods_list ();
2484 2546
2485 for (tree_classdef_body::methods_list_iterator it = mb_list.begin (); 2547 for (tree_classdef_body::methods_list_iterator it = mb_list.begin ();
2497 { 2559 {
2498 std::string aname = (*ait)->ident ()->name (); 2560 std::string aname = (*ait)->ident ()->name ();
2499 octave_value avalue = compute_attribute_value (*ait); 2561 octave_value avalue = compute_attribute_value (*ait);
2500 2562
2501 gnulib::printf ("method attribute: %s = %s\n", aname.c_str (), 2563 gnulib::printf ("method attribute: %s = %s\n", aname.c_str (),
2502 attribute_value_to_string (*ait, avalue).c_str ()); 2564 attribute_value_to_string (*ait, avalue).c_str ());
2503 amap[aname] = avalue; 2565 amap[aname] = avalue;
2504 } 2566 }
2505 } 2567 }
2506 2568
2507 // Methods 2569 // Methods
2510 { 2572 {
2511 for (tree_classdef_methods_list::iterator mit = (*it)->element_list ()->begin (); 2573 for (tree_classdef_methods_list::iterator mit = (*it)->element_list ()->begin ();
2512 mit != (*it)->element_list ()->end (); ++mit) 2574 mit != (*it)->element_list ()->end (); ++mit)
2513 { 2575 {
2514 std::string mname = mit->function_value ()->name (); 2576 std::string mname = mit->function_value ()->name ();
2515 cdef_method meth = make_method (retval, mname, *mit); 2577 std::string mprefix = mname.substr (0, 4);
2516 2578
2517 gnulib::printf ("%s: %s\n", (mname == class_name ? "constructor" : "method"), 2579 if (mprefix == "get.")
2518 mname.c_str ()); 2580 get_methods[mname.substr (4)] =
2519 for (std::map<std::string, octave_value>::iterator ait = amap.begin (); 2581 make_fcn_handle (*mit, full_class_name + ">" + mname);
2520 ait != amap.end (); ++ait) 2582 else if (mprefix == "set.")
2521 meth.put (ait->first, ait->second); 2583 set_methods[mname.substr (4)] =
2522 2584 make_fcn_handle (*mit, full_class_name + ">" + mname);
2523 retval.install_method (meth); 2585 else
2586 {
2587 cdef_method meth = make_method (retval, mname, *mit);
2588
2589 gnulib::printf ("%s: %s\n", (mname == class_name ? "constructor" : "method"),
2590 mname.c_str ());
2591 for (std::map<std::string, octave_value>::iterator ait = amap.begin ();
2592 ait != amap.end (); ++ait)
2593 meth.put (ait->first, ait->second);
2594
2595 retval.install_method (meth);
2596 }
2524 } 2597 }
2525 } 2598 }
2526 } 2599 }
2527 2600
2528 // Property blocks 2601 // Property blocks
2567 if ((*it)->element_list ()) 2640 if ((*it)->element_list ())
2568 { 2641 {
2569 for (tree_classdef_property_list::iterator pit = (*it)->element_list ()->begin (); 2642 for (tree_classdef_property_list::iterator pit = (*it)->element_list ()->begin ();
2570 pit != (*it)->element_list ()->end (); ++pit) 2643 pit != (*it)->element_list ()->end (); ++pit)
2571 { 2644 {
2572 cdef_property prop = ::make_property (retval, (*pit)->ident ()->name ()); 2645 std::string prop_name = (*pit)->ident ()->name ();
2646
2647 cdef_property prop = ::make_property (retval, prop_name);
2573 2648
2574 gnulib::printf ("property: %s\n", (*pit)->ident ()->name ().c_str ()); 2649 gnulib::printf ("property: %s\n", (*pit)->ident ()->name ().c_str ());
2575 if ((*pit)->expression ()) 2650 if ((*pit)->expression ())
2576 { 2651 {
2577 octave_value pvalue = (*pit)->expression ()->rvalue1 (); 2652 octave_value pvalue = (*pit)->expression ()->rvalue1 ();
2579 gnulib::printf ("property default: %s\n", 2654 gnulib::printf ("property default: %s\n",
2580 attribute_value_to_string (*pit, pvalue).c_str ()); 2655 attribute_value_to_string (*pit, pvalue).c_str ());
2581 prop.put ("DefaultValue", pvalue); 2656 prop.put ("DefaultValue", pvalue);
2582 } 2657 }
2583 2658
2659 // Install property attributes. This is done before assigning the
2660 // property accessors so we can do validationby using cdef_property
2661 // methods.
2662
2584 for (std::map<std::string, octave_value>::iterator ait = amap.begin (); 2663 for (std::map<std::string, octave_value>::iterator ait = amap.begin ();
2585 ait != amap.end (); ++ait) 2664 ait != amap.end (); ++ait)
2586 prop.put (ait->first, ait->second); 2665 prop.put (ait->first, ait->second);
2666
2667 // Install property access methods, if any. Remove the accessor
2668 // methods from the temporary storage map, so we can detect which
2669 // ones are invalid and do not correspond to a defined property.
2670
2671 std::map<std::string, octave_value>::iterator git =
2672 get_methods.find (prop_name);
2673
2674 if (git != get_methods.end ())
2675 {
2676 prop.put ("GetMethod", git->second);
2677 get_methods.erase (git);
2678 }
2679
2680 std::map<std::string, octave_value>::iterator sit =
2681 set_methods.find (prop_name);
2682
2683 if (sit != set_methods.end ())
2684 {
2685 prop.put ("SetMethod", sit->second);
2686 set_methods.erase (sit);
2687 }
2587 2688
2588 retval.install_property (prop); 2689 retval.install_property (prop);
2589 } 2690 }
2590 } 2691 }
2591 } 2692 }
2630 2731
2631 octave_value get_fcn = get ("GetMethod"); 2732 octave_value get_fcn = get ("GetMethod");
2632 2733
2633 // FIXME: should check whether we're already in get accessor method 2734 // FIXME: should check whether we're already in get accessor method
2634 2735
2635 if (get_fcn.is_empty ()) 2736 if (get_fcn.is_empty () || is_method_executing (get_fcn, obj))
2636 retval = obj.get (get ("Name").string_value ()); 2737 retval = obj.get (get ("Name").string_value ());
2637 else 2738 else
2638 { 2739 {
2639 octave_value_list args; 2740 octave_value_list args;
2640 2741
2695 } 2796 }
2696 } 2797 }
2697 2798
2698 octave_value set_fcn = get ("SetMethod"); 2799 octave_value set_fcn = get ("SetMethod");
2699 2800
2700 if (set_fcn.is_empty () || is_recursive_set (obj)) 2801 if (set_fcn.is_empty () || is_method_executing (set_fcn, obj))
2701 { 2802 obj.put (get ("Name").string_value (), val);
2702 obj.put (get ("Name").string_value (), val);
2703 }
2704 else 2803 else
2705 { 2804 {
2706 octave_value_list args; 2805 octave_value_list args;
2707 2806
2708 args(0) = to_ov (obj); 2807 args(0) = to_ov (obj);