Subject: bug#78898: Make read/readevalloop reentrant

From: Lynn Winebarger <[email-masked]>
Date: Thu, 3 Jul 2025 22:47:38 -0400

The attached patch (in addition to the preceding 2) eliminates most of
the errors from "make check".  I've attached the ones from edebug.  I
see "edebug-tests-break-in-lambda-out-of-defining-context" fail in my
build of the master checkout, so that leaves two unexplained failures,
edebug-tests-step-into-function and
edebug-tests-trace-function-call-and-return.  I currently have no idea
what is going on.  The other 67 (or thereabouts) errors were fixed by
1. making the control flow more faithful to the original for
eval-region [ about 35 tests].  I split LREAD_FRAME_MULTIPLE into
LREAD_FRAME_INTEGRAL (where all lisp state changes are integrated) and
LREAD_FRAME_DYNAMIC_WIND (where eval-region provides some protection
of the current buffer state).  The previous version of the code relied
on !NILP(start) to prompt this protective wind-down/wind-up behavior
between read and eval.
2. Ensure that if readfun is called and generates an eof due to the
point moving to the end of the region, any expression returned gets
evaluated before the eof is honored in readevalloop.
3. ensuring that calling "load-read-function" uses the buffer as a
stream argument if that is the input for the frame.  Edebug relies on
this undocumented behavior in advising load-read-function to
instrument code being debugged [20+ tests].
4. Getting rid of the new signal handlers used to bypass frames - may
have been responsible for one or two test failures.  They were mainly
an experiment to understand how the dynamic stack works and if a more
explicit dynamic-wind type behavior would work if code rand while
processing a maybe_quit.  I'm not sure if this changes an observed
test results, but it doesn't really do anything useful beyond testing
how the idea might actually work.  So, calls to read are still
susceptible to eof signals generated but not handled by elisp code run
while reading the stream.

I can't perceive any difference in startup time with the debug build
compiled without support for native compilation.

Overall results of 'make check'
SUMMARY OF TEST RESULTS
-----------------------
Files examined: 533
Ran 8119 tests, 7959 results as expected, 27 unexpected, 133 skipped
4 files contained unexpected results:
  lisp/vc/vc-tests/vc-tests.log
  lisp/progmodes/python-tests.log
  lisp/progmodes/c-ts-mode-tests.log
  lisp/emacs-lisp/edebug-tests.log

For the non-edebug tests, I get the same failures for my reference
checkout on my test system.
I'd be happy to take any hints on those two remaining edebug
regressions.  I have no idea what the difference of interest is
between those two and all the other similar looking tests is, in terms
of reliance on the behavior of
eval-region/eval-buffer/load-read-function.
From caca0a7739d0c27d6275b2bedd27c26cae43011a Mon Sep 17 00:00:00 2001
From: Onnie Lynn Winebarger <[email-masked]>
Date: Thu, 3 Jul 2025 22:05:36 -0400
Subject: [PATCH 3/3] Reverted over-regularization of control flow

---
 src/lread.c | 479 ++++++++++++++++++++--------------------------------
 1 file changed, 184 insertions(+), 295 deletions(-)

diff --git a/src/lread.c b/src/lread.c
index 91d181c604a..3be2f8bd7d2 100644
--- a/src/lread.c
+++ b/src/lread.c
@@ -381,8 +381,10 @@ #define INIT_READ_STACK {NULL, 0, 0}
   LREAD_FRAME_UNINIT,
   /* some form of "read" call*/
   LREAD_FRAME_SINGLE,
-  /* some form of readevalloop call*/
-  LREAD_FRAME_MULTIPLE
+  /* readevalloop that does not hide changes to lisp state */
+  LREAD_FRAME_INTEGRAL,
+  /* readevalloop that hides changes to lisp state */
+  LREAD_FRAME_DYNAMIC_WIND
 } lread_frame_type;
 
 typedef struct lread_frame lread_frame;
@@ -461,6 +463,7 @@ #define INIT_READ_STACK {NULL, 0, 0}
   bool printflag;
   Lisp_Object value;
   Lisp_Object error;
+  bool read_expr;
 };
 
 #ifdef WRAP_SPECPDL_REF
@@ -505,6 +508,7 @@ #define INIT_LREAD_FRAME()	(lread_frame)				\
       .error = Qnil,							\
       .interrupted = false,						\
       .prev = NULL,							\
+      .read_expr = false,						\
       .mule_charset = NULL						\
     }
 
@@ -558,11 +562,7 @@ #define INIT_LREAD_FRAME()	(lread_frame)				\
 static Lisp_Object read_bool_vector (register lread_frame *frame);
 static void skip_space_and_comments (register lread_frame *frame);
 static Lisp_Object read0 (register lread_frame *frame);
-static Lisp_Object call_read0 (Lisp_Object frameptr)
-{
-  register lread_frame *frame = xmint_pointer (frameptr);
-  return read0 (frame);
-}
+
 static bool handle_read_stream_defaults (Lisp_Object *stream);
 static struct buffer *lread_get_buffer (register lread_frame *frame);
 static Lisp_Object lread_get_buffer_obj (register lread_frame *frame);
@@ -573,9 +573,8 @@ #define INIT_LREAD_FRAME()	(lread_frame)				\
 static Lisp_Object lread_set_read_eof (Lisp_Object data);
 static bool setup_buffer_for_readevalloop (register lread_frame *frame,
 					   Lisp_Object start, Lisp_Object end);
-static void prepare_buffer_for_interruptible_read (register lread_frame *frame);
-static Lisp_Object interrupt_error_handler (Lisp_Object data);
-static Lisp_Object base_interrupt_error_handler (Lisp_Object data);
+static void prepare_buffer_for_interruptible_read (register lread_frame *frame,
+						   struct buffer *buf);
 static Lisp_Object readevalloop_eof_trampoline (Lisp_Object frameptr);
 
 static void build_load_history (register lread_frame *frame, Lisp_Object, bool);
@@ -631,8 +630,6 @@ grow_int_buffer (int *buf, ptrdiff_t offset,
 INLINE void
 lread_unwind_for_eval (register lread_frame *frame)
 {
-  /* if (UNINIT_SPECPDL_REF(frame->dynamic_frame)) */
-  /*   return; */
   eassert (!UNINIT_SPECPDL_REF(frame->dynamic_frame));
   unbind_to (frame->dynamic_frame, Qnil);
 }
@@ -640,8 +637,6 @@ lread_unwind_for_eval (register lread_frame *frame)
 INLINE void
 lread_init_dynamic_frame (register lread_frame *frame)
 {
-  /* if (!UNINIT_SPECPDL_REF(frame->dynamic_frame)) */
-  /*   unbind_to (frame->dynamic_frame, Qnil); */
   eassert (UNINIT_SPECPDL_REF(frame->dynamic_frame));
   frame->dynamic_frame = SPECPDL_INDEX ();
 }
@@ -981,96 +976,6 @@ report_lread_multibyte (register lread_frame *frame)
 static int readbyte_from_file (register lread_frame *frame);

-static bool
-cleanup_after_interrupt_p (register lread_frame *frame)
-{
-  switch (frame->intype)
-    {
-    case LREAD_INPUT_PROCEDURE:
-    case LREAD_INPUT_STRING:
-    case LREAD_INPUT_BINARY:
-    case LREAD_INPUT_NULL:
-    case LREAD_INPUT_FILE:
-    case LREAD_INPUT_FILE_MULE:
-      return false;
-      break;
-    case LREAD_INPUT_BUFFER:
-    case LREAD_INPUT_MARKER:
-      return (frame->type == LREAD_FRAME_MULTIPLE);
-      break;
-    case LREAD_INPUT_INDIRECT:
-      return cleanup_after_interrupt_p (frame->input.frame.source);
-      UNRECOGNIZED_LREAD_INPUT_CASES("cleanup_after_interrupt_p");
-    }
-  eassert (0);
-}
-
-static void
-lread_continue_frame_after_interrupt (register lread_frame *frame)
-{
-  /* If we are unwinding due to a non-local exit during an
-     interrupt, do nothing*/
-  if (frame->interrupted)
-    return;
-  unbind_to (frame->dynamic_frame, Qnil);
-  /* need to ensure buffers are still safe */
-  switch (frame->intype)
-    {
-    case LREAD_INPUT_PROCEDURE:
-    case LREAD_INPUT_STRING:
-    case LREAD_INPUT_BINARY:
-    case LREAD_INPUT_NULL:
-    case LREAD_INPUT_FILE:
-    case LREAD_INPUT_FILE_MULE:
-      break;
-    case LREAD_INPUT_BUFFER:
-    case LREAD_INPUT_MARKER:
-      if (frame->type == LREAD_FRAME_MULTIPLE)
-	prepare_buffer_for_interruptible_read (frame);
-      break;
-    case LREAD_INPUT_INDIRECT:
-      lread_continue_frame_after_interrupt (frame->input.frame.source);
-      break;
-      UNRECOGNIZED_LREAD_INPUT_CASES("continue_frame_after_interrupt");
-    }
-}
-
-static Lisp_Object
-call_maybe_quit (void)
-{
-  maybe_quit();
-  return Qnil;
-}
-static void
-lread_maybe_quit (register lread_frame *frame)
-{
-  bool need_cleanup = cleanup_after_interrupt_p (frame);
-  /* ensure frame is properly restored if
-     some code affects a lisp object we are
-     working with */
-  if (need_cleanup)
-    {
-      /* this should leave the binding to restore the previous
-	 frame in place */
-      lread_unwind_for_eval (frame);
-    }
-  specpdl_ref count = SPECPDL_INDEX();
-  /* any errors thrown during an interrupt handler will be ignored in the
-     dynamic extent of this frame, i.e.  spurious end-of-file signals */
-  /* should also deal with throws, but we don't install any catch handlers */
-  /* of course, this does not deal with all the other places interrupts
-     could be processed during a read or readevalloop....  */
-  frame->interrupted = true;
-  internal_condition_case (call_maybe_quit, Qt, interrupt_error_handler);
-  frame->interrupted = false;
-  if (need_cleanup)
-    {
-      eassert (!specpdl_ref_lt (count, frame->dynamic_frame));
-      unbind_to (count, Qnil);
-      lread_continue_frame_after_interrupt (frame);
-    }
-}
-
 static int
 readbyte_from_stdio (register lread_frame *frame)
 {
@@ -1090,7 +995,7 @@ readbyte_from_stdio (register lread_frame *frame)
 	 && ferror (frame->input.f.stream))
     {
       unblock_input ();
-      lread_maybe_quit (frame);
+      maybe_quit ();
       block_input ();
       clearerr (frame->input.f.stream);
     }
@@ -1111,7 +1016,7 @@ readbyte_from_stdio (register lread_frame *frame)
 	if (errno == EINTR)
 	  {
 	    unblock_input ();
-	    lread_maybe_quit (frame);
+	    maybe_quit ();
 	    block_input ();
 	    goto retry;
 	  }
@@ -1722,59 +1627,58 @@ closing_unreadchar (register lread_frame *frame)
       break;
     case LREAD_INPUT_FILE:
     case LREAD_INPUT_FILE_MULE:
-      if (frame->type == LREAD_FRAME_SINGLE)
-	{
-	  /* always put back any loose bytes that have been peeked
-	     but not part of a character consumed by read0 */
-	  for (int i = frame->input.f.lookahead-1; i >= 0; i--)
-	    {
-	      ungetc (frame->input.f.buf[i], frame->input.f.stream);
-	    }
-	  frame->input.f.lookahead = 0;
-	  /* Then any character that has been unread
-	     has to be turned into a byte
-	     encoding and returned */
-	  if (frame->unread_char >= 0)
-	    {
-	      int c = frame->unread_char;
-	      int dim = frame->unread_bytes;
-	      unsigned char buf[MAX_MULTIBYTE_LENGTH];
-	      frame->unread_char = -1;
-	      frame->unread_bytes = 0;
-	      if (frame->multibyte && !frame->input.f.mule)
-		{
-		  /* only emacs-internal coding allowed
-		     for actual multibyte file streams
-		     Only case is reading from standard
-		     input in non-editor mode.
-		  */
-		  int len = CHAR_STRING (c, buf);
-		  eassume (len == dim);
-		  for (int i = (len - 1); i >= 0; i--)
-		    ungetc (buf[i], frame->input.f.stream);
-		}
-	      else if (!frame->multibyte)
-		{
-		  /* unibyte */
-		}
-	      else
-		{
-		  /* emacs-mule encoding
-		     Least likely case last */
-		  int sz = CHARSET_DIMENSION (frame->mule_charset);
-		  unsigned code = ENCODE_CHAR (frame->mule_charset, c);
-		  int len;
-		  for (len = 0; sz > 0 && (code & 0xFF); sz--)
-		    {
-		      buf[len++] = (unsigned char) (code & 0xFF);
-		      code >>= 8;
-		    }
-		  eassume (len == dim);
-		  for (int i = (len - 1); i >= 0; i--)
-		    ungetc (buf[i], frame->input.f.stream);
-		}
-	    }
-	}
+      {
+	/* always put back any loose bytes that have been peeked
+	   but not part of a character consumed by read0 */
+	for (int i = frame->input.f.lookahead-1; i >= 0; i--)
+	  {
+	    ungetc (frame->input.f.buf[i], frame->input.f.stream);
+	  }
+	frame->input.f.lookahead = 0;
+	/* Then any character that has been unread
+	   has to be turned into a byte
+	   encoding and returned */
+	if (frame->unread_char >= 0)
+	  {
+	    int c = frame->unread_char;
+	    int dim = frame->unread_bytes;
+	    unsigned char buf[MAX_MULTIBYTE_LENGTH];
+	    frame->unread_char = -1;
+	    frame->unread_bytes = 0;
+	    if (frame->multibyte && !frame->input.f.mule)
+	      {
+		/* only emacs-internal coding allowed
+		   for actual multibyte file streams
+		   Only case is reading from standard
+		   input in non-editor mode.
+		*/
+		int len = CHAR_STRING (c, buf);
+		eassume (len == dim);
+		for (int i = (len - 1); i >= 0; i--)
+		  ungetc (buf[i], frame->input.f.stream);
+	      }
+	    else if (!frame->multibyte)
+	      {
+		/* unibyte */
+	      }
+	    else
+	      {
+		/* emacs-mule encoding
+		   Least likely case last */
+		int sz = CHARSET_DIMENSION (frame->mule_charset);
+		unsigned code = ENCODE_CHAR (frame->mule_charset, c);
+		int len;
+		for (len = 0; sz > 0 && (code & 0xFF); sz--)
+		  {
+		    buf[len++] = (unsigned char) (code & 0xFF);
+		    code >>= 8;
+		  }
+		eassume (len == dim);
+		for (int i = (len - 1); i >= 0; i--)
+		  ungetc (buf[i], frame->input.f.stream);
+	      }
+	  }
+      }
       break;
     case LREAD_INPUT_INDIRECT:
       {
@@ -1853,33 +1757,6 @@ invalid_syntax (register lread_frame *frame, const char *s)
 static Lisp_Object substitute_object_recurse (struct subst *, Lisp_Object);
 static void substitute_in_interval (INTERVAL, void *);
 
-/* if an error was thrown during an interrupt and not caught, it shouldn't be handled
-   by this frame, but continue unwinding
- */
-Lisp_Object
-interrupt_error_handler (Lisp_Object data)
-{
-  /* data should always be a cons*/
-  eassert (CONSP (data));
-  if (EQ (XCAR (data), Qerror_during_interrupt))
-    Fsignal (XCAR (data),XCDR (data));
-  else
-    Fsignal (Qerror_during_interrupt, data);
-  eassume (false);
-}
-
-Lisp_Object
-base_interrupt_error_handler (Lisp_Object data)
-{
-  /* data should always be a cons*/
-  eassert (CONSP (data));
-  if (EQ (XCAR (data), Qerror_during_interrupt))
-    data = XCDR (data);
-  /* we have skipped all lread frames now */
-  eassert (CONSP (data));
-  Fsignal (XCAR (data), XCDR (data));
-  eassume (false);
-}
 
 
 /* Get a character from the tty.  */
@@ -2692,7 +2569,7 @@ DEFUN ("load", Fload, Sload, 1, 5, 0,
   init_lread_input_from_stream (&load_frame,
 				Qnil, Qnil, Qnil,
 				false, false, NULL, Qnil);
-  activate_lread_frame (&load_frame, LREAD_FRAME_MULTIPLE);
+  activate_lread_frame (&load_frame, LREAD_FRAME_INTEGRAL);
   lread_init_dynamic_frame (&load_frame);
 
 #if !defined USE_ANDROID_ASSETS
@@ -2953,7 +2830,7 @@ DEFUN ("load", Fload, Sload, 1, 5, 0,
   if (is_module)
     {
 #ifdef HAVE_MODULES
-      activate_lread_frame (&this_frame, LREAD_FRAME_MULTIPLE);
+      activate_lread_frame (&this_frame, LREAD_FRAME_INTEGRAL);
       loadhist_initialize (found);
       Fmodule_load (found);
       build_load_history (&this_frame,found, true);
@@ -2965,7 +2842,7 @@ DEFUN ("load", Fload, Sload, 1, 5, 0,
   else if (is_native_elisp)
     {
 #ifdef HAVE_NATIVE_COMP
-      activate_lread_frame (&this_frame, LREAD_FRAME_MULTIPLE);
+      activate_lread_frame (&this_frame, LREAD_FRAME_INTEGRAL);
       loadhist_initialize (hist_file_name);
       Fnative_elisp_load (found, Qnil);
       build_load_history (&this_frame,hist_file_name, true);
@@ -2977,15 +2854,12 @@ DEFUN ("load", Fload, Sload, 1, 5, 0,
     }
   else
     {
-      activate_lread_frame (&this_frame, LREAD_FRAME_MULTIPLE);
+      activate_lread_frame (&this_frame, LREAD_FRAME_INTEGRAL);
       set_unwind_protect_ptr (fd_index, close_infile_unwind, &this_frame);
       Fset (Qlexical_binding,
 	    get_lexical_binding(&this_frame, compiled ? Qnil : file));
 
-      internal_condition_case_1 (readevalloop_eof_trampoline,
-				 make_mint_ptr(&this_frame),
-				 list1 (Qerror_during_interrupt),
-				 base_interrupt_error_handler);
+      readevalloop_eof_trampoline (make_mint_ptr(&this_frame));
     }
   unbind_to (count, Qnil);
 
@@ -3546,13 +3420,13 @@ build_load_history (register lread_frame *frame, Lisp_Object filename, bool enti
 		  if (NILP (Fmember (newelt, tem)))
 		    Fsetcar (tail, Fcons (XCAR (tem),
 					  Fcons (newelt, XCDR (tem))));
-		  lread_maybe_quit (frame);
+		  maybe_quit ();
 		}
 	    }
 	}
       else
 	prev = tail;
-      lread_maybe_quit (frame);
+      maybe_quit ();
     }
 
   /* If we're loading an entire file, cons the new assoc onto the
@@ -3664,10 +3538,7 @@ buffer_point_marker (Lisp_Object buffer)
   if (buf == current_buffer)
     return Fpoint_marker();
   if (NILP (BVAR (buf, pt_marker)))
-    {
-      Lisp_Object tmp = Fmake_marker();
-      return Fset_marker (tmp, make_fixnum (buf->pt), buffer);
-    }
+    return build_marker (buf, BUF_PT (buf), BUF_PT_BYTE (buf));
   /* else */
   return Fcopy_marker (BVAR (buf, pt_marker), Qnil);
 }
@@ -3681,33 +3552,6 @@ setup_buffer_for_readevalloop (register lread_frame *frame,
   specpdl_ref count = SPECPDL_INDEX ();
   Lisp_Object buffer = lread_get_buffer_obj (frame);
   struct buffer *buf = NILP (buffer) ? NULL : XBUFFER (buffer);
-  if (NILP (end))
-    {
-      Lisp_Object tmp = Fmake_marker();
-      frame->end = Fset_marker (tmp, make_fixnum (BUF_Z (buf)),buffer);
-    }
-  else if (FIXNUMP (end))
-    {
-      Lisp_Object tmp = Fmake_marker();
-      frame->end = Fset_marker (tmp, end,buffer);
-    }
-  else if (!MARKERP (end))
-    xsignal1 (Qerror, CALLN (Fformat,
-			     build_string ("read: end argument must be nil, "
-			       "number, or marker: %S"),
-			     end));
-  else
-    {
-      if (XMARKER (end)->buffer == buf)
-	frame->end = end;
-      else
-	{
-	  Lisp_Object tmp = Fmake_marker ();
-	  frame->end = Fset_marker (tmp,
-				    make_fixnum (XMARKER(end)->charpos),
-				    buffer);
-	}
-    }
   if (NILP (start))
     {
       frame->start = buffer_point_marker (buffer);
@@ -3736,6 +3580,33 @@ setup_buffer_for_readevalloop (register lread_frame *frame,
 			   buffer);
 	}
     }
+  if (NILP (end))
+    {
+      Lisp_Object tmp = Fmake_marker();
+      frame->end = Fset_marker (tmp, make_fixnum (BUF_Z (buf)),buffer);
+    }
+  else if (FIXNUMP (end))
+    {
+      Lisp_Object tmp = Fmake_marker();
+      frame->end = Fset_marker (tmp, end,buffer);
+    }
+  else if (!MARKERP (end))
+    xsignal1 (Qerror, CALLN (Fformat,
+			     build_string ("read: end argument must be nil, "
+			       "number, or marker: %S"),
+			     end));
+  else
+    {
+      if (XMARKER (end)->buffer == buf)
+	frame->end = end;
+      else
+	{
+	  Lisp_Object tmp = Fmake_marker ();
+	  frame->end = Fset_marker (tmp,
+				    make_fixnum (XMARKER(end)->charpos),
+				    buffer);
+	}
+    }
   /* is restriction the whole buffer? */
   bool rv = ((XMARKER (frame->start)->charpos == BUF_BEG (buf))
 	     && (XMARKER (frame->end)->charpos == BUF_Z (buf)));
@@ -3743,22 +3614,36 @@ setup_buffer_for_readevalloop (register lread_frame *frame,
   return rv;
 }
 
-/* This procedure performs 2 functions:
-   Arrange for lisp-visible state of buffer at entry to read function to be
-       reinstated when the dynamic frame associated with the read/readevalloop
-       is unwound.
-   Set buffer state for use by read.
-   For recursively-reentrant read/eval, this has to be called at any (re)entry
-   to read, and unwound to perform any intervening eval as well as after
-   read0 has completed.
- */
-void
-prepare_buffer_for_interruptible_read (register lread_frame *frame)
+INLINE void
+check_live_input (register lread_frame *frame, struct buffer *buf)
 {
-  /* Lisp_Object buffer = lread_get_buffer_obj(frame); */
-  struct buffer *buf = lread_get_buffer(frame);
+  /* The only input type that eval might kill is a buffer */
   if (buf != 0 && !BUFFER_LIVE_P (buf))
     error ("Reading from killed buffer");
+
+}
+
+/* During "eval-region", readevalloop performs evaluations in 2 contexts
+   with respect to "current-buffer" and restrictions on it that might either
+   affect or be affected by the operation of reading the buffer.
+
+   The first is the evaluation context in which the "eval-region" is called,
+       and also in which the forms being read are called.
+   The second is the context in which the stream is read.
+
+   readevalloop treats the two contexts as if a sort of dynamic-wind surrounds
+   each:
+   (dynamic-wind wind-up-eval eval-context unwind-eval)
+   (dynamic-wind wind-up-read read-context unwind-read)
+
+   This procedure is a combination of "unwind-eval" and "wind-up-read",
+   but the "unwind-eval" has the form "unwind-protect wind-up-eval"
+ */
+void
+prepare_buffer_for_interruptible_read (register lread_frame *frame,
+				       struct buffer *buf)
+{
+  /* struct buffer *buf = lread_get_buffer(frame); */
   /* Switch to the buffer we are reading from.  */
   record_unwind_protect_excursion ();
   set_buffer_internal (buf);
@@ -3785,9 +3670,14 @@ nonstandard_read (Lisp_Object frameptr)
 {
   register lread_frame *frame = xmint_pointer (frameptr);
   Lisp_Object val;
+  Lisp_Object stream;
+  if (FROM_BUFFER_P (frame->intype))
+    stream = lread_get_buffer_obj (frame);
+  else
+    stream = Qreader_input_stream;
   if (!NILP (frame->readfun))
     {
-      val = calln (frame->readfun, Qreader_input_stream);
+      val = calln (frame->readfun, stream);
 
       /* If READCHARFUN has set point to ZV, we should
 	 stop reading, even if the form read sets point
@@ -3801,10 +3691,10 @@ nonstandard_read (Lisp_Object frameptr)
     }
   else
     {
-      /* ! NILP (Vload_read_function)
-	 || !EQ (Vload_read_function, Qread) */
-      val = calln (Vload_read_function, Qreader_input_stream);
+      /* ! NILP (Vload_read_function) */
+      val = calln (Vload_read_function, stream);
     }
+  frame->read_expr = true;
   return val;
 }
 
@@ -3850,8 +3740,8 @@ readevalloop (Lisp_Object frameptr)
   bool ignore_readfun = NILP (frame->readfun);
   bool use_read0 = compiled || will_dump_p()
     || (ignore_readfun
-	&& (NILP (Vload_read_function)
-	    || EQ (Vload_read_function, Qread)));
+	&& (NILP (Vload_read_function)));
+	    /* || EQ (Vload_read_function, Qread))); */
   Lisp_Object prev_Vload_read_function = Vload_read_function;
 
   macroexpand = Qinternal_macroexpand_for_load;
@@ -3866,7 +3756,13 @@ readevalloop (Lisp_Object frameptr)
   if (! NILP (frame->start) && !b)
     emacs_abort ();
 
-  specbind (Qstandard_input, Qreader_input_stream);
+  /* edebug's instrumentation mechanism wraps load-read-function
+     and requires the stream argument to be the current-buffer.
+     Just using Qreader_input_stream breaks this undocumented
+     assumption about the argument to load-read-function. */
+  Lisp_Object stream = FROM_BUFFER_P (frame->intype) ?
+    lread_get_buffer_obj (frame) : Qreader_input_stream;
+  specbind (Qstandard_input, stream);
 
   /* If lexical binding is active (either because it was specified in
      the file's header, or via a buffer-local variable), create an empty
@@ -3883,16 +3779,21 @@ readevalloop (Lisp_Object frameptr)
       setup_buffer_for_readevalloop (frame, frame->start, frame->end)
       || frame->whole_buffer;
 
+  /* the read dynamic frame must be initialized even in the stream is empty */
   lread_init_dynamic_frame (frame);
-
   while (!frame->read_eof)
     {
+
       frame->objects_map = Fclrhash (frame->objects_map);
       frame->objects_completed = Fclrhash (frame->objects_completed);
 
-      if (b)
-	prepare_buffer_for_interruptible_read (frame);
+      /* Check this whether the frame is integral or dynamic-wind */
+      check_live_input (frame, b);
+
+      if (b && frame->type == LREAD_FRAME_DYNAMIC_WIND)
+	prepare_buffer_for_interruptible_read (frame, b);
 
+      frame->read_expr = false;
       /* Handle the most common case first */
       /* not sure why the Vpurify_flag/will_dump_p case is special */
       if (use_read0)
@@ -3900,10 +3801,7 @@ readevalloop (Lisp_Object frameptr)
 	/* avoid condition case */
 	  lread_skip_insignificant (frame);
 	  if (frame->read_eof)
-	    {
-	      lread_unwind_for_eval (frame);
 	      break;
-	    }
 	  val = read0 (frame);
 	  frame->nexprs++;
 	}
@@ -3916,35 +3814,30 @@ readevalloop (Lisp_Object frameptr)
 					   make_mint_ptr (frame),
 					   list1 (Qend_of_file),
 					   lread_set_read_eof);
-	  if (frame->read_eof)
-	    {
-	      eassert (!UNINIT_SPECPDL_REF (frame->dynamic_frame));
-	      lread_unwind_for_eval (frame);
+	  if (frame->read_eof && !frame->read_expr)
 	      break;
-	    }
 	  frame->nexprs++;
 	}

-      if (b && !frame->read_eof)
-	{
-	  frame->start = Fpoint_marker ();
-	  frame->char_offset = XMARKER (frame->start)->charpos;
-	  frame->byte_offset = XMARKER (frame->start)->bytepos;
-	}
-
       /* Restore saved point and BEGV.  */
       eassert (!UNINIT_SPECPDL_REF (frame->dynamic_frame));
-      lread_unwind_for_eval (frame);
-
+      if (frame->type == LREAD_FRAME_DYNAMIC_WIND)
+	{
+	  if (b && !frame->read_eof && frame->type == LREAD_FRAME_DYNAMIC_WIND)
+	    {
+	      frame->start = Fpoint_marker ();
+	      frame->char_offset = XMARKER (frame->start)->charpos;
+	      frame->byte_offset = XMARKER (frame->start)->bytepos;
+	    }
+	  lread_unwind_for_eval (frame);
+	}
       /* Now eval what we just read.  */
       if (!NILP (macroexpand))
 	frame->value = readevalloop_eager_expand_eval (val, macroexpand);
       else
 	frame->value = eval_sub (val);
 
-      lread_unwind_for_eval (frame);
-
       /* check if the evaluation affected whether we can use the
 	 shortcut to read0 */
       if (!EQ (Vload_read_function, prev_Vload_read_function))
@@ -3952,8 +3845,8 @@ readevalloop (Lisp_Object frameptr)
 	  prev_Vload_read_function = Vload_read_function;
 	  use_read0 = compiled || will_dump_p ()
 	    || (ignore_readfun
-		&& (NILP (Vload_read_function)
-		    || EQ (Vload_read_function, Qread)));
+		&& (NILP (Vload_read_function)));
+		    /* || EQ (Vload_read_function, Qread))); */
 	}
 
       if (frame->printflag)
@@ -3965,7 +3858,13 @@ readevalloop (Lisp_Object frameptr)
 	    Fprint (frame->value, Qnil);
 	}
 
+      /* pop the read dynamic frame without actually unwinding */
+      frame->dynamic_frame = SPECPDL_REF_UNINIT;
+      lread_init_dynamic_frame (frame);
+
     }
+  if (frame->type == LREAD_FRAME_DYNAMIC_WIND)
+    lread_unwind_for_eval (frame);
   return frame->value;
 }
 
@@ -3983,11 +3882,8 @@ enter_stream_readevalloop (Lisp_Object stream)
 				false, false, NULL, Qnil);
   init_lread_eval_params(&this_frame, Qnil, Qnil,
 			 Qnil, false);
-  activate_lread_frame (&this_frame, LREAD_FRAME_MULTIPLE);
-  val = internal_condition_case_1 (readevalloop_eof_trampoline,
-				   make_mint_ptr (&this_frame),
-				   list1 (Qt),
-				   base_interrupt_error_handler);
+  activate_lread_frame (&this_frame, LREAD_FRAME_INTEGRAL);
+  val = readevalloop_eof_trampoline (make_mint_ptr (&this_frame));
   return unbind_to (this_frame.loop_dynamic_frame, val);
 }
 
@@ -4005,6 +3901,8 @@ enter_buffer_readevalloop (Lisp_Object buffer,
   specpdl_ref count = SPECPDL_INDEX ();
   lread_frame this_frame = INIT_LREAD_FRAME();
   Lisp_Object full_name;
+  lread_frame_type eval_type = whole_buffer ?
+    LREAD_FRAME_INTEGRAL : LREAD_FRAME_DYNAMIC_WIND;
   if (NILP (buffer))
     buf = Fcurrent_buffer ();
   else
@@ -4028,7 +3926,7 @@ enter_buffer_readevalloop (Lisp_Object buffer,
   init_lread_input_from_stream (&this_frame,
 				buf,
 				start, end,
-				false, true, NULL,Qnil);
+				false, true, NULL, Qnil);
   init_lread_eval_params(&this_frame, full_name, filename,
 			 read_function, whole_buffer);
   /* note at this stage, whole_buffer is determined by the caller
@@ -4045,14 +3943,14 @@ enter_buffer_readevalloop (Lisp_Object buffer,
     {
       record_unwind_protect_excursion ();
       BUF_TEMP_SET_PT (XBUFFER (buf), BUF_BEGV (XBUFFER (buf)));
-      specbind (Qlexical_binding, get_lexical_binding (&this_frame, buf));
+      /* Don't emit a warning about 'lexical-binding' if it already has a
+	 local binding in the buffer.  */
+      if (NILP (Flocal_variable_p (Qlexical_binding, buf)))
+	specbind (Qlexical_binding, get_lexical_binding (&this_frame, buf));
       BUF_TEMP_SET_PT (XBUFFER (buf), BUF_BEGV (XBUFFER (buf)));
     }
-  activate_lread_frame (&this_frame, LREAD_FRAME_MULTIPLE);
-  val = internal_condition_case_1 (readevalloop_eof_trampoline,
-				   make_mint_ptr (&this_frame),
-				   list1 (Qt),
-				   base_interrupt_error_handler);
+  activate_lread_frame (&this_frame, eval_type);
+  val = readevalloop_eof_trampoline (make_mint_ptr (&this_frame));
   return unbind_to (count, val);
 }
 
@@ -4117,7 +4015,8 @@ DEFUN ("eval-region", Feval_region, Seval_region, 2, 4, "r",
 which is the input stream for reading characters.
 
 This function does not move point.  */)
-  (Lisp_Object start, Lisp_Object end, Lisp_Object printflag, Lisp_Object read_function)
+  (Lisp_Object start, Lisp_Object end, Lisp_Object printflag,
+   Lisp_Object read_function)
 {
   return enter_buffer_readevalloop (Qnil, printflag, Qnil,
 				    Qnil, start, end, read_function,
@@ -4144,17 +4043,7 @@ enter_read (Lisp_Object stream, Lisp_Object start, Lisp_Object end,
     *char_offset = this_frame.char_offset;
   Lisp_Object data = Qnil;
   /* Fread may be called while loading the dump file */
-  if (initialized || will_dump_p())
-    {
-      data = internal_condition_case_1 (call_read0,
-					make_mint_ptr (&this_frame),
-					Qerror_during_interrupt,
-					base_interrupt_error_handler);
-    }
-  else
-    {
-      data = read0 (&this_frame);
-    }
+  data = read0 ( &this_frame);
   if (char_offset)
     *char_offset = this_frame.char_offset;
    unbind_to (this_frame.loop_dynamic_frame, Qnil);
-- 
2.49.0