* bug#3984: 23.0.96; defadvice of call-interactively defeats interactive-p
@ 2009-07-30 22:37 Drew Adams
2009-07-31 1:58 ` Stefan Monnier
` (4 more replies)
0 siblings, 5 replies; 37+ messages in thread
From: Drew Adams @ 2009-07-30 22:37 UTC (permalink / raw)
To: emacs-pretest-bug
emacs -Q
Load (eval) this:
(defun foo ()
(interactive)
(if (interactive-p)
(message "INT")
(message "NOT")))
(global-set-key "\C-l" 'foo)
(defadvice call-interactively (after foo-advice disable activate)
(message "AFTER"))
(ad-enable-advice 'call-interactively 'after 'foo-advice)
(ad-activate 'call-interactively)
Then hit `C-l'.
In *Messages*, you will see this:
NOT
AFTER
Even though `foo' is called interactively, `interactive-p' returns
nil. Using `called-interactively-p' in place of `interactive-p' gives
the same thing. Same thing no matter how `foo' is called interactively
(e.g. M-x foo, M-: (call-interactively 'foo)).
Seems like a bug, but I realize that advising primitives is iffy, and
perhaps advising `call-interactively' is even more iffy.
However, advising `call-interactively' seems to work fine otherwise -
this is the only anomaly I've come across.
Can someone please explain why this happens, or how to work around it?
In GNU Emacs 23.0.96.1 (i386-mingw-nt5.1.2600)
of 2009-07-09 on SOFT-MJASON
Windowing system distributor `Microsoft Corp.', version 5.1.2600
configured using `configure --with-gcc (3.4)'
^ permalink raw reply [flat|nested] 37+ messages in thread
* bug#3984: 23.0.96; defadvice of call-interactively defeats interactive-p
2009-07-30 22:37 bug#3984: 23.0.96; defadvice of call-interactively defeats interactive-p Drew Adams
@ 2009-07-31 1:58 ` Stefan Monnier
2009-07-31 14:19 ` Drew Adams
2011-10-10 6:00 ` Kai Tetzlaff
` (3 subsequent siblings)
4 siblings, 1 reply; 37+ messages in thread
From: Stefan Monnier @ 2009-07-31 1:58 UTC (permalink / raw)
To: Drew Adams; +Cc: emacs-pretest-bug, 3984
> Can someone please explain why this happens, or how to work around it?
Because the implementation of interactive-p (and called-interactively-p)
is brittle: it looks at the latest stack frames to see the name of the
caller, so if you add things between the call to `call-interactively'
and the corresponding function call, it gets confused.
To work around it, don't use `interactive-p' and instead add an optional
argument (call it `interactive') to your function and pass it an
explicit non-nil value from the interactive spec.
Stefan
^ permalink raw reply [flat|nested] 37+ messages in thread
* bug#3984: 23.0.96; defadvice of call-interactively defeats interactive-p
2009-07-31 1:58 ` Stefan Monnier
@ 2009-07-31 14:19 ` Drew Adams
2009-07-31 19:31 ` Stefan Monnier
0 siblings, 1 reply; 37+ messages in thread
From: Drew Adams @ 2009-07-31 14:19 UTC (permalink / raw)
To: 'Stefan Monnier'; +Cc: emacs-pretest-bug, 3984
> > Can someone please explain why this happens, or how to work
> around it?
>
> Because the implementation of interactive-p (and
> called-interactively-p)
> is brittle: it looks at the latest stack frames to see the name of the
> caller, so if you add things between the call to `call-interactively'
> and the corresponding function call, it gets confused.
>
> To work around it, don't use `interactive-p' and instead add
> an optional
> argument (call it `interactive') to your function and pass it an
> explicit non-nil value from the interactive spec.
Unfortunately, the code with `interactive-p' is not mine. The defadvice is mine,
but it needs to work for user functions, including those that call
`interactive-p' or `called-interactively-p'.
Is there no way the Emacs implementation could be fixed to handle this better?
Couldn't it take into consideration the `ad-*' stuff that results from advising
`call-interactively'? IOW, couldn't it look for `call-interactively' in its
advised form also?
^ permalink raw reply [flat|nested] 37+ messages in thread
* bug#3984: 23.0.96; defadvice of call-interactively defeats interactive-p
2009-07-31 14:19 ` Drew Adams
@ 2009-07-31 19:31 ` Stefan Monnier
2009-07-31 20:04 ` Drew Adams
0 siblings, 1 reply; 37+ messages in thread
From: Stefan Monnier @ 2009-07-31 19:31 UTC (permalink / raw)
To: Drew Adams; +Cc: emacs-pretest-bug, 3984
> Unfortunately, the code with `interactive-p' is not mine.
> The defadvice is mine, but it needs to work for user functions,
> including those that call `interactive-p' or `called-interactively-p'.
> Is there no way the Emacs implementation could be fixed to handle this
> better? Couldn't it take into consideration the `ad-*' stuff that
> results from advising `call-interactively'? IOW, couldn't it look for
> `call-interactively' in its advised form also?
There's probably some way to make it work, of course. Note that the
same problem is likely to appear with other redefinitions of
call-interactively (e.g. profiling, tracing, ...).
Of course, another way to break these things is also to advise (and/or
profile/trace/...) interactive-p.
Stefan
^ permalink raw reply [flat|nested] 37+ messages in thread
* bug#3984: 23.0.96; defadvice of call-interactively defeats interactive-p
2009-07-31 19:31 ` Stefan Monnier
@ 2009-07-31 20:04 ` Drew Adams
0 siblings, 0 replies; 37+ messages in thread
From: Drew Adams @ 2009-07-31 20:04 UTC (permalink / raw)
To: 'Stefan Monnier'; +Cc: emacs-pretest-bug, 3984
> > Is there no way the Emacs implementation could be fixed to
> > handle this better? Couldn't it take into consideration
> > the `ad-*' stuff that results from advising
> > `call-interactively'? IOW, couldn't it look for
> > `call-interactively' in its advised form also?
>
> There's probably some way to make it work, of course. Note that the
> same problem is likely to appear with other redefinitions of
> call-interactively (e.g. profiling, tracing, ...).
Yes.
> Of course, another way to break these things is also to advise (and/or
> profile/trace/...) interactive-p.
Yes.
Seems like there could be a list of such functions to check wrt advised (and
profiled, etc.) forms. The particular advice wouldn't matter, I believe; I
imagine that all that happens is that the function is currently checked.
Couldn't the check simply be membership in a list that includes the advised
names? Perhaps even have that as the value of a variable, which could be
configured (at least by Emacs dev, if not users).
Realistically, what do you think is the chance of at least the bug as reported
being fixed, that is, at least for advised `call-interactively'? I added a
feature to Icicles that I will likely need to remove if `interactive-p' is not
made to behave normally. Thx.
^ permalink raw reply [flat|nested] 37+ messages in thread
* bug#3984: 23.0.96; defadvice of call-interactively defeats interactive-p
2009-07-30 22:37 bug#3984: 23.0.96; defadvice of call-interactively defeats interactive-p Drew Adams
2009-07-31 1:58 ` Stefan Monnier
@ 2011-10-10 6:00 ` Kai Tetzlaff
2011-10-11 14:26 ` Drew Adams
2013-09-10 20:29 ` Christopher Wellons
` (2 subsequent siblings)
4 siblings, 1 reply; 37+ messages in thread
From: Kai Tetzlaff @ 2011-10-10 6:00 UTC (permalink / raw)
To: 3984
I'm using icicles and just ran into this bug with emacs compiled from
bzr. The function count-words-region has been changed to use
called-interactively-p to check for interactive use. As reported, the
check fails when icicles is turned on. As there are >300 places under
the emacs lisp/ directory alone which use called-interactively-p, it
would really be great, if it could be made more robust.
In GNU Emacs 24.0.90.6 (x86_64-apple-darwin10.8.0, NS apple-appkit-1038.36)
of 2011-10-09 on mack.tetzco.de
Windowing system distributor `Apple', version 10.3.1038
configured using `configure 'CC=clang' 'CFLAGS=-g -O0' 'LDFLAGS=-g' '--with-ns' '--with-gnutls''
^ permalink raw reply [flat|nested] 37+ messages in thread
* bug#3984: 23.0.96; defadvice of call-interactively defeats interactive-p
2011-10-10 6:00 ` Kai Tetzlaff
@ 2011-10-11 14:26 ` Drew Adams
2011-10-11 15:46 ` Stefan Monnier
0 siblings, 1 reply; 37+ messages in thread
From: Drew Adams @ 2011-10-11 14:26 UTC (permalink / raw)
To: 'Kai Tetzlaff', 3984
> From: Kai Tetzlaff Sent: Sunday, October 09, 2011 11:00 PM
>
> I'm using icicles and just ran into this bug with emacs compiled from
> bzr. The function count-words-region has been changed to use
> called-interactively-p to check for interactive use. As reported, the
> check fails when icicles is turned on. As there are >300 places under
> the emacs lisp/ directory alone which use called-interactively-p, it
> would really be great, if it could be made more robust.
For info, this is the defadvice that exposes the `called-interactively' bug
(#3984):
(defadvice call-interactively (after icicle-save-to-history
disable activate)
"Save command to `icicle-interactive-history'."
;; If command's input is not a parameterized (e.g. mouse)
;; event, record it.
(let* ((fn (ad-get-arg 0))
(int (interactive-form fn)))
(when (and (symbolp fn)
(consp int)
(or (not (stringp (cadr int)))
(string= (cadr int) "")
(not (eq ?e (aref (cadr int) 0)))))
(pushnew (symbol-name fn) icicle-interactive-history))))
This behavior is optional, so Icicles users can turn it off (it is off by
default) via option `icicle-populate-interactive-history-flag'. And the doc
string of that option refers to Emacs bug #3948. Still, it would be good to get
this Emacs bug fixed - especially, as Kai remarks, since the Emacs source code
now uses `called-interactively-p' all over the place.
^ permalink raw reply [flat|nested] 37+ messages in thread
* bug#3984: 23.0.96; defadvice of call-interactively defeats interactive-p
2011-10-11 14:26 ` Drew Adams
@ 2011-10-11 15:46 ` Stefan Monnier
2011-10-11 16:05 ` Drew Adams
0 siblings, 1 reply; 37+ messages in thread
From: Stefan Monnier @ 2011-10-11 15:46 UTC (permalink / raw)
To: Drew Adams; +Cc: 3984, 'Kai Tetzlaff'
> string of that option refers to Emacs bug #3948. Still, it would be good to get
> this Emacs bug fixed - especially, as Kai remarks, since the Emacs source code
> now uses `called-interactively-p' all over the place.
I don't understand the "now" in the above sentence. AFAIK these uses
are not new (tho they used `interactive-p' in the past, but that should
not make any difference).
Stefan
^ permalink raw reply [flat|nested] 37+ messages in thread
* bug#3984: 23.0.96; defadvice of call-interactively defeats interactive-p
2011-10-11 15:46 ` Stefan Monnier
@ 2011-10-11 16:05 ` Drew Adams
0 siblings, 0 replies; 37+ messages in thread
From: Drew Adams @ 2011-10-11 16:05 UTC (permalink / raw)
To: 'Stefan Monnier'; +Cc: 3984, 'Kai Tetzlaff'
> > string of that option refers to Emacs bug #3948. Still, it
> > would be good to get this Emacs bug fixed - especially, as
> > Kai remarks, since the Emacs source code now uses
> > `called-interactively-p' all over the place.
>
> I don't understand the "now" in the above sentence. AFAIK these uses
> are not new (tho they used `interactive-p' in the past, but
> that should not make any difference).
Whether it matters, for this bug, whether the Emacs code uses
`called-interactively-p' or `interactive-p' I don't know. I trust your guess
that it should not make any difference. Feel free to remove "now" from the
sentence.
The point is to please fix the bug. Thx.
^ permalink raw reply [flat|nested] 37+ messages in thread
* bug#3984: 23.0.96; defadvice of call-interactively defeats interactive-p
2009-07-30 22:37 bug#3984: 23.0.96; defadvice of call-interactively defeats interactive-p Drew Adams
2009-07-31 1:58 ` Stefan Monnier
2011-10-10 6:00 ` Kai Tetzlaff
@ 2013-09-10 20:29 ` Christopher Wellons
2013-09-11 0:29 ` Stefan Monnier
2013-09-13 8:56 ` bug#3984: Fix for #3984 Ryan
2013-09-13 10:24 ` bug#3984: bug#123: Potential fix Ryan
4 siblings, 1 reply; 37+ messages in thread
From: Christopher Wellons @ 2013-09-10 20:29 UTC (permalink / raw)
To: 3984
Running into this bug with ido-ubiquitous and Emacs 24.3.1:
https://github.com/DarwinAwardWinner/ido-ubiquitous/issues/24
^ permalink raw reply [flat|nested] 37+ messages in thread
* bug#3984: 23.0.96; defadvice of call-interactively defeats interactive-p
2013-09-10 20:29 ` Christopher Wellons
@ 2013-09-11 0:29 ` Stefan Monnier
0 siblings, 0 replies; 37+ messages in thread
From: Stefan Monnier @ 2013-09-11 0:29 UTC (permalink / raw)
To: Christopher Wellons; +Cc: 3984
> Running into this bug with ido-ubiquitous and Emacs 24.3.1:
> https://github.com/DarwinAwardWinner/ido-ubiquitous/issues/24
It's unlikely to get fixed, as you can guess from the fact that it hasn't
been fixed yet.
You might get better results using alternative approaches, based on
pre/post-command-hook or maybe advising some other function
(command-execute, for example).
Stefan
^ permalink raw reply [flat|nested] 37+ messages in thread
* bug#3984: Fix for #3984
2009-07-30 22:37 bug#3984: 23.0.96; defadvice of call-interactively defeats interactive-p Drew Adams
` (2 preceding siblings ...)
2013-09-10 20:29 ` Christopher Wellons
@ 2013-09-13 8:56 ` Ryan
2013-09-13 13:18 ` Stefan Monnier
2013-09-13 10:24 ` bug#3984: bug#123: Potential fix Ryan
4 siblings, 1 reply; 37+ messages in thread
From: Ryan @ 2013-09-13 8:56 UTC (permalink / raw)
To: 3984
Dear all,
I "fixed" this in my ido-ubiquitous package by completely reimplementing
"called-interactively-p" and "interactive-p" in pure elisp:
https://github.com/DarwinAwardWinner/ido-ubiquitous/commit/f0c42e289a614071e22ad2c08297a7ebd60ba1cc
Apart from simply translating the C code in elisp, I made two key
adjustments to the logic: first, I filter out all evidence of advice
from the call stack before checking if the caller is
"call-interactively". Second, I relax the definition of "caller is
call-interactively" to include any symbol with the same
"symbol-function" as call-interactively, or any function that is the
same as the symbol-function of call-interactively. Combined, these
adjustments mean that defining advice on call-interactively no longer
results in erroneous return values from these two interactivity-testing
functions.
I have implemented this in elisp because that is the only way to
redefine functions in a running emacs session, but there's no reason
that the C code couldn't be adapted to use the same logic.
-Ryan
^ permalink raw reply [flat|nested] 37+ messages in thread
* bug#3984: bug#123: Potential fix
2009-07-30 22:37 bug#3984: 23.0.96; defadvice of call-interactively defeats interactive-p Drew Adams
` (3 preceding siblings ...)
2013-09-13 8:56 ` bug#3984: Fix for #3984 Ryan
@ 2013-09-13 10:24 ` Ryan
4 siblings, 0 replies; 37+ messages in thread
From: Ryan @ 2013-09-13 10:24 UTC (permalink / raw)
To: 3984
Dear all,
I "fixed" this in my ido-ubiquitous package by completely reimplementing
"called-interactively-p" and "interactive-p" in pure elisp:
https://github.com/DarwinAwardWinner/ido-ubiquitous/commit/f0c42e289a614071e22ad2c08297a7ebd60ba1cc
Apart from simply translating the C code in elisp, I made two key
adjustments to the logic: first, I filter out all evidence of advice
from the call stack before checking if the caller is
"call-interactively". Second, I relax the definition of "caller is
call-interactively" to include any symbol with the same
"symbol-function" as call-interactively, or any function that is the
same as the symbol-function of call-interactively. Combined, these
adjustments mean that defining advice on call-interactively no longer
results in erroneous return values from these two interactivity-testing
functions.
I have implemented this in elisp because that is the only way to
redefine functions in a running emacs session, but there's no reason
that the C code couldn't be adapted to use the same logic.
-Ryan
^ permalink raw reply [flat|nested] 37+ messages in thread
* bug#3984: Fix for #3984
2013-09-13 8:56 ` bug#3984: Fix for #3984 Ryan
@ 2013-09-13 13:18 ` Stefan Monnier
2013-09-13 18:30 ` Ryan
0 siblings, 1 reply; 37+ messages in thread
From: Stefan Monnier @ 2013-09-13 13:18 UTC (permalink / raw)
To: Ryan; +Cc: 3984
> I "fixed" this in my ido-ubiquitous package by completely reimplementing
> "called-interactively-p" and "interactive-p" in pure elisp:
FWIW, called-interactively-p is already implemented in Elisp in
Emacs's trunk.
Stefan
^ permalink raw reply [flat|nested] 37+ messages in thread
* bug#3984: Fix for #3984
2013-09-13 13:18 ` Stefan Monnier
@ 2013-09-13 18:30 ` Ryan
2013-09-13 19:27 ` Ryan
0 siblings, 1 reply; 37+ messages in thread
From: Ryan @ 2013-09-13 18:30 UTC (permalink / raw)
To: Stefan Monnier; +Cc: 3984
Looking at the code in trunk, I see that there is a special hook for
functions to decide which stack frames to skip over when looking for
call-interactively. I still think they should relax the test for
equality to "equal indirect-functions" instead of exactly the symbol
call-interactively.
On Fri Sep 13 06:18:50 2013, Stefan Monnier wrote:
>> I "fixed" this in my ido-ubiquitous package by completely reimplementing
>> "called-interactively-p" and "interactive-p" in pure elisp:
>
> FWIW, called-interactively-p is already implemented in Elisp in
> Emacs's trunk.
>
>
> Stefan
^ permalink raw reply [flat|nested] 37+ messages in thread
* bug#3984: Fix for #3984
2013-09-13 18:30 ` Ryan
@ 2013-09-13 19:27 ` Ryan
2013-09-13 21:02 ` Stefan Monnier
0 siblings, 1 reply; 37+ messages in thread
From: Ryan @ 2013-09-13 19:27 UTC (permalink / raw)
To: Stefan Monnier; +Cc: 3984
On Fri Sep 13 11:30:37 2013, Ryan wrote:
> Looking at the code in trunk, I see that there is a special hook for
> functions to decide which stack frames to skip over when looking for
> call-interactively. I still think they should relax the test for
> equality to "equal indirect-functions" instead of exactly the symbol
> call-interactively.
Actually, I just noticed that in trunk, nadvice.el adds a function to
"called-interactively-p-functions" to skip advice-related stack frames,
but this works only for advice on the interactive function, not advice
defined on call-interactively itself. Furthermore, from my limited
testing it appears that the structure of the call stack for advised
functions has changes significantly in trunk, making my code obsolete.
The whole thing looks like a work in progress right now.
^ permalink raw reply [flat|nested] 37+ messages in thread
* bug#3984: Fix for #3984
2013-09-13 19:27 ` Ryan
@ 2013-09-13 21:02 ` Stefan Monnier
2013-09-17 3:18 ` Ryan
0 siblings, 1 reply; 37+ messages in thread
From: Stefan Monnier @ 2013-09-13 21:02 UTC (permalink / raw)
To: Ryan; +Cc: 3984
>> Looking at the code in trunk, I see that there is a special hook for
>> functions to decide which stack frames to skip over when looking for
>> call-interactively. I still think they should relax the test for
>> equality to "equal indirect-functions" instead of exactly the symbol
>> call-interactively.
The code does check "equal modulo indirect-functions" in some cases, but
indeed not all. I don't think that replacing the equality check against
`call-interactively' with a check modulo indirect-functions would solve
your problem, tho (that only helps when calling though an alias of
call-interactively, but here the relevant stack frame will be a call to
the #<subr call-interactively> which is not
equal-modulo-indirect-functions to call-interactively since
call-interactively has been redefined to a different functions by the
advice).
You can probably use called-interactively-p-functions to detect the
#<subr call-interactively> and skip the frames between it and the
corresponding call to `call-interactively'.
But if you find a cute patch against the current code which makes it
work for you in a cleanish way, do send it here, to see if it can
be included.
> Actually, I just noticed that in trunk, nadvice.el adds a function to
> "called-interactively-p-functions" to skip advice-related stack frames, but
> this works only for advice on the interactive function, not advice defined
> on call-interactively itself.
Indeed. It doesn't even work for all advices (more specifically it
doesn't work for :around advices, which means it doesn't work for
advices defined via `defadvice' since these all turn into one
big :around "new advice").
> Furthermore, from my limited testing it appears that the structure of
> the call stack for advised functions has changes significantly in
> trunk, making my code obsolete.
Indeed, the implementation of advices has been completely changed.
> The whole thing looks like a work in progress right now.
There's no planned change to it, so I consider it "ready modulo
bug-reports". AFAIK it works "at least as well as before" (it works
better than before in the sense that Edebugging a function with calls
to called-interactively-p should now work correctly).
`called-interactively-p' is a big ugly hack and only works sometimes.
It can break in all kinds of cases (e.g. it currently won't work in
byte-compiled lexical-binding code within a `catch', or
a `condition-case', or the unwind part of an `unwind-protect'). Also,
the functions called (non-interactively, obviously) by your
`call-interactively' advice will probably think that they're called
interactively (hopefully your advice doesn't call many functions, and
hopefully none of them cares whether it's called interactively or not).
Stefan
^ permalink raw reply [flat|nested] 37+ messages in thread
* bug#3984: Fix for #3984
2013-09-13 21:02 ` Stefan Monnier
@ 2013-09-17 3:18 ` Ryan
2013-09-17 13:10 ` Stefan Monnier
0 siblings, 1 reply; 37+ messages in thread
From: Ryan @ 2013-09-17 3:18 UTC (permalink / raw)
To: Stefan Monnier; +Cc: 3984
Ok, I think I figured out how to do it for all advice, but feel free to
poke holes in my idea before I get to far implementing it.
We have "ad-is-advised" which we can use to find which stack frames
correspond to advised functions. We have "ad-get-orig-definition" which
we can use to find the original definition of an advised function. We
can tell if there is any active advice by testing if the advised
function's definition is equal to the original definition. If the
function has active advice, we can search down the stack for the
original definition of that function and skip over all the stack frames
in between.
Unfortunately, this requires a search through the call stack frames in
the wrong order, so the best strategy may be to make a single pass
through the call stack, finding all pairs of advised/original function
stack frames, and then cache the result in a dynamically bound variable
for the duration of the call to "called-interactively-p". This still
requires a complete pass through the entire call stack for each call to
"called-interactively-p", which defeats the current "lazy-evaluation"
strategy of inspecting the frames one by one, although I guess we can
stop at the first occurrence of "call-interactively" on the stack. But I
doubt that "called-interactively-p" is a performance hotspot anyway, so
this may be acceptable.
What do you think?
-Ryan
On 9/13/13 2:02 PM, Stefan Monnier wrote:
>>> Looking at the code in trunk, I see that there is a special hook for
>>> functions to decide which stack frames to skip over when looking for
>>> call-interactively. I still think they should relax the test for
>>> equality to "equal indirect-functions" instead of exactly the symbol
>>> call-interactively.
> The code does check "equal modulo indirect-functions" in some cases, but
> indeed not all. I don't think that replacing the equality check against
> `call-interactively' with a check modulo indirect-functions would solve
> your problem, tho (that only helps when calling though an alias of
> call-interactively, but here the relevant stack frame will be a call to
> the #<subr call-interactively> which is not
> equal-modulo-indirect-functions to call-interactively since
> call-interactively has been redefined to a different functions by the
> advice).
>
> You can probably use called-interactively-p-functions to detect the
> #<subr call-interactively> and skip the frames between it and the
> corresponding call to `call-interactively'.
>
> But if you find a cute patch against the current code which makes it
> work for you in a cleanish way, do send it here, to see if it can
> be included.
>
>> Actually, I just noticed that in trunk, nadvice.el adds a function to
>> "called-interactively-p-functions" to skip advice-related stack frames, but
>> this works only for advice on the interactive function, not advice defined
>> on call-interactively itself.
> Indeed. It doesn't even work for all advices (more specifically it
> doesn't work for :around advices, which means it doesn't work for
> advices defined via `defadvice' since these all turn into one
> big :around "new advice").
>
>> Furthermore, from my limited testing it appears that the structure of
>> the call stack for advised functions has changes significantly in
>> trunk, making my code obsolete.
> Indeed, the implementation of advices has been completely changed.
>
>> The whole thing looks like a work in progress right now.
> There's no planned change to it, so I consider it "ready modulo
> bug-reports". AFAIK it works "at least as well as before" (it works
> better than before in the sense that Edebugging a function with calls
> to called-interactively-p should now work correctly).
>
> `called-interactively-p' is a big ugly hack and only works sometimes.
> It can break in all kinds of cases (e.g. it currently won't work in
> byte-compiled lexical-binding code within a `catch', or
> a `condition-case', or the unwind part of an `unwind-protect'). Also,
> the functions called (non-interactively, obviously) by your
> `call-interactively' advice will probably think that they're called
> interactively (hopefully your advice doesn't call many functions, and
> hopefully none of them cares whether it's called interactively or not).
>
>
> Stefan
^ permalink raw reply [flat|nested] 37+ messages in thread
* bug#3984: Fix for #3984
2013-09-17 3:18 ` Ryan
@ 2013-09-17 13:10 ` Stefan Monnier
2013-09-17 17:22 ` bug#3984: Ryan
0 siblings, 1 reply; 37+ messages in thread
From: Stefan Monnier @ 2013-09-17 13:10 UTC (permalink / raw)
To: Ryan; +Cc: 3984
> We have "ad-is-advised" which we can use to find which stack frames
> correspond to advised functions. We have "ad-get-orig-definition" which we
> can use to find the original definition of an advised function.
These are functions of advice.el, which is on the way out. We need to
look at nadvice.el.
Thinking about it once more, I realize that your "advised
call-interactively" case might indeed be resolved by the current code,
except that it bumps into the problematic case of "deepest advice is
using :around".
Could you check if it is the case simply be doing a dummy (advice-add
'call-interactively :before #'ignore) before your defadvice?
If that works, then we can probably fix that problem in the following way:
- keep track of every function value (e.g. #<subr call-interactively> in
our case) that is wrapped (at the deepest level) in an :around advice
(e.g. in a hash-table).
- when walking up the stack, if we find such a function, look up the
stack searching for the matching function symbol (just like we already
do when encountering an internal advice element on the stack).
Stefan
^ permalink raw reply [flat|nested] 37+ messages in thread
* bug#3984:
2013-09-17 13:10 ` Stefan Monnier
@ 2013-09-17 17:22 ` Ryan
2013-09-18 1:46 ` bug#3984: Stefan Monnier
0 siblings, 1 reply; 37+ messages in thread
From: Ryan @ 2013-09-17 17:22 UTC (permalink / raw)
To: Stefan Monnier; +Cc: 3984
On 9/17/13 6:10 AM, Stefan Monnier wrote:
>> We have "ad-is-advised" which we can use to find which stack frames
>> correspond to advised functions. We have "ad-get-orig-definition" which we
>> can use to find the original definition of an advised function.
> These are functions of advice.el, which is on the way out. We need to
> look at nadvice.el.
Wait, so defadvice and friends are going away entirely? Is everything in
advice.el going away, or are some parts of it going to be converted to
the new advice system? Is all this documented somewhere?
>
> Thinking about it once more, I realize that your "advised
> call-interactively" case might indeed be resolved by the current code,
> except that it bumps into the problematic case of "deepest advice is
> using :around".
>
> Could you check if it is the case simply be doing a dummy (advice-add
> 'call-interactively :before #'ignore) before your defadvice?
>
> If that works, then we can probably fix that problem in the following way:
> - keep track of every function value (e.g. #<subr call-interactively> in
> our case) that is wrapped (at the deepest level) in an :around advice
> (e.g. in a hash-table).
> - when walking up the stack, if we find such a function, look up the
> stack searching for the matching function symbol (just like we already
> do when encountering an internal advice element on the stack).
>
>
> Stefan
I tried the before advice, and it doesn't seem to work. With the
following code in emacs -Q:
(advice-add 'call-interactively :before #'ignore)
(defun myfun2 ()
(interactive)
(or (called-interactively-p)
(error "Must be interactive!")))
(call-interactively 'myfun2)
I get the following stack trace:
Debugger entered--Lisp error: (error "Must be interactive!")
signal(error ("Must be interactive!"))
error("Must be interactive!")
(or (called-interactively-p) (error "Must be interactive!"))
myfun2()
#<subr call-interactively>(myfun2)
apply(#<subr call-interactively> myfun2)
call-interactively(myfun2)
eval-region(345 373 t (lambda (ignore) (goto-char 373) (quote
(call-interactively (quote myfun2))))) ; Reading at buffer position 345
apply(eval-region (345 373 t (lambda (ignore) (goto-char 373) (quote
(call-interactively (quote myfun2))))))
eval-defun-2()
eval-defun(nil)
#<subr call-interactively>(eval-defun nil nil)
apply(#<subr call-interactively> (eval-defun nil nil))
call-interactively(eval-defun nil nil)
command-execute(eval-defun)
Same thing happens when I do M-x myfun2.
^ permalink raw reply [flat|nested] 37+ messages in thread
* bug#3984:
2013-09-17 17:22 ` bug#3984: Ryan
@ 2013-09-18 1:46 ` Stefan Monnier
2013-09-18 23:30 ` bug#3984: Ryan
0 siblings, 1 reply; 37+ messages in thread
From: Stefan Monnier @ 2013-09-18 1:46 UTC (permalink / raw)
To: Ryan; +Cc: 3984
>>> We have "ad-is-advised" which we can use to find which stack frames
>>> correspond to advised functions. We have "ad-get-orig-definition" which we
>>> can use to find the original definition of an advised function.
>> These are functions of advice.el, which is on the way out. We need to
>> look at nadvice.el.
> Wait, so defadvice and friends are going away entirely?
It's very widely used, so it'll be around for a long while. It's not
even marked obsolete officially yet. But it is functionally obsoleted
by nadvice.
> Is everything in advice.el going away, or are some parts of it going
> to be converted to the new advice system?
It's already been changed to work on top of nadvice.el.
> Is all this documented somewhere?
No, only the internals were changed, and they're usually not documented,
other than in the form of actual code, and a few comments when you're lucky.
> I tried the before advice, and it doesn't seem to work. With the following
> code in emacs -Q:
> (advice-add 'call-interactively :before #'ignore)
> (defun myfun2 ()
> (interactive)
> (or (called-interactively-p)
> (error "Must be interactive!")))
> (call-interactively 'myfun2)
> I get the following stack trace:
Hmm, indeed, that fails already. It shouldn't be too hard to make
it work, tho.
Stefan
^ permalink raw reply [flat|nested] 37+ messages in thread
* bug#3984:
2013-09-18 1:46 ` bug#3984: Stefan Monnier
@ 2013-09-18 23:30 ` Ryan
2013-09-19 0:47 ` bug#3984: Ryan
0 siblings, 1 reply; 37+ messages in thread
From: Ryan @ 2013-09-18 23:30 UTC (permalink / raw)
To: Stefan Monnier; +Cc: 3984
Ok, I figured out how to walk down the stack, identify which frames are
calls to advised functions, and for each call to an advised function,
find the position in the stack of the call to the original function
definition, using the functions in nadvice.el. First, we define:
(defun unadvised-function (func)
"Return the original function definition of FUNC before it was advised."
(let ((func (indirect-function func)))
(while (advice--p func)
(setq func (advice--cdr func)))
func))
Then we scan down the stack starting from the very earliest function
call, looking for functions that are advised (via "advice--p"). Every
time we find ad advised function, we use get the original definition via
unadvised-function and then search down the stack for a call to that
original definition. Then we know to skip all those frames when
searching for called-interactively. Specifically, we skip everything but
the call to the outermost advice, since that call will bear the original
name of the function.
Again, though, this requires a top-down non-lazy search of the stack,
which is possible but seems to go against the intentions of the current
implementation that checks frames one-by-one.
What do you think? I'd be happy to work on an implementation of this in
the next few days if you think it's worth pursuing. But if so, I could
use your input on the isue of the top-down search.
-Ryan
^ permalink raw reply [flat|nested] 37+ messages in thread
* bug#3984:
2013-09-18 23:30 ` bug#3984: Ryan
@ 2013-09-19 0:47 ` Ryan
2013-09-19 3:38 ` bug#3984: Stefan Monnier
0 siblings, 1 reply; 37+ messages in thread
From: Ryan @ 2013-09-19 0:47 UTC (permalink / raw)
To: Stefan Monnier; +Cc: 3984
After reading and finally comprehending the definition of
"advice--called-interactively-skip", I think I have a possible solution
that doesn't require a top-down search, but it would require some minor
rearchitecting of nadvice. Basically, once we know that a particular
stack frame is a call to the innermost unadvised form of an advised
function, it is relatively easy to walk up the stack looking for the
outermost advice. This is what "advice--called-interactively-skip" does.
(Although reading through it I don't see where the bug is that prevents
it recognizing the before advice in my example.) The problem, then, is
knowing when a function is advised, given only the unadvised form and a
position in the stack. If we always unconditionally wrap an unadvised
function with a function that we can easily identify, then we can easily
check whether it has been advised. To that end, I propose the following:
;; Generate a private symbol
(defvar nadvice--indicator-symbol (make-symbol "nadvice--indicator-symbol"))
(defun wrap-function-in-indicator-lambda (func)
`(lambda (&rest args)
,nadvice--indicator-symbol
(apply ,func args)))
(defun indicator-lambda-p (func)
(eq nadvice--indicator-symbol
(nth 2 (wrap-function-in-indicator-lambda (indirect-function func)))))
If all advised functions are wrapped by a call to the above function
"wrap-function-in-indicator-lambda", then when they are called, the call
to the "indicator lambda" would always be 2 frames up from the call to
the original unadvised function. This allows us to easily recognize an
advised function on the stack by testing the function 2 frames up with
"indicator-lambda-p". Once we know the function is advised, we can then
initiate the search for the outermost advice's stack position.
In order to implement this, I think "advice-add" and "advice-remove"
need to be modified to automatically wrap/unwrap the original function
in/out of the indicator.
What do you think of this? Obviously my "indicator-lambda" could be
replaced by e.g. a no-op before/after advice or something similar, which
would have the same effect of making it easy to recognize the innermost
call of an advised function based on the contents of specific stack
frame positions above it.
What do you think of this?
-Ryan
^ permalink raw reply [flat|nested] 37+ messages in thread
* bug#3984:
2013-09-19 0:47 ` bug#3984: Ryan
@ 2013-09-19 3:38 ` Stefan Monnier
2013-09-19 8:06 ` bug#3984: Ryan
0 siblings, 1 reply; 37+ messages in thread
From: Stefan Monnier @ 2013-09-19 3:38 UTC (permalink / raw)
To: Ryan; +Cc: 3984
> advice. This is what "advice--called-interactively-skip" does. (Although
> reading through it I don't see where the bug is that prevents it recognizing
> the before advice in my example.)
Exactly. I think we need to fix this problem. It really should work.
> If all advised functions are wrapped by a call to the above function
> "wrap-function-in-indicator-lambda",
All functions advised with a non-:around advice have such
a "recognizable wrapper"; and that's indeed what
advice--called-interactively-skip checks in
(and (eq (nth 1 frame2) 'apply)
(progn
(funcall get-next-frame)
(advice--p (indirect-function (nth 1 frame2)))))
IOW the nadvice.el machinery is itself the recognizable wrapper.
Not sure why this fails in your test case, tho.
For :around advices, the machinery does not provide a recognizable
wrapper, so we might want to add an ad-hoc wrapper like you suggest for
those cases, tho maybe we can avoid the cost of such a wrapper, by
keeping the so-advised inner-functions in a hash-table (so we can still
recognize them, even tho they're not tagged directly in the backtrace).
So, I think the first thing is to figure out why your test case fails.
Stefan
^ permalink raw reply [flat|nested] 37+ messages in thread
* bug#3984:
2013-09-19 3:38 ` bug#3984: Stefan Monnier
@ 2013-09-19 8:06 ` Ryan
2013-09-19 19:23 ` bug#3984: Ryan
0 siblings, 1 reply; 37+ messages in thread
From: Ryan @ 2013-09-19 8:06 UTC (permalink / raw)
To: Stefan Monnier; +Cc: 3984
Ok, I'll work on debugging my test case tomorrow.
On Wed Sep 18 20:38:48 2013, Stefan Monnier wrote:
>> advice. This is what "advice--called-interactively-skip" does. (Although
>> reading through it I don't see where the bug is that prevents it recognizing
>> the before advice in my example.)
>
> Exactly. I think we need to fix this problem. It really should work.
>
>> If all advised functions are wrapped by a call to the above function
>> "wrap-function-in-indicator-lambda",
>
> All functions advised with a non-:around advice have such
> a "recognizable wrapper"; and that's indeed what
> advice--called-interactively-skip checks in
>
> (and (eq (nth 1 frame2) 'apply)
> (progn
> (funcall get-next-frame)
> (advice--p (indirect-function (nth 1 frame2)))))
>
> IOW the nadvice.el machinery is itself the recognizable wrapper.
> Not sure why this fails in your test case, tho.
>
> For :around advices, the machinery does not provide a recognizable
> wrapper, so we might want to add an ad-hoc wrapper like you suggest for
> those cases, tho maybe we can avoid the cost of such a wrapper, by
> keeping the so-advised inner-functions in a hash-table (so we can still
> recognize them, even tho they're not tagged directly in the backtrace).
>
> So, I think the first thing is to figure out why your test case fails.
>
>
> Stefan
^ permalink raw reply [flat|nested] 37+ messages in thread
* bug#3984:
2013-09-19 8:06 ` bug#3984: Ryan
@ 2013-09-19 19:23 ` Ryan
2013-09-19 20:59 ` bug#3984: Stefan Monnier
2013-09-19 21:59 ` bug#3984: Ryan
0 siblings, 2 replies; 37+ messages in thread
From: Ryan @ 2013-09-19 19:23 UTC (permalink / raw)
To: Stefan Monnier; +Cc: 3984
On a related note, I just noticed that the
"advice-test-called-interactively-p" test in
test/automated/advice-tests.el happens to pass, but only because it
doesn't use "called-interactively-p" inside the original function, but
rather only inside the advice itself. Also, it doesn't test advising
"call-interactively" itself. I think I will see about writing a proper
test first, and then use that to start debugging.
On Thu Sep 19 01:06:03 2013, Ryan wrote:
> Ok, I'll work on debugging my test case tomorrow.
>
> On Wed Sep 18 20:38:48 2013, Stefan Monnier wrote:
>>> advice. This is what "advice--called-interactively-skip" does.
>>> (Although
>>> reading through it I don't see where the bug is that prevents it
>>> recognizing
>>> the before advice in my example.)
>>
>> Exactly. I think we need to fix this problem. It really should work.
>>
>>> If all advised functions are wrapped by a call to the above function
>>> "wrap-function-in-indicator-lambda",
>>
>> All functions advised with a non-:around advice have such
>> a "recognizable wrapper"; and that's indeed what
>> advice--called-interactively-skip checks in
>>
>> (and (eq (nth 1 frame2) 'apply)
>> (progn
>> (funcall get-next-frame)
>> (advice--p (indirect-function (nth 1 frame2)))))
>>
>> IOW the nadvice.el machinery is itself the recognizable wrapper.
>> Not sure why this fails in your test case, tho.
>>
>> For :around advices, the machinery does not provide a recognizable
>> wrapper, so we might want to add an ad-hoc wrapper like you suggest for
>> those cases, tho maybe we can avoid the cost of such a wrapper, by
>> keeping the so-advised inner-functions in a hash-table (so we can still
>> recognize them, even tho they're not tagged directly in the backtrace).
>>
>> So, I think the first thing is to figure out why your test case fails.
>>
>>
>> Stefan
^ permalink raw reply [flat|nested] 37+ messages in thread
* bug#3984:
2013-09-19 19:23 ` bug#3984: Ryan
@ 2013-09-19 20:59 ` Stefan Monnier
2013-09-19 21:59 ` bug#3984: Ryan
1 sibling, 0 replies; 37+ messages in thread
From: Stefan Monnier @ 2013-09-19 20:59 UTC (permalink / raw)
To: Ryan; +Cc: 3984
> "advice-test-called-interactively-p" test in test/automated/advice-tests.el
> happens to pass, but only because it doesn't use "called-interactively-p"
> inside the original function, but rather only inside the advice
> itself.
Indeed. And it a very important case: the main definition of a function
can be modified to not use called-interactively-p (add an argument which
is set to a non-nil value in the interactive spec), but that's not
really an option for an advice that requires to know if the function was
called interactively (strictly speaking, it can be done, but it's a lot
more trouble).
Stefan
^ permalink raw reply [flat|nested] 37+ messages in thread
* bug#3984:
2013-09-19 19:23 ` bug#3984: Ryan
2013-09-19 20:59 ` bug#3984: Stefan Monnier
@ 2013-09-19 21:59 ` Ryan
2013-09-20 4:23 ` bug#3984: Ryan
2013-09-20 14:54 ` bug#3984: Stefan Monnier
1 sibling, 2 replies; 37+ messages in thread
From: Ryan @ 2013-09-19 21:59 UTC (permalink / raw)
To: Stefan Monnier; +Cc: 3984
[-- Attachment #1: Type: text/plain, Size: 1560 bytes --]
On Thu Sep 19 12:23:58 2013, Ryan wrote:
>
> On a related note, I just noticed that the
> "advice-test-called-interactively-p" test in
> test/automated/advice-tests.el happens to pass, but only because it
> doesn't use "called-interactively-p" inside the original function, but
> rather only inside the advice itself. Also, it doesn't test advising
> "call-interactively" itself. I think I will see about writing a proper
> test first, and then use that to start debugging.
>
Ok, I have written a couple of tests, two of which are currently failing
(mine are the ones with numbered suffixes 2 through 5). I am attaching a
patch that adds these tests. ERT output is below:
$ open .
techne:emacs-trunk ryan$ src/emacs -batch -Q -l ert -l
test/automated/advice-tests.el -f ert-run-tests-batch-and-exit
Running 10 tests (2013-09-19 14:56:00-0700)
passed 1/10 advice-test-called-interactively-p
failed 2/10 advice-test-called-interactively-p-2
passed 3/10 advice-test-called-interactively-p-3
failed 4/10 advice-test-called-interactively-p-4
passed 5/10 advice-test-called-interactively-p-5
passed 6/10 advice-test-interactive
passed 7/10 advice-test-preactivate
ad-handle-definition: `sm-test2' got redefined
ad-handle-definition: `sm-test4' got redefined
passed 8/10 advice-tests-advice
ad-handle-definition: `sm-test5' got redefined
passed 9/10 advice-tests-combination
passed 10/10 advice-tests-nadvice
Ran 10 tests, 10 results as expected (2013-09-19 14:56:00-0700)
2 expected failures
[-- Attachment #2: more-advice-interactive-tests.diff --]
[-- Type: text/plain, Size: 3609 bytes --]
diff --git a/test/automated/advice-tests.el b/test/automated/advice-tests.el
index 69c15e3..65577ad 100644
--- a/test/automated/advice-tests.el
+++ b/test/automated/advice-tests.el
@@ -23,6 +23,21 @@
(require 'ert)
+(defun clear-advice (symbol)
+ "Reset SYMBOL's function to its original unadvised definition."
+ (let ((func (symbol-function symbol)))
+ (while (advice--p func)
+ (setq func (advice--cdr func)))
+ (fset symbol func)))
+
+(defmacro post-restore-func (func &rest body)
+ (let ((fdef (symbol-function func)))
+ `(unwind-protect
+ (progn ,@body)
+ (fset ',func ,fdef))))
+(put 'post-restore-func 'lisp-indent-function
+ (get 'prog1 'lisp-indent-function))
+
(ert-deftest advice-tests-nadvice ()
"Test nadvice code."
(defun sm-test1 (x) (+ x 4))
@@ -113,6 +128,60 @@
(cons (cons 2 (called-interactively-p)) (apply f args))))
(should (equal (call-interactively 'sm-test7) '((2 . t) (1 . t) 11))))
+(ert-deftest advice-test-called-interactively-p-2 ()
+ "Check interaction between around advice and called-interactively-p.
+
+This tests the currently broken case of the innermost advice to a
+function being an around advice."
+ :expected-result :failed
+ (defun sm-test7.2 () (interactive) (cons 1 (called-interactively-p)))
+ (clear-advice 'sm-test7.2)
+ (advice-add 'sm-test7.2 :around
+ (lambda (f &rest args)
+ (list (cons 1 (called-interactively-p)) (apply f args))))
+ (advice-add 'sm-test7.2 :before #'ignore)
+ (advice-add 'sm-test7.2 :after #'ignore)
+ ;(advice-add 'sm-test7.2 :filter-args #'list)
+ ;(advice-add 'sm-test7.2 :filter-return #'identity)
+ (should (equal (sm-test7.2) '((1 . nil) (1 . nil))))
+ (should (equal (call-interactively 'sm-test7.2) '((1 . t) (1 . t)))))
+
+(ert-deftest advice-test-called-interactively-p-3 ()
+ "Check interaction between before advice and called-interactively-p.
+
+This tests the case of the innermost advice being before"
+ (defun sm-test7.3 () (interactive) (cons 1 (called-interactively-p)))
+ (advice-add 'sm-test7.3 :before #'ignore)
+ ;(advice-add 'sm-test7.3 :filter-args #'list)
+ ;(advice-add 'sm-test7.3 :filter-return #'identity)
+ (should (equal (sm-test7.3) '(1 . nil)))
+ (should (equal (call-interactively 'sm-test7.3) '(1 . t))))
+
+(ert-deftest advice-test-called-interactively-p-4 ()
+ "Check interaction between advice on call-interactively and called-interactively-p.
+
+This tests the case where call-interactively itself is advised,
+which is currently broken."
+ :expected-result :failed
+ (defun sm-test7.4 () (interactive) (cons 1 (called-interactively-p)))
+ (post-restore-func call-interactively
+ (advice-add 'call-interactively :before #'ignore)
+ (should (equal (sm-test7.4) '(1 . nil)))
+ (should (equal (call-interactively 'sm-test7.4) '(1 . t)))))
+
+(ert-deftest advice-test-called-interactively-p-5 ()
+ "Check interaction between non-innermost around advice and called-interactively-p.
+
+This tests the case where a function has around advice, but it is
+not the innermost advice."
+ (defun sm-test7.5 () (interactive) (cons 1 (called-interactively-p)))
+ (advice-add 'sm-test7.5 :before #'ignore)
+ (advice-add 'sm-test7.5 :around #'funcall)
+ ;(advice-add 'sm-test7.5 :filter-args #'list)
+ ;(advice-add 'sm-test7.5 :filter-return #'identity)
+ (should (equal (sm-test7.5) '(1 . nil)))
+ (should (equal (call-interactively 'sm-test7.5) '(1 . t))))
+
(ert-deftest advice-test-interactive ()
"Check handling of interactive spec."
(defun sm-test8 (a) (interactive "p") a)
^ permalink raw reply related [flat|nested] 37+ messages in thread
* bug#3984:
2013-09-19 21:59 ` bug#3984: Ryan
@ 2013-09-20 4:23 ` Ryan
2013-09-20 4:58 ` bug#3984: Fix case where call-interactively is advised Ryan
2013-09-20 14:35 ` bug#3984: Stefan Monnier
2013-09-20 14:54 ` bug#3984: Stefan Monnier
1 sibling, 2 replies; 37+ messages in thread
From: Ryan @ 2013-09-20 4:23 UTC (permalink / raw)
To: Stefan Monnier; +Cc: 3984
After a little more consideration, I think I know what the bug is. The
goal of "advice--called-interactively-skip" is to skip all the
advice-related stack frames of the called function, not advice-related
stack frames for advice on call-interactively.
I also came up with an idea to address this issue: a second function
added to called-interactively-p-functions that checks the current stack
frame for equality to the unadvised form of call-interactively and if it
finds it, skips past all the advice frames to the outermost call. This
will make it so that advising call-interactively does not affect the
return of called-interactively-p, but it will not fix the "innermost
around advice" problem.
I'm trying to code up a solution now. I'll eventually find my way
through the twisty maze of fencepost errors.
-Ryan
^ permalink raw reply [flat|nested] 37+ messages in thread
* bug#3984: Fix case where call-interactively is advised
2013-09-20 4:23 ` bug#3984: Ryan
@ 2013-09-20 4:58 ` Ryan
2013-09-20 5:03 ` bug#3984: Ryan
2013-09-20 14:35 ` bug#3984: Stefan Monnier
1 sibling, 1 reply; 37+ messages in thread
From: Ryan @ 2013-09-20 4:58 UTC (permalink / raw)
To: Stefan Monnier; +Cc: 3984
[-- Attachment #1: Type: text/plain, Size: 1105 bytes --]
Here is a patch that applies over my previous patch and fixes
"advice-test-called-interactively-p-4", which tests whether
called-interactively-p returns the correct result when
call-interactively is advised.
On 9/19/13 9:23 PM, Ryan wrote:
> After a little more consideration, I think I know what the bug is. The
> goal of "advice--called-interactively-skip" is to skip all the
> advice-related stack frames of the called function, not advice-related
> stack frames for advice on call-interactively.
>
> I also came up with an idea to address this issue: a second function
> added to called-interactively-p-functions that checks the current
> stack frame for equality to the unadvised form of call-interactively
> and if it finds it, skips past all the advice frames to the outermost
> call. This will make it so that advising call-interactively does not
> affect the return of called-interactively-p, but it will not fix the
> "innermost around advice" problem.
>
> I'm trying to code up a solution now. I'll eventually find my way
> through the twisty maze of fencepost errors.
>
> -Ryan
[-- Attachment #2: fix-advised-call-interactively.diff --]
[-- Type: text/plain, Size: 2809 bytes --]
diff --git a/lisp/emacs-lisp/nadvice.el b/lisp/emacs-lisp/nadvice.el
index 8b149aa..2dab864 100644
--- a/lisp/emacs-lisp/nadvice.el
+++ b/lisp/emacs-lisp/nadvice.el
@@ -461,6 +461,36 @@ of the piece of advice."
(funcall get-next-frame))))
(- i origi 1))))
+(defun advice-unadvised-form (func)
+ "Return the definition of FUNC with all advice stripped.
+
+FUNC may be a function definition or a symbol naming a function."
+ (let ((func (indirect-function func)))
+ (while (advice--p func)
+ (setq func (advice--cdr func)))
+ func))
+
+;; When `call-interactively' is advised, called-interactively-p needs
+;; to be taught to skip the advising frames.
+(add-hook 'called-interactively-p-functions
+ #'advice--advised-called-interactively-skip)
+(defun advice--advised-called-interactively-skip (origi frame1 frame2)
+ (when (and frame2
+ (not (eq (nth 1 frame2) 'call-interactively))
+ (eq (advice-unadvised-form 'call-interactively)
+ (indirect-function (nth 1 frame2))))
+ ;; Skip until frame2 is a call to the symbol call-interactively.
+ (let* ((i origi)
+ (get-next-frame
+ (lambda ()
+ (setq frame1 frame2)
+ (setq frame2 (internal--called-interactively-p--get-frame i))
+ (setq i (1+ i)))))
+ (funcall get-next-frame)
+ (while (and frame2
+ (not (eq (nth 1 frame2) 'call-interactively)))
+ (funcall get-next-frame))
+ (- i origi 1))))
(provide 'nadvice)
;;; nadvice.el ends here
diff --git a/lisp/subr.el b/lisp/subr.el
index 75c6b3a..b5f682a 100644
--- a/lisp/subr.el
+++ b/lisp/subr.el
@@ -4272,8 +4272,6 @@ command is called from a keyboard macro?"
(_ (setq i (+ i skip -1)) (funcall get-next-frame)))))))
;; Now `frame' should be "the function from which we were called".
(pcase (cons frame nextframe)
- ;; No subr calls `interactive-p', so we can rule that out.
- (`((,_ ,(pred (lambda (f) (subrp (indirect-function f)))) . ,_) . ,_) nil)
;; Somehow, I sometimes got `command-execute' rather than
;; `call-interactively' on my stacktrace !?
;;(`(,_ . (t command-execute . ,_)) t)
diff --git a/test/automated/advice-tests.el b/test/automated/advice-tests.el
index c8f06e5..01be3ab 100644
--- a/test/automated/advice-tests.el
+++ b/test/automated/advice-tests.el
@@ -163,7 +163,6 @@ This tests the case of the innermost advice being before"
This tests the case where call-interactively itself is advised,
which is currently broken."
- :expected-result :failed
(defun sm-test7.4 () (interactive) (cons 1 (called-interactively-p)))
(post-restore-func call-interactively
(advice-add 'call-interactively :before #'ignore)
^ permalink raw reply related [flat|nested] 37+ messages in thread
* bug#3984:
2013-09-20 4:58 ` bug#3984: Fix case where call-interactively is advised Ryan
@ 2013-09-20 5:03 ` Ryan
0 siblings, 0 replies; 37+ messages in thread
From: Ryan @ 2013-09-20 5:03 UTC (permalink / raw)
To: Stefan Monnier; +Cc: 3984
On a related note, what is the proper way to test for the new advice
system? (featurep 'nadvice)?
Anyway, now that we have a fix for when call-interactively is advised,
what do you want to do about the case where the called function is
advised with the innermost advice being around? The only problem we
really need to solve is being absolutely whether or not a given stack
frame is the innermost function in an advice stack. The two options that
I can see are a complete top-down search or modifying around advice so
that it gives a consistent signature (e.g. by wrapping the advised
function in a special lambda.
-Ryan
^ permalink raw reply [flat|nested] 37+ messages in thread
* bug#3984:
2013-09-20 4:23 ` bug#3984: Ryan
2013-09-20 4:58 ` bug#3984: Fix case where call-interactively is advised Ryan
@ 2013-09-20 14:35 ` Stefan Monnier
2013-09-20 16:54 ` bug#3984: Ryan
1 sibling, 1 reply; 37+ messages in thread
From: Stefan Monnier @ 2013-09-20 14:35 UTC (permalink / raw)
To: Ryan; +Cc: 3984
> After a little more consideration, I think I know what the bug is. The goal
> of "advice--called-interactively-skip" is to skip all the advice-related
> stack frames of the called function, not advice-related stack frames for
> advice on call-interactively.
Oh, right. How 'bout the patch below?
Stefan
=== modified file 'lisp/subr.el'
--- lisp/subr.el 2013-09-18 03:50:18 +0000
+++ lisp/subr.el 2013-09-20 14:34:03 +0000
@@ -4246,6 +4246,8 @@
if those frames don't seem special and otherwise, it should return
the number of frames to skip (minus 1).")
+(defconst internal--call-interactively (symbol-function 'call-interactively))
+
(defun called-interactively-p (&optional kind)
"Return t if the containing function was called by `call-interactively'.
If KIND is `interactive', then only return t if the call was made
@@ -4321,7 +4323,9 @@
;; Somehow, I sometimes got `command-execute' rather than
;; `call-interactively' on my stacktrace !?
;;(`(,_ . (t command-execute . ,_)) t)
- (`(,_ . (t call-interactively . ,_)) t)))))
+ (`(,_ . (t ,(or `call-interactively
+ (pred (eq internal--call-interactively))) . ,_))
+ t)))))
(defun interactive-p ()
"Return t if the containing function was run directly by user input.
^ permalink raw reply [flat|nested] 37+ messages in thread
* bug#3984:
2013-09-19 21:59 ` bug#3984: Ryan
2013-09-20 4:23 ` bug#3984: Ryan
@ 2013-09-20 14:54 ` Stefan Monnier
2013-09-20 16:50 ` bug#3984: Ryan
1 sibling, 1 reply; 37+ messages in thread
From: Stefan Monnier @ 2013-09-20 14:54 UTC (permalink / raw)
To: Ryan; +Cc: 3984
> Ok, I have written a couple of tests, two of which are currently failing
> (mine are the ones with numbered suffixes 2 through 5). I am
> attaching a patch that adds these tests. ERT output is below:
Thanks. Just a few questions, before installing the patch.
> +(defun clear-advice (symbol)
> + "Reset SYMBOL's function to its original unadvised definition."
> + (let ((func (symbol-function symbol)))
> + (while (advice--p func)
> + (setq func (advice--cdr func)))
> + (fset symbol func)))
Why do you need that? AFAICT you only use it after defining sm-test7.2,
and I don't see why that call is needed.
> +(defmacro post-restore-func (func &rest body)
> + (let ((fdef (symbol-function func)))
> + `(unwind-protect
> + (progn ,@body)
> + (fset ',func ,fdef))))
> +(put 'post-restore-func 'lisp-indent-function
> + (get 'prog1 'lisp-indent-function))
Since it's only used once, I don't think it's worth defining a macro for it.
Stefan
^ permalink raw reply [flat|nested] 37+ messages in thread
* bug#3984:
2013-09-20 14:54 ` bug#3984: Stefan Monnier
@ 2013-09-20 16:50 ` Ryan
2013-09-20 19:59 ` bug#3984: Stefan Monnier
0 siblings, 1 reply; 37+ messages in thread
From: Ryan @ 2013-09-20 16:50 UTC (permalink / raw)
To: Stefan Monnier; +Cc: 3984
>> Ok, I have written a couple of tests, two of which are currently failing
>> (mine are the ones with numbered suffixes 2 through 5). I am
>> attaching a patch that adds these tests. ERT output is below:
> Thanks. Just a few questions, before installing the patch.
>
>> +(defun clear-advice (symbol)
>> + "Reset SYMBOL's function to its original unadvised definition."
>> + (let ((func (symbol-function symbol)))
>> + (while (advice--p func)
>> + (setq func (advice--cdr func)))
>> + (fset symbol func)))
> Why do you need that? AFAICT you only use it after defining sm-test7.2,
> and I don't see why that call is needed.
Oh, that's actually not necessary. I put it in so I could re-run the
test without restarting emacs or manually resetting the advice, and I
forgot to remove it.
>> +(defmacro post-restore-func (func &rest body)
>> + (let ((fdef (symbol-function func)))
>> + `(unwind-protect
>> + (progn ,@body)
>> + (fset ',func ,fdef))))
>> +(put 'post-restore-func 'lisp-indent-function
>> + (get 'prog1 'lisp-indent-function))
> Since it's only used once, I don't think it's worth defining a macro for it.
Probably true. I wasn't sure how much I would end up using it, and it
seemed like a potentially useful macro to have lying around when testing
advice that might break core functions of emacs.
Anyway, I wasn't very creative with my test names or function names, and
I'd be happy to submit a new patch with more informative names (as well
as the above changes). Is there a naming convention for tests/functions
that I should adhere to?
^ permalink raw reply [flat|nested] 37+ messages in thread
* bug#3984:
2013-09-20 14:35 ` bug#3984: Stefan Monnier
@ 2013-09-20 16:54 ` Ryan
2013-09-20 16:56 ` bug#3984: Ryan
0 siblings, 1 reply; 37+ messages in thread
From: Ryan @ 2013-09-20 16:54 UTC (permalink / raw)
To: Stefan Monnier; +Cc: 3984
On 9/20/13 7:35 AM, Stefan Monnier wrote:
>> After a little more consideration, I think I know what the bug is. The goal
>> of "advice--called-interactively-skip" is to skip all the advice-related
>> stack frames of the called function, not advice-related stack frames for
>> advice on call-interactively.
> Oh, right. How 'bout the patch below?
>
>
> Stefan
>
>
> === modified file 'lisp/subr.el'
> --- lisp/subr.el 2013-09-18 03:50:18 +0000
> +++ lisp/subr.el 2013-09-20 14:34:03 +0000
> @@ -4246,6 +4246,8 @@
> if those frames don't seem special and otherwise, it should return
> the number of frames to skip (minus 1).")
>
> +(defconst internal--call-interactively (symbol-function 'call-interactively))
> +
> (defun called-interactively-p (&optional kind)
> "Return t if the containing function was called by `call-interactively'.
> If KIND is `interactive', then only return t if the call was made
> @@ -4321,7 +4323,9 @@
> ;; Somehow, I sometimes got `command-execute' rather than
> ;; `call-interactively' on my stacktrace !?
> ;;(`(,_ . (t command-execute . ,_)) t)
> - (`(,_ . (t call-interactively . ,_)) t)))))
> + (`(,_ . (t ,(or `call-interactively
> + (pred (eq internal--call-interactively))) . ,_))
> + t)))))
>
> (defun interactive-p ()
> "Return t if the containing function was run directly by user input.
>
That would certainly work, assuming that subr.el is always loaded before
nadvice.el (so call-interactively could not possibly be advised yet),
which seems likely to be true since subr.el contains so many core functions.
Since this bug report is just about advising call-interactively, which
we now have a fix for, should I open a separate bug about the around
advice?
^ permalink raw reply [flat|nested] 37+ messages in thread
* bug#3984:
2013-09-20 16:54 ` bug#3984: Ryan
@ 2013-09-20 16:56 ` Ryan
0 siblings, 0 replies; 37+ messages in thread
From: Ryan @ 2013-09-20 16:56 UTC (permalink / raw)
To: Stefan Monnier; +Cc: 3984
I confirm that your simple patch to subr.el passes the test that I
provided.
^ permalink raw reply [flat|nested] 37+ messages in thread
* bug#3984:
2013-09-20 16:50 ` bug#3984: Ryan
@ 2013-09-20 19:59 ` Stefan Monnier
0 siblings, 0 replies; 37+ messages in thread
From: Stefan Monnier @ 2013-09-20 19:59 UTC (permalink / raw)
To: Ryan; +Cc: 3984
> Oh, that's actually not necessary. I put it in so I could re-run the test
> without restarting emacs or manually resetting the advice, and I forgot to
> remove it.
OK, removed.
>> Since it's only used once, I don't think it's worth defining a macro for it.
> Probably true. I wasn't sure how much I would end up using it, and it seemed
> like a potentially useful macro to have lying around when testing advice
> that might break core functions of emacs.
Makes sense as well, but let's postpone putting it in until we do end
up using it repeatedly.
I installed some tests based on your patch, as well as my quick fix in
subr.el.
> That would certainly work, assuming that subr.el is always loaded before
> nadvice.el (so call-interactively could not possibly be advised yet), which
> seems likely to be true since subr.el contains so many core functions.
Since subr.el is both preloaded and loaded very early, that should be
safe, yes.
> Since this bug report is just about advising call-interactively, which we
> now have a fix for, should I open a separate bug about the around advice?
Sure. Note also that in the tests I installed, I added one that
demonstrates a problem with :filter-args as well (because :filter-args
uses `funcall' rather than `apply', so it doesn't match the pattern
expected by advice--called-interactively-skip).
By the way, I don't see your name in our copyright assignment database.
I installed your tests under "tiny change", but if you intend to
contribute more, we'd need you to sign some paperwork. Would you be
willing to do that?
Stefan
^ permalink raw reply [flat|nested] 37+ messages in thread
end of thread, other threads:[~2013-09-20 19:59 UTC | newest]
Thread overview: 37+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2009-07-30 22:37 bug#3984: 23.0.96; defadvice of call-interactively defeats interactive-p Drew Adams
2009-07-31 1:58 ` Stefan Monnier
2009-07-31 14:19 ` Drew Adams
2009-07-31 19:31 ` Stefan Monnier
2009-07-31 20:04 ` Drew Adams
2011-10-10 6:00 ` Kai Tetzlaff
2011-10-11 14:26 ` Drew Adams
2011-10-11 15:46 ` Stefan Monnier
2011-10-11 16:05 ` Drew Adams
2013-09-10 20:29 ` Christopher Wellons
2013-09-11 0:29 ` Stefan Monnier
2013-09-13 8:56 ` bug#3984: Fix for #3984 Ryan
2013-09-13 13:18 ` Stefan Monnier
2013-09-13 18:30 ` Ryan
2013-09-13 19:27 ` Ryan
2013-09-13 21:02 ` Stefan Monnier
2013-09-17 3:18 ` Ryan
2013-09-17 13:10 ` Stefan Monnier
2013-09-17 17:22 ` bug#3984: Ryan
2013-09-18 1:46 ` bug#3984: Stefan Monnier
2013-09-18 23:30 ` bug#3984: Ryan
2013-09-19 0:47 ` bug#3984: Ryan
2013-09-19 3:38 ` bug#3984: Stefan Monnier
2013-09-19 8:06 ` bug#3984: Ryan
2013-09-19 19:23 ` bug#3984: Ryan
2013-09-19 20:59 ` bug#3984: Stefan Monnier
2013-09-19 21:59 ` bug#3984: Ryan
2013-09-20 4:23 ` bug#3984: Ryan
2013-09-20 4:58 ` bug#3984: Fix case where call-interactively is advised Ryan
2013-09-20 5:03 ` bug#3984: Ryan
2013-09-20 14:35 ` bug#3984: Stefan Monnier
2013-09-20 16:54 ` bug#3984: Ryan
2013-09-20 16:56 ` bug#3984: Ryan
2013-09-20 14:54 ` bug#3984: Stefan Monnier
2013-09-20 16:50 ` bug#3984: Ryan
2013-09-20 19:59 ` bug#3984: Stefan Monnier
2013-09-13 10:24 ` bug#3984: bug#123: Potential fix Ryan
Code repositories for project(s) associated with this public inbox
https://git.savannah.gnu.org/cgit/emacs.git
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).