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