]> git.donarmstrong.com Git - lilypond.git/commitdiff
Fix 380: Auto-detect all cyclic references in markups
authorReinhold Kainhofer <reinhold@kainhofer.com>
Fri, 9 Sep 2011 16:46:25 +0000 (18:46 +0200)
committerReinhold Kainhofer <reinhold@kainhofer.com>
Mon, 19 Sep 2011 10:32:41 +0000 (12:32 +0200)
Use a hare+tortoise-like algorithm to detect cyclic references in markups.

input/regression/header-cyclic-reference.ly [new file with mode: 0644]
input/regression/markup-cyclic-reference.ly [new file with mode: 0644]
lily/text-interface.cc

diff --git a/input/regression/header-cyclic-reference.ly b/input/regression/header-cyclic-reference.ly
new file mode 100644 (file)
index 0000000..03bd5c3
--- /dev/null
@@ -0,0 +1,15 @@
+\version "2.15.11"
+#(ly:set-option 'warning-as-error #f)
+
+\header {
+  texidoc = "Cyclic references in header fields should cause a warning, but
+not crash LilyPond with an endless loop"
+
+  title = \markup {Cyclic reference to \fromproperty #'header:title }
+
+  composer = \markup {Cyclic reference to \fromproperty #'header:temp }
+  temp = \markup {Cyclic reference to \fromproperty #'header:composer }
+}
+\score {
+  { c' d' e' f' }
+}
diff --git a/input/regression/markup-cyclic-reference.ly b/input/regression/markup-cyclic-reference.ly
new file mode 100644 (file)
index 0000000..f4495db
--- /dev/null
@@ -0,0 +1,24 @@
+\version "2.15.11"
+#(ly:set-option 'warning-as-error #f)
+
+\header {
+  texidoc = "Cyclic markup definitions should cause a warning, but
+not crash LilyPond with an endless loop"
+}
+
+% A simple markup function that calls itself in a loop.
+#(define-markup-command (cycle layout props m)
+  (markup?)
+  (interpret-markup layout props (make-cycle-markup m)))
+
+% Two simple markup functions that call each other in a loop.
+#(define-markup-command (cycleI layout props m)
+  (markup?)
+  (interpret-markup layout props (make-cycleII-markup m)))
+#(define-markup-command (cycleII layout props m)
+  (markup?)
+  (interpret-markup layout props (make-cycleI-markup m)))
+
+
+\markup { \cycle "a" }
+\markup { \cycleI "a" }
index 8071df3cb856428af7aacac5aaa87028f9fecbb8..5bd13641d737d3bb25dcd08cb6cc3d411a581b8b 100644 (file)
@@ -101,7 +101,27 @@ Text_interface::interpret_markup (SCM layout_smob, SCM props, SCM markup)
       if (!is_markup (markup))
         programming_error ("markup head has no markup signature");
 
-      return scm_apply_2 (func, layout_smob, props, args);
+      /* Use a hare/tortoise algorithm to detect whether we are in a cycle,
+       * i.e. whether we have already encountered the same markup in the
+       * current branch of the markup tree structure. */
+      static vector<SCM> encountered_markups;
+      size_t depth = encountered_markups.size ();
+      if (depth > 0)
+        {
+          int slow = depth / 2;
+          if (ly_is_equal (encountered_markups[slow], markup))
+            {
+              string name = ly_symbol2string (scm_procedure_name (func));
+              // TODO: Also print the arguments of the markup!
+              non_fatal_error (_f("Cyclic markup detected: %s", name));
+              return Stencil().smobbed_copy ();
+            }
+        }
+
+      encountered_markups.push_back (markup);
+      SCM retval = scm_apply_2 (func, layout_smob, props, args);
+      encountered_markups.pop_back ();
+      return retval;
     }
   else
     {