X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=lily%2Fgrob-property.cc;h=9ec39db159640e7c822c6473bb3eccf425b0114d;hb=2f9328a294a9317af7fe91ebdb00534e0cba29ea;hp=d4a0272734ed28d82d8ed4534034ffeed32b87e1;hpb=42e20e7c10237378b290cc9d8bcae7a6fda7b076;p=lilypond.git diff --git a/lily/grob-property.cc b/lily/grob-property.cc index d4a0272734..9ec39db159 100644 --- a/lily/grob-property.cc +++ b/lily/grob-property.cc @@ -1,226 +1,366 @@ /* Implement storage and manipulation of grob properties. - */ +*/ -#include -#include +#include #include "main.hh" -#include "input-smob.hh" - -#include "group-interface.hh" +#include "input.hh" +#include "pointer-group-interface.hh" #include "misc.hh" #include "paper-score.hh" -#include "paper-def.hh" -#include "grob.hh" - +#include "output-def.hh" #include "spanner.hh" +#include "international.hh" #include "item.hh" #include "misc.hh" #include "item.hh" +#include "program-option.hh" +#include "profile.hh" +#include "simple-closure.hh" +#include "unpure-pure-container.hh" +#include "warn.hh" +#include "protected-scm.hh" -/* - HASHING_FOR_MUTABLE_PROPS: - - - plain, -O0 compile - -user 0m12.400s - -sz == 13, -O0 compile - - xdvi trip - -user 0m13.780s - -sz == 5 +Protected_scm grob_property_callback_stack (SCM_EOL); +extern bool debug_property_callbacks; -user 0m13.000s - -sz == 3 +#ifdef DEBUG +static void +print_property_callback_stack () +{ + int frame = 0; + for (SCM s = grob_property_callback_stack; scm_is_pair (s); s = scm_cdr (s)) + message (_f ("%d: %s", frame++, ly_scm_write_string (scm_car (s)).c_str ())); +} +#endif +static Protected_scm modification_callback (SCM_EOL); +static Protected_scm cache_callback (SCM_EOL); -user 0m13.080s +/* +FIXME: this should use ly:set-option interface instead. +*/ -Hashing doesn't improve the result of grob property lookup, at least -not with naive hashing. It is possible that the overhead of the -scm_hash* functions take too much time. One way to solve this is by -using vector accesses directly, and precompute the hashvalues, similar -to CACHE_SYMBOLS. That option could only cause slowdowns if the hash -tables produces weird cache-line trashing. +LY_DEFINE (ly_set_grob_modification_callback, "ly:set-grob-modification-callback", + 1, 0, 0, (SCM cb), + "Specify a procedure that will be called every time LilyPond" + " modifies a grob property. The callback will receive as" + " arguments the grob that is being modified, the name of the" + " C++ file in which the modification was requested, the line" + " number in the C++ file in which the modification was requested," + " the name of the function in which the modification was" + " requested, the property to be changed, and the new value for" + " the property.") +{ + modification_callback = (ly_is_procedure (cb)) ? cb : SCM_BOOL_F; + return SCM_UNSPECIFIED; +} -Second option: we could index immutable props in a hash tab as -well. This only takes space, since they are immutable no updates are -needed. This does take a lot of space, since we must duplicate the -alists (but not the entries). +LY_DEFINE (ly_set_property_cache_callback, "ly:set-property-cache-callback", + 1, 0, 0, (SCM cb), + "Specify a procedure that will be called whenever lilypond" + " calculates a callback function and caches the result. The" + " callback will receive as arguments the grob whose property it" + " is, the name of the property, the name of the callback that" + " calculated the property, and the new (cached) value of the" + " property.") +{ + cache_callback = (ly_is_procedure (cb)) ? cb : SCM_BOOL_F; + return SCM_UNSPECIFIED; +} -*/ +void +Grob::instrumented_set_property (SCM sym, SCM v, + char const *file, + int line, + char const *fun) +{ +#ifdef DEBUG + if (ly_is_procedure (modification_callback)) + scm_apply_0 (modification_callback, + scm_list_n (self_scm (), + scm_from_locale_string (file), + scm_from_int (line), + scm_from_ascii_string (fun), + sym, v, SCM_UNDEFINED)); +#else + (void) file; + (void) line; + (void) fun; +#endif -// #define HASHING_FOR_MUTABLE_PROPS + internal_set_property (sym, v); +} -/* - Remove the value associated with KEY, and return it. The result is - that a next call will yield SCM_EOL (and not the underlying - `basic' property. -*/ SCM -Grob::remove_grob_property (const char* key) +Grob::get_property_alist_chain (SCM def) const { - SCM val = get_grob_property (key); - if (val != SCM_EOL) - set_grob_property (key, SCM_EOL); - return val; + return scm_list_n (mutable_property_alist_, + immutable_property_alist_, + def, + SCM_UNDEFINED); } +extern void check_interfaces_for_property (Grob const *me, SCM sym); -SCM -Grob::get_property_alist_chain (SCM def) const +void +Grob::internal_set_property (SCM sym, SCM v) { -#ifndef HASHING_FOR_MUTABLE_PROPS - return scm_list_n (mutable_property_alist_, - immutable_property_alist_, - def, - SCM_UNDEFINED); -#else - SCM chain = gh_list (immutable_property_alist_, def, SCM_UNDEFINED); - SCM * velts = SCM_VELTS (mutable_property_alist_); - int l = SCM_VECTOR_LENGTH(mutable_property_alist_); - for (int i = 0; i < l; i++) - { - if (gh_pair_p (velts[i])) - chain = gh_cons ( velts[i], chain); - } + internal_set_value_on_alist (&mutable_property_alist_, + sym, v); - return chain; -#endif } +void +Grob::internal_set_value_on_alist (SCM *alist, SCM sym, SCM v) +{ + /* Perhaps we simply do the assq_set, but what the heck. */ + if (!is_live ()) + return; + if (do_internal_type_checking_global) + { + if (!ly_is_procedure (v) + && !unsmob (v) + && !unsmob (v) + && !scm_is_eq (v, ly_symbol2scm ("calculation-in-progress"))) + type_check_assignment (sym, v, ly_symbol2scm ("backend-type?")); -/* - This special add_thing routine is slightly more efficient than + check_interfaces_for_property (this, sym); + } - set_prop (name,cons (thing, get_prop (name))) + *alist = scm_assq_set_x (*alist, sym, v); +} - since it can reuse the handle returned by scm_assq(). -*/ -void -Grob::add_to_list_property (SCM sym, SCM thing) +SCM +Grob::internal_get_property_data (SCM sym) const { - SCM handle -#ifndef HASHING_FOR_MUTABLE_PROPS - = scm_sloppy_assq (sym, mutable_property_alist_) -#else - = scm_hashq_get_handle (mutable_property_alist_, sym); +#ifdef DEBUG + if (profile_property_accesses) + note_property_access (&grob_property_lookup_table, sym); #endif - ; - if (handle != SCM_BOOL_F) + + SCM handle = scm_sloppy_assq (sym, mutable_property_alist_); + if (scm_is_true (handle)) + return scm_cdr (handle); + + handle = scm_sloppy_assq (sym, immutable_property_alist_); + + if (do_internal_type_checking_global && scm_is_pair (handle)) { - gh_set_cdr_x (handle, gh_cons (thing, gh_cdr (handle))); + SCM val = scm_cdr (handle); + if (!ly_is_procedure (val) && !unsmob (val) + && !unsmob (val)) + type_check_assignment (sym, val, ly_symbol2scm ("backend-type?")); + + check_interfaces_for_property (this, sym); } - else + + return scm_is_false (handle) ? SCM_EOL : scm_cdr (handle); +} + +SCM +Grob::internal_get_property (SCM sym) const +{ + SCM val = get_property_data (sym); + +#ifdef DEBUG + if (scm_is_eq (val, ly_symbol2scm ("calculation-in-progress"))) { - /* - There is no mutable prop yet, so create an entry, and put it in front of the - mutable prop list. - */ - handle = scm_sloppy_assq (sym, immutable_property_alist_); - SCM tail = (handle != SCM_BOOL_F) ? gh_cdr(handle) : SCM_EOL; - SCM val = gh_cons (thing, tail); -#ifndef HASHING_FOR_MUTABLE_PROPS - mutable_property_alist_ = gh_cons (gh_cons (sym, val), - mutable_property_alist_); -#else - scm_hashq_set_x (mutable_property_alist_, sym, val); + programming_error (to_string ("cyclic dependency: calculation-in-progress encountered for #'%s (%s)", + ly_symbol2string (sym).c_str (), + name ().c_str ()));//assert (1==0); + if (debug_property_callbacks) + { + message ("backtrace: "); + print_property_callback_stack (); + } + } #endif + + if (Unpure_pure_container *upc = unsmob (val)) + val = upc->unpure_part (); + + if (ly_is_procedure (val) + || unsmob (val)) + { + Grob *me = ((Grob *)this); + val = me->try_callback_on_alist (&me->mutable_property_alist_, sym, val); } + + return val; } +/* Unlike internal_get_property, this function does no caching. Use it, therefore, with caution. */ +SCM +Grob::internal_get_pure_property (SCM sym, int start, int end) const +{ + SCM val = internal_get_property_data (sym); + if (ly_is_procedure (val)) + return call_pure_function (val, scm_list_1 (self_scm ()), start, end); + + if (Unpure_pure_container *upc = unsmob (val)) { + // Do cache, if the function ignores 'start' and 'end' + if (upc->is_unchanging ()) + return internal_get_property (sym); + else + return call_pure_function (val, scm_list_1 (self_scm ()), start, end); + } + + if (Simple_closure *sc = unsmob (val)) + return evaluate_with_simple_closure (self_scm (), + sc->expression (), + true, start, end); + return val; +} -extern void check_interfaces_for_property (Grob const *me, SCM sym); +SCM +Grob::internal_get_maybe_pure_property (SCM sym, bool pure, int start, int end) const +{ + return pure ? internal_get_pure_property (sym, start, end) : internal_get_property (sym); +} -void -Grob::internal_set_grob_property (SCM s, SCM v) +SCM +Grob::try_callback_on_alist (SCM *alist, SCM sym, SCM proc) { + SCM marker = ly_symbol2scm ("calculation-in-progress"); /* - Perhaps we simply do the assq_set, but what the heck. - */ - if (!live()) - return ; + need to put a value in SYM to ensure that we don't get a + cyclic call chain. + */ + *alist = scm_assq_set_x (*alist, sym, marker); + +#ifdef DEBUG + if (debug_property_callbacks) + grob_property_callback_stack = scm_cons (scm_list_3 (self_scm (), sym, proc), grob_property_callback_stack); +#endif -#ifndef NDEBUG - if (internal_type_checking_global_b) + SCM value = SCM_EOL; + if (ly_is_procedure (proc)) + value = scm_call_1 (proc, self_scm ()); + else if (Simple_closure *sc = unsmob (proc)) { - assert (type_check_assignment (s, v, ly_symbol2scm ("backend-type?"))); - check_interfaces_for_property(this, s); + value = evaluate_with_simple_closure (self_scm (), + sc->expression (), + false, 0, 0); } + +#ifdef DEBUG + if (debug_property_callbacks) + grob_property_callback_stack = scm_cdr (grob_property_callback_stack); #endif -#ifndef HASHING_FOR_MUTABLE_PROPS - mutable_property_alist_ = scm_assq_set_x (mutable_property_alist_, s, v); -#else - scm_hashq_set_x (mutable_property_alist_, s, v); + if (scm_is_eq (value, SCM_UNSPECIFIED)) + { + value = get_property_data (sym); + assert (scm_is_null (value) || scm_is_eq (value, marker)); + if (scm_is_eq (value, marker)) + *alist = scm_assq_remove_x (*alist, sym); + } + else + { +#ifdef DEBUG + if (ly_is_procedure (cache_callback)) + scm_apply_0 (cache_callback, + scm_list_n (self_scm (), + sym, + proc, + value, + SCM_UNDEFINED)); #endif + internal_set_value_on_alist (alist, sym, value); + } + + return value; } +void +Grob::internal_set_object (SCM s, SCM v) +{ + /* Perhaps we simply do the assq_set, but what the heck. */ + if (!is_live ()) + return; + + object_alist_ = scm_assq_set_x (object_alist_, s, v); +} + +void +Grob::internal_del_property (SCM sym) +{ + mutable_property_alist_ = scm_assq_remove_x (mutable_property_alist_, sym); +} SCM -Grob::internal_get_grob_property (SCM sym) const +Grob::internal_get_object (SCM sym) const { -#ifndef HASHING_FOR_MUTABLE_PROPS - SCM s = scm_sloppy_assq (sym, mutable_property_alist_); - if (s != SCM_BOOL_F) - return ly_cdr (s); -#else - if (mutable_property_alist_ == SCM_EOL) - return SCM_EOL; - - SCM s = scm_hashq_ref (mutable_property_alist_, sym, SCM_EOL); - if (s!=SCM_EOL) - return s; -#endif + if (profile_property_accesses) + note_property_access (&grob_property_lookup_table, sym); + + SCM s = scm_sloppy_assq (sym, object_alist_); - s = scm_sloppy_assq (sym, immutable_property_alist_); - -#ifndef NDEBUG - if (internal_type_checking_global_b && gh_pair_p (s)) + if (scm_is_true (s)) { - assert (type_check_assignment (sym, gh_cdr (s), ly_symbol2scm ("backend-type?"))); - check_interfaces_for_property(this, sym); + SCM val = scm_cdr (s); + if (ly_is_procedure (val) + || unsmob (val) + || unsmob (val)) + { + Grob *me = ((Grob *)this); + val = me->try_callback_on_alist (&me->object_alist_, sym, val); + } + + return val; } -#endif - return (s == SCM_BOOL_F) ? SCM_EOL : ly_cdr (s); + return SCM_EOL; } -void -Grob::substitute_mutable_properties (SCM crit, SCM orig) +bool +Grob::is_live () const { - set_break_subsititution(crit); -#ifndef HASHING_FOR_MUTABLE_PROPS - mutable_property_alist_ = substitute_mutable_property_alist (orig); -#else - if (orig == SCM_EOL) + return scm_is_pair (immutable_property_alist_); +} + +bool +Grob::internal_has_interface (SCM k) +{ + return scm_is_true (scm_c_memq (k, interfaces_)); +} + +SCM +call_pure_function (SCM unpure, SCM args, int start, int end) +{ + if (Unpure_pure_container *upc = unsmob (unpure)) { - mutable_property_alist_ = SCM_EOL; - return ; + SCM pure = upc->pure_part (); + + if (Simple_closure *sc = unsmob (pure)) + { + SCM expr = sc->expression (); + return evaluate_with_simple_closure (scm_car (args), expr, true, start, end); + } + + if (ly_is_procedure (pure)) + return scm_apply_0 (pure, + scm_append (scm_list_2 (scm_list_3 (scm_car (args), + scm_from_int (start), + scm_from_int (end)), + scm_cdr (args)))); + + return pure; } - - SCM * src_elts = SCM_VELTS (orig); - SCM * dest_elts = SCM_VELTS (mutable_property_alist_); - unsigned int l = SCM_VECTOR_LENGTH(mutable_property_alist_); - assert (l == SCM_VECTOR_LENGTH(orig)); - for (unsigned int i = 0; i < l; i++) + + if (Simple_closure *sc = unsmob (unpure)) { - dest_elts[i] = substitute_mutable_property_alist (src_elts[i]); + SCM expr = sc->expression (); + return evaluate_with_simple_closure (scm_car (args), expr, true, start, end); } -#endif -} + if (!ly_is_procedure (unpure)) + return unpure; -bool -Grob::live () const -{ - return immutable_property_alist_ != SCM_EOL; + return SCM_BOOL_F; } +