diff options
| author | Stefan Monnier | 2025-05-02 16:59:17 -0400 |
|---|---|---|
| committer | Stefan Monnier | 2025-05-02 17:06:09 -0400 |
| commit | 9048fcf22c3ec3b5cc77dbb98993a53f5f9b7cd5 (patch) | |
| tree | d633384235b0038f3aa3568f096367374493a1e6 | |
| parent | 99ca41b6ef300653a0d15b73a0c0d446a9a9e059 (diff) | |
| download | emacs-9048fcf22c3ec3b5cc77dbb98993a53f5f9b7cd5.tar.gz emacs-9048fcf22c3ec3b5cc77dbb98993a53f5f9b7cd5.zip | |
(decode_coding): Avoid nested *-change-functions (bug#78042)
* 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).
| -rw-r--r-- | src/coding.c | 6 | ||||
| -rw-r--r-- | test/src/editfns-tests.el | 28 |
2 files changed, 29 insertions, 5 deletions
diff --git a/src/coding.c b/src/coding.c index 63b0dbeb18b..a923b6bd82d 100644 --- a/src/coding.c +++ b/src/coding.c | |||
| @@ -7363,6 +7363,7 @@ decode_coding (struct coding_system *coding) | |||
| 7363 | struct ccl_spec cclspec; | 7363 | struct ccl_spec cclspec; |
| 7364 | int carryover; | 7364 | int carryover; |
| 7365 | int i; | 7365 | int i; |
| 7366 | specpdl_ref count = SPECPDL_INDEX (); | ||
| 7366 | 7367 | ||
| 7367 | USE_SAFE_ALLOCA; | 7368 | USE_SAFE_ALLOCA; |
| 7368 | 7369 | ||
| @@ -7389,6 +7390,9 @@ decode_coding (struct coding_system *coding) | |||
| 7389 | 7390 | ||
| 7390 | undo_list = BVAR (current_buffer, undo_list); | 7391 | undo_list = BVAR (current_buffer, undo_list); |
| 7391 | bset_undo_list (current_buffer, Qt); | 7392 | bset_undo_list (current_buffer, Qt); |
| 7393 | /* Avoid running nested *-change-functions via 'produce_annotation'. | ||
| 7394 | Our callers run *-change-functions over the whole region anyway. */ | ||
| 7395 | specbind (Qinhibit_modification_hooks, Qt); | ||
| 7392 | } | 7396 | } |
| 7393 | 7397 | ||
| 7394 | coding->consumed = coding->consumed_char = 0; | 7398 | coding->consumed = coding->consumed_char = 0; |
| @@ -7501,7 +7505,7 @@ decode_coding (struct coding_system *coding) | |||
| 7501 | record_insert (coding->dst_pos, coding->produced_char); | 7505 | record_insert (coding->dst_pos, coding->produced_char); |
| 7502 | } | 7506 | } |
| 7503 | 7507 | ||
| 7504 | SAFE_FREE (); | 7508 | SAFE_FREE_UNBIND_TO (count, Qnil); |
| 7505 | } | 7509 | } |
| 7506 | 7510 | ||
| 7507 | 7511 | ||
diff --git a/test/src/editfns-tests.el b/test/src/editfns-tests.el index 2553ad3ec2c..9a27c420f1e 100644 --- a/test/src/editfns-tests.el +++ b/test/src/editfns-tests.el | |||
| @@ -498,10 +498,10 @@ | |||
| 498 | (defvar sanity-check-change-functions-op nil) | 498 | (defvar sanity-check-change-functions-op nil) |
| 499 | (defmacro sanity-check-change-functions-with-op (op &rest body) | 499 | (defmacro sanity-check-change-functions-with-op (op &rest body) |
| 500 | (declare (debug t) (indent 1)) | 500 | (declare (debug t) (indent 1)) |
| 501 | `(let ((sanity-check-change-functions-op ,op)) | 501 | `(let ((sanity-check-change-functions-op (list ,op))) |
| 502 | (sanity-check--message "%S..." sanity-check-change-functions-op) | 502 | (sanity-check--message "%S..." ,op) |
| 503 | ,@body | 503 | ,@body |
| 504 | (sanity-check--message "%S...done" sanity-check-change-functions-op))) | 504 | (sanity-check--message "%S...done" ,op))) |
| 505 | 505 | ||
| 506 | (defun sanity-check--message (&rest args) | 506 | (defun sanity-check--message (&rest args) |
| 507 | (if sanity-check-change-functions-verbose (apply #'message args))) | 507 | (if sanity-check-change-functions-verbose (apply #'message args))) |
| @@ -530,6 +530,7 @@ | |||
| 530 | (setq sanity-check-change-functions-buffer-size (buffer-size))))) | 530 | (setq sanity-check-change-functions-buffer-size (buffer-size))))) |
| 531 | 531 | ||
| 532 | (defun sanity-check-change-functions-before (beg end) | 532 | (defun sanity-check-change-functions-before (beg end) |
| 533 | (push `(BEFORE ,beg ,end) sanity-check-change-functions-op) | ||
| 533 | (sanity-check--message "Before: %S %S" beg end) | 534 | (sanity-check--message "Before: %S %S" beg end) |
| 534 | (unless (<= (point-min) beg end (point-max)) | 535 | (unless (<= (point-min) beg end (point-max)) |
| 535 | (sanity-check-change-functions-error | 536 | (sanity-check-change-functions-error |
| @@ -540,6 +541,7 @@ | |||
| 540 | (setq sanity-check-change-functions-end end)) | 541 | (setq sanity-check-change-functions-end end)) |
| 541 | 542 | ||
| 542 | (defun sanity-check-change-functions-after (beg end len) | 543 | (defun sanity-check-change-functions-after (beg end len) |
| 544 | (push `(AFTER ,beg ,end ,len) sanity-check-change-functions-op) | ||
| 543 | (sanity-check--message "After : %S %S (%S)" beg end len) | 545 | (sanity-check--message "After : %S %S (%S)" beg end len) |
| 544 | (unless (<= (point-min) beg end (point-max)) | 546 | (unless (<= (point-min) beg end (point-max)) |
| 545 | (sanity-check-change-functions-error | 547 | (sanity-check-change-functions-error |
| @@ -565,7 +567,7 @@ | |||
| 565 | (defun sanity-check-change-functions-errors () | 567 | (defun sanity-check-change-functions-errors () |
| 566 | (sanity-check-change-functions-check-size) | 568 | (sanity-check-change-functions-check-size) |
| 567 | (if sanity-check-change-functions-errors | 569 | (if sanity-check-change-functions-errors |
| 568 | (cons sanity-check-change-functions-op | 570 | (cons (reverse sanity-check-change-functions-op) |
| 569 | sanity-check-change-functions-errors))) | 571 | sanity-check-change-functions-errors))) |
| 570 | 572 | ||
| 571 | (ert-deftest editfns-tests--before/after-change-functions () | 573 | (ert-deftest editfns-tests--before/after-change-functions () |
| @@ -591,6 +593,24 @@ | |||
| 591 | (decode-coding-region beg (point) 'utf-8) | 593 | (decode-coding-region beg (point) 'utf-8) |
| 592 | (should (null (sanity-check-change-functions-errors))))) | 594 | (should (null (sanity-check-change-functions-errors))))) |
| 593 | 595 | ||
| 596 | (let ((beg (point))) ;bug#78042 | ||
| 597 | (apply #'insert (make-list 5000 "hell\351 ")) | ||
| 598 | (sanity-check-change-functions-with-op 'DECODE-CODING-LARGE-REGION | ||
| 599 | (decode-coding-region beg (point) 'windows-1252) | ||
| 600 | (should-not (sanity-check-change-functions-errors)))) | ||
| 601 | |||
| 602 | (let ((beg (point))) ;bug#78042 | ||
| 603 | (sanity-check-change-functions-with-op 'DECODE-CODING-INSERT | ||
| 604 | ;; The `insert' calls make sure we track the buffer-size | ||
| 605 | ;; so as to detect if `decode-coding-string' fails to run the | ||
| 606 | ;; `*-change-functions'. | ||
| 607 | (insert "<") | ||
| 608 | (decode-coding-string "hell\351 " 'windows-1252 nil (current-buffer)) | ||
| 609 | (forward-char 6) | ||
| 610 | (insert ">") | ||
| 611 | (should (equal "<hellé >" (buffer-substring beg (point)))) | ||
| 612 | (should-not (sanity-check-change-functions-errors)))) | ||
| 613 | |||
| 594 | (sanity-check-change-functions-with-op 'ENCODE-CODING-STRING | 614 | (sanity-check-change-functions-with-op 'ENCODE-CODING-STRING |
| 595 | (encode-coding-string "ééé" 'utf-8 nil (current-buffer)) | 615 | (encode-coding-string "ééé" 'utf-8 nil (current-buffer)) |
| 596 | (should (null (sanity-check-change-functions-errors)))) | 616 | (should (null (sanity-check-change-functions-errors)))) |