U60 change makes annotations visible in synthesized methods, causes CDI to see lambdas as observer methods

Alex Buckley alex.buckley at oracle.com
Wed Aug 26 19:51:58 UTC 2015


Jan,

The mail below was sent to lambda-dev [1] and subsequently core-libs-dev 
[2].

While CDI should be ignoring synthetic methods, there is still the 
question of why the synthetic method representing the d->... lambda has 
a RuntimeVisibleParameterAnnotations attribute.

It looks like the @Observes annotation from the first processBean 
parameter has mysteriously made its way to the first 
lambda$processBean$4 parameter. I'm wondering if this is related to your 
8u60 backport of JDK-8037546 ?

Alex

[1] 
http://mail.openjdk.java.net/pipermail/lambda-dev/2015-August/012146.html
[2] 
http://mail.openjdk.java.net/pipermail/core-libs-dev/2015-August/034990.html

> *From: *arjan tijms <arjan.tijms at gmail.com <mailto:arjan.tijms at gmail.com>>
> *Subject: **U60 change makes annotations visible in synthesized
> methods, causes CDI to see lambdas as observer methods*
> *Date: *August 26, 2015 at 10:04:22 AM EDT
> *To: *lambda-dev at openjdk.java.net <mailto:lambda-dev at openjdk.java.net>
>
> Hi,
>
> I'm with the JSF EG/Mojarra team and we're seeing what looks like a
> problematic regression in JDK8 u60.
>
> In a CDI extension we have the following observer method:
>
>    public <T> void processBean(@Observes ProcessBean<T> event,
> BeanManager beanManager) {
>        getAnnotation(beanManager, event.getAnnotated(),
> FacesDataModel.class)
>           .ifPresent(d ->
>               forClassToDataModelClass.put(
>                   d.forClass(),
>                   (Class<? extends DataModel<?>>)
> event.getBean().getBeanClass()
>               )
>           );
>    }
>
> With u45 and below this will generate the following (decompiled the
> class with javap -v -p -s -c)
>
>  private void
> lambda$processBean$4(javax.enterprise.inject.spi.ProcessBean,
> javax.faces.model.FacesDataModel);
>    descriptor:
> (Ljavax/enterprise/inject/spi/ProcessBean;Ljavax/faces/model/FacesDataModel;)V
>    flags: ACC_PRIVATE, ACC_SYNTHETIC
>    Code:
>      stack=3, locals=3, args_size=3
>         0: aload_0
>         1: getfield      #4                  // Field
> forClassToDataModelClass:Ljava/util/Map;
>         4: aload_2
>         5: invokeinterface #57,  1           // InterfaceMethod
> javax/faces/model/FacesDataModel.forClass:()Ljava/lang/Class;
>        10: aload_1
>        11: invokeinterface #58,  1           // InterfaceMethod
> javax/enterprise/inject/spi/ProcessBean.getBean:()Ljavax/enterprise/inject/spi/Bean;
>        16: invokeinterface #59,  1           // InterfaceMethod
> javax/enterprise/inject/spi/Bean.getBeanClass:()Ljava/lang/Class;
>        21: invokeinterface #55,  3           // InterfaceMethod
> java/util/Map.put:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
>        26: checkcast     #45                 // class java/lang/Class
>        29: pop
>        30: return
>      LineNumberTable:
>        line 110: 0
>        line 111: 5
>        line 112: 11
>        line 110: 21
>      LocalVariableTable:
>        Start  Length  Slot  Name   Signature
>            0      31     0  this   Lcom/sun/faces/cdi/CdiExtension;
>            0      31     2     d   Ljavax/faces/model/FacesDataModel;
>
>
> U60 however generates the following:
>
>  private void
> lambda$processBean$4(javax.enterprise.inject.spi.ProcessBean,
> javax.faces.model.FacesDataModel);
>    descriptor:
> (Ljavax/enterprise/inject/spi/ProcessBean;Ljavax/faces/model/FacesDataModel;)V
>    flags: ACC_PRIVATE, ACC_SYNTHETIC
>    Code:
>      stack=3, locals=3, args_size=3
>         0: aload_0
>         1: getfield      #4                  // Field
> forClassToDataModelClass:Ljava/util/Map;
>         4: aload_2
>         5: invokeinterface #57,  1           // InterfaceMethod
> javax/faces/model/FacesDataModel.forClass:()Ljava/lang/Class;
>        10: aload_1
>        11: invokeinterface #58,  1           // InterfaceMethod
> javax/enterprise/inject/spi/ProcessBean.getBean:()Ljavax/enterprise/inject/spi/Bean;
>        16: invokeinterface #59,  1           // InterfaceMethod
> javax/enterprise/inject/spi/Bean.getBeanClass:()Ljava/lang/Class;
>        21: invokeinterface #55,  3           // InterfaceMethod
> java/util/Map.put:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
>        26: checkcast     #45                 // class java/lang/Class
>        29: pop
>        30: return
>      LineNumberTable:
>        line 110: 0
>        line 111: 5
>        line 112: 11
>        line 110: 21
>      LocalVariableTable:
>        Start  Length  Slot  Name   Signature
>            0      31     0  this   Lcom/sun/faces/cdi/CdiExtension;
>            0      31     1 event
>   Ljavax/enterprise/inject/spi/ProcessBean;
>            0      31     2     d   Ljavax/faces/model/FacesDataModel;
>    RuntimeVisibleParameterAnnotations:
>      parameter 0:
>        0: #79()
>      parameter 1:
>
> The difference is that the parameter annotation is made visible for
> this synthesized method. The result is that CDI subsequently scans
> this class and sees the synthesized method for the lambda as an
> observer method, which is of course not the intention. In this
> particular case it will result in a definition error:
>
> "Error occurred during deployment: Exception while loading the app :
>
> CDI definition failure:
>
> WELD-000409: Observer method for container lifecycle event
> [[BackedAnnotatedMethod]
>
> private com.sun.faces.cdi.CdiExtension.lambda$processBean$4(@Observes
> ProcessBean<Object>, FacesDataModel)] can only inject BeanManager.."
>
> I think that if the synthesized method happened to be a correct
> observer method, e.g. for a no-parameter lambda or one that happens to
> have the BeanManager as its first and only parameter, then CDI would
> silently start to call this method in response to events, which too is
> highly undesirable.
>
> Kind regards,
> Arjan Tijms
>





More information about the compiler-dev mailing list