]> git.eshelyaron.com Git - emacs.git/commitdiff
(decode_coding): Avoid nested *-change-functions (bug#78042)
authorStefan Monnier <monnier@iro.umontreal.ca>
Fri, 2 May 2025 20:59:17 +0000 (16:59 -0400)
committerEshel Yaron <me@eshelyaron.com>
Sat, 10 May 2025 06:56:57 +0000 (08:56 +0200)
* src/coding.c (decode_coding): Avoid nested *-change-functions (bug#78042).

* test/src/editfns-tests.el (sanity-check-change-functions-before)
(sanity-check-change-functions-after): Record notifications in
`sanity-check-change-functions-op`.
(sanity-check-change-functions-with-op): Don't rely on
`sanity-check-change-functions-op` always holding only the `op`.
(sanity-check-change-functions-errors): Include the sequence of
notifications in the error info.
(editfns-tests--before/after-change-functions): Add tests for (bug#78042).

(cherry picked from commit 9048fcf22c3ec3b5cc77dbb98993a53f5f9b7cd5)

src/coding.c
test/src/editfns-tests.el

index 63b0dbeb18b03e2ed182ed5ecd3d7ca63f324e5f..a923b6bd82d1f21779ead40fd9871a9ea87fa217 100644 (file)
@@ -7363,6 +7363,7 @@ decode_coding (struct coding_system *coding)
   struct ccl_spec cclspec;
   int carryover;
   int i;
+  specpdl_ref count = SPECPDL_INDEX ();
 
   USE_SAFE_ALLOCA;
 
@@ -7389,6 +7390,9 @@ decode_coding (struct coding_system *coding)
 
       undo_list = BVAR (current_buffer, undo_list);
       bset_undo_list (current_buffer, Qt);
+      /* Avoid running nested *-change-functions via 'produce_annotation'.
+         Our callers run *-change-functions over the whole region anyway.  */
+      specbind (Qinhibit_modification_hooks, Qt);
     }
 
   coding->consumed = coding->consumed_char = 0;
@@ -7501,7 +7505,7 @@ decode_coding (struct coding_system *coding)
       record_insert (coding->dst_pos, coding->produced_char);
     }
 
-  SAFE_FREE ();
+  SAFE_FREE_UNBIND_TO (count, Qnil);
 }
 
 
index 2553ad3ec2cbc06768bbba61901694baabce97ba..9a27c420f1ed114eaa0f6b4c497f3c1d9a7c9880 100644 (file)
 (defvar sanity-check-change-functions-op nil)
 (defmacro sanity-check-change-functions-with-op (op &rest body)
   (declare (debug t) (indent 1))
-  `(let ((sanity-check-change-functions-op ,op))
-     (sanity-check--message "%S..." sanity-check-change-functions-op)
+  `(let ((sanity-check-change-functions-op (list ,op)))
+     (sanity-check--message "%S..." ,op)
      ,@body
-     (sanity-check--message "%S...done" sanity-check-change-functions-op)))
+     (sanity-check--message "%S...done" ,op)))
 
 (defun sanity-check--message (&rest args)
   (if sanity-check-change-functions-verbose (apply #'message args)))
     (setq sanity-check-change-functions-buffer-size (buffer-size)))))
 
 (defun sanity-check-change-functions-before (beg end)
+  (push `(BEFORE ,beg ,end) sanity-check-change-functions-op)
   (sanity-check--message "Before: %S %S" beg end)
   (unless (<= (point-min) beg end (point-max))
     (sanity-check-change-functions-error
   (setq sanity-check-change-functions-end end))
 
 (defun sanity-check-change-functions-after (beg end len)
+  (push `(AFTER ,beg ,end ,len) sanity-check-change-functions-op)
   (sanity-check--message "After : %S %S (%S)" beg end len)
   (unless (<= (point-min) beg end (point-max))
     (sanity-check-change-functions-error
 (defun sanity-check-change-functions-errors ()
   (sanity-check-change-functions-check-size)
   (if sanity-check-change-functions-errors
-      (cons sanity-check-change-functions-op
+      (cons (reverse sanity-check-change-functions-op)
             sanity-check-change-functions-errors)))
 
 (ert-deftest editfns-tests--before/after-change-functions ()
         (decode-coding-region beg (point) 'utf-8)
         (should (null (sanity-check-change-functions-errors)))))
 
+    (let ((beg (point)))                ;bug#78042
+      (apply #'insert (make-list 5000 "hell\351 "))
+      (sanity-check-change-functions-with-op 'DECODE-CODING-LARGE-REGION
+        (decode-coding-region beg (point) 'windows-1252)
+        (should-not (sanity-check-change-functions-errors))))
+
+    (let ((beg (point)))                ;bug#78042
+      (sanity-check-change-functions-with-op 'DECODE-CODING-INSERT
+        ;; The `insert' calls make sure we track the buffer-size
+        ;; so as to detect if `decode-coding-string' fails to run the
+        ;; `*-change-functions'.
+        (insert "<")
+        (decode-coding-string "hell\351 " 'windows-1252 nil (current-buffer))
+        (forward-char 6)
+        (insert ">")
+        (should (equal "<hellé >" (buffer-substring beg (point))))
+        (should-not (sanity-check-change-functions-errors))))
+
     (sanity-check-change-functions-with-op 'ENCODE-CODING-STRING
       (encode-coding-string "ééé" 'utf-8 nil (current-buffer))
       (should (null (sanity-check-change-functions-errors))))