[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: master 5252c45: Initialise unread buffer

From: Mattias Engdegård
Subject: Re: master 5252c45: Initialise unread buffer
Date: Mon, 20 Sep 2021 12:16:24 +0200

19 sep. 2021 kl. 17.41 skrev Lars Ingebrigtsen <larsi@gnus.org>:

> I'm not absolutely sure that it's your change that's causing this weird
> error, but it seemed the most likely.  :-/  And reverting back to the
> checkout before yours made the problem go away.

It's more fun actually. Thanks to the fixed reader bug, the lexical-binding 
cookie is now honoured in loadup.el. That causes Emacs to be dumped with 
`lexical-binding` set to t which as a consequence becomes the new default.

It's a testament to the sterling work by Stefan, Stefan, you, and everybody 
else that Emacs seems to work almost flawlessly anyway, but as it really wasn't 
our intention to do the switch now I've pushed an explicit binding of 
lexical-binding around the actual dumping so we're now back to normal.

What about the failing eval-tests then? They check, among other things, that

 (lambda (&rest) 'ok)

can be interpreted, byte-compiled, and returns `ok` when called. This fails 
with lexical binding enabled but works with dynamic binding, so when the 
default was inadvertently switched to lexical, the test started failing.

But wait, you protest. eval-tests.el is marked with -*- lexical-binding: t -*-. 
Surely? No?

Actually, any code in `ert-deftest` uses dynamic binding (until the accidental 
switch), regardless of the file cookie. Not many people know that.

So what is this bug that is still present? Let's take a look:

(byte-compile (lambda (&rest) 'ok))
#[128 "\300\207" [ok] 1 "..."]

Let's pick it apart:
- The number 128 means that there are no positional args but a single &rest 
- The "\300\207" are the two byte-ops
   constant ok   ; push `ok` on the stack
   return        ; return the value on top of the stack
- The number 1 is the maximum stack size required.

When byte-code execution starts, the arguments are pushed on the stack -- in 
this case, a single nil for the &rest. The first byte-op then pushes the 
constant `ok`. But the stack is only sized for one element so that overwrites 
what follows, which is a copy of the byte-code. The smashed ops are then 
executed: if we are lucky we get an error, if unlucky an Emacs abort or crash.

I'm not sure why the byte-code interpreter makes a copy of the byte code for 
each function call. It certainly doesn't help the lamentably high function call 
overhead a bit. Most likely it's programmer, not code, efficiency that was 
optimised: since GC can move string data, the pc would need to be rebased each 
time that can occur, and that might need to be done in a lot of places. We 
should probably do something about that.

If the argument is named, then we get:

(byte-compile (lambda (&rest _x) 'ok))
#[128 "\300\207" [ok] 2 "..."]

Note the max depth 2 here which gives a sufficiently big stack.

There's a number of things to fix here, but this message is too long already. 
Thanks for your interest! There are books for sale in the foyer.

reply via email to

[Prev in Thread] Current Thread [Next in Thread]