JDK-8162500 : Receiver annotations of inner classes of local classes not found at runtime
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.lang:reflect
  • Affected Version: 8,9,21
  • Priority: P3
  • Status: In Progress
  • Resolution: Unresolved
  • Submitted: 2016-07-25
  • Updated: 2024-11-05
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 24
24Unresolved
Related Reports
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Description
Receiver parameter annotations of inner classes of local classes (but not inner classes of static member classes) are not found at runtime.

For annotated receivers of inner classes of both static member classes and local classes in static methods, javac correctly generates runtime annotations.  However, if they are queried at runtime using Executable.getAnnotatedReceiverType().getAnnotations, the annotations from inner classes of local classes are not returned.  Test follows:

/*
 * Copyright 2016 Google Inc.  All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.AnnotatedType;
import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.util.Arrays;

/**
 * Receiver parameter annotations of inner classes of local classes not
 * found at runtime.
 */
public class ReceiverAnnotation {

    @Retention(RetentionPolicy.RUNTIME)
    @Target({ ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
    public @interface Anno {}

    static class Member {
        abstract class Inner {
            Inner(@Anno Member Member.this) { m(); }
            abstract void myMethod(@Anno Inner this);
        }
        void m() {}
    }

    public static void main(String[] args) throws Throwable {
        class Local {
            abstract class Inner {
                Inner(@Anno Local Local.this) { m(); }
                abstract void myMethod(@Anno Inner this);
            }
            void m() {}
        }

        int fails = 0;
        Class<?>[] clazzes = { Member.Inner.class, Local.Inner.class };
        for (Class<?> clazz : clazzes) {
            try {
                assertReceiverAnnotated(
                    clazz.getDeclaredMethod("myMethod"));
            } catch (Throwable t) {
                System.err.println(clazz.getName());
                t.printStackTrace();
                fails++;
            }
            try {
                assertReceiverAnnotated(
                    clazz.getDeclaredConstructor(clazz.getEnclosingClass()));
            } catch (Throwable t) {
                System.err.println(clazz.getName());
                t.printStackTrace();
                fails++;
            }
        }

        if (fails > 0) throw new AssertionError();
    }

    static void assertReceiverAnnotated(Executable ex) {
        AnnotatedType type = ex.getAnnotatedReceiverType();
        String expected = "[@" + Anno.class.getName().replace('$', '.') + "()]";
        String actual = Arrays.toString(type.getAnnotations());
        if (!expected.equals(actual))
            throw new AssertionError(
                String.format("expected: %s, actual: %s",
                              expected, actual));
    }
}

Comments
A pull request was submitted for review. Branch: master URL: https://git.openjdk.org/jdk/pull/20200 Date: 2024-07-16 18:02:57 +0000
16-07-2024

The root cause may be the same as JDK-8162501, that javac doesn't emit a static inner class access flag for local classes in static methods. Currently, core reflection rejects retrieving a receiver for JDK-8162501 blindly, while it treats the static local class here as an instance local class, causing the annotation to be lost on constructor receiver and placed on Local instead of Inner on method receiver.
22-05-2023

Reproducible on current mainline JDK 21, changed the example to avoid false positives for $ and . mismatch.
17-05-2023