Bug 364886 - Invokedynamic and AspectJ
Summary: Invokedynamic and AspectJ
Status: NEW
Alias: None
Product: AspectJ
Classification: Tools
Component: Compiler (show other bugs)
Version: unspecified   Edit
Hardware: All All
: P3 enhancement (vote)
Target Milestone: ---   Edit
Assignee: aspectj inbox CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2011-11-26 05:36 EST by Eric Bodden CLA
Modified: 2020-03-12 00:50 EDT (History)
3 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Eric Bodden CLA 2011-11-26 05:36:22 EST
This bug is to discuss how AspectJ should treat invokedynamic bytecodes. (see post "Any researchers out there put time into invokedynamic interplay with AspectJ pointcuts?" on aspectj-dev)
Comment 1 Andrew Clement CLA 2011-11-28 12:25:26 EST
As Eric mentioned on the mailing list there are few things that this bug report
could cover:

(1) AspectJ coping with the invokedynamic instruction (not doing anything
special for it, just coping with it)
(2) AspectJ recognizing and matching/weaving invokedynamic calls
(3) AspectJ using invokedynamic in generated code

My goal primarily is (1) whilst exploring (2).  A later goal would be (3) if we
could show an appreciable benefit - doesn't have to be a performance benefit
(seems unlikely that it would be), it could be a flexibility benefit.

Now (1) seems pretty straightforward, just letting that instruction through and
not damaging it, I doubt this will take much effort.

(2) is where I'd like the assistance right now.  I don't know if I want a new
pointcut, a new modifier on 'call', or even if there is enough information
known when using invokedynamic such that we can do the right thing.  I've
really not looked into invokedynamic thoroughly yet.
Comment 2 Eric Bodden CLA 2011-12-20 04:11:54 EST
Hi Andy.

Regarding (1) I would hope that ASM is taking care of that automatically. Can you confirm this? (You are using ASM for weaving, aren't you?)

Regarding (3), this is indeed very tricky. IMHO execution joinpoints should just work as before (because they are at the callee side), and so should all other joinpoints except for "call". For call joinpoints, however, one obviously cannot do the usual static matching using the pointcut signature. 

I think one way one possibly *could* go about this is to somehow wrap or replace the MethodHandle lookup mechanism that the invokedynamic-using application uses. One would then need to recognize advised calls at runtime and reroute the method handles to woven versions of the code dynamically. If I am not mistaken, then lookup is usually performed through a Lookup object, see http://docs.oracle.com/javase/7/docs/api/java/lang/invoke/MethodHandles.Lookup.html#findGetter(java.lang.Class, java.lang.String, java.lang.Class)
Comment 3 Andrew Clement CLA 2011-12-20 22:53:51 EST
quick reply from me:

> Regarding (1) I would hope that ASM is taking care of that automatically. Can
you confirm this? (You are using ASM for weaving, aren't you?)

No, we still don't use ASM.  ASM doesn't provide the right granularity for us (some things we don't want to take apart but we don't get a choice with asm), we continue to use bcel (heavily modified of course).  What happens here will depend on (a) whether I modified the bcel code to recognize invokedynamic - dont remember, (b) what happens when the weaver encounters invokedynamic, will it try to create the wrong kind of shadow? will it crash? will it be ignored? will it be swallowed? I just don't know.  I will get this sorted for M2.

ASM *is* used for recomputing stackmap attributes right now, after weaving is completed.
Comment 4 Eric Bodden CLA 2012-01-12 08:53:49 EST
I have thought a bit more about "(2) AspectJ recognizing and matching/weaving invokedynamic calls"...

One may have two different view points. The first view point may be that invokedynamic calls are calls like any other calls, and therefore a pointcut call(foo()) should match on *any* call to foo() no matter whether invoked through invokedynamic or whatever other bytecode. This, however would be very hard (impossible?) to implement with the current API. From what I can see, one would need to wrap the program's bootstrap methods to return wrapper call site objects which execute the pointcut matching logic at runtime. On a call to CallSite.getTarget() one would need to capture this target, see if a pointcut matches on it, and if not, return a MethodHandle to some "nop method". The problem is that CallSite.getTarget() cannot be overwritten, and one also cannot advise calls to this method with aspects because they come from native code. 

The second view point may be that an invokedynamic instruction is special, and therefore there should be a special pointcut for it, exposing the arguments passed to this call. This would be simple to implement, but the question is how useful it would be.



Regarding "(3) AspectJ using invokedynamic in generated code", this would certainly be the way to go. The resulting generated code should be much simpler than today if invokedynamic were used.



P.S. On another note, I am also experiencing Bug 353467 when compiling with -target 1.7
Comment 5 Andrew Clement CLA 2012-03-19 16:58:40 EDT
just committed changes for (1).  Added support for 3 new constant pool entry types, the new attribute and the invokedynamic instruction.  Some new IndyTests verify I can weave into bytecode that contains the invokedynamic instruction.  The invokedynamic call is currently ignored and will not manifest as a joinpoint.
Comment 6 Alexander Kriegisch CLA 2020-03-12 00:42:08 EDT
This one might be blocking https://bugs.eclipse.org/bugs/show_bug.cgi?id=471347. I am mentioning it because Andy asked for possible benefits of (2). I think the two tickets are related.
Comment 7 Alexander Kriegisch CLA 2020-03-12 00:49:19 EDT
I forgot to explain: The fact that AspectJ is currently blind to lambda executions and calls is partly due to the fact that invokedynamic is being ignored.

A popular and potentially useful example would be intercepting Runnable.run() whereas the runnable is implemented as a lambda, i.e. usually as a private method inside the defining class dynamically called like this for example:

invokedynamic #26,  0
  // InvokeDynamic #0:run:
  //   Lde/scrum_master/app/Application;)
  //   Ljava/lang/Runnable;

invokeinterface #30,  2
  // InterfaceMethod
  //   java/util/concurrent/ExecutorService.execute:
  //   (Ljava/lang/Runnable;)V
Comment 8 Alexander Kriegisch CLA 2020-03-12 00:50:55 EDT
It is also not possible to intercept constructor calls on such lambdas, i.e. to find out where and when they are being created. In multi-threaded scenarios this makes tracing certain asynchronous control flows via AOP next to impossible.