diff --git a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotJDKReflection.java b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotJDKReflection.java index 6ee925cd3a7..280e5ee2aad 100644 --- a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotJDKReflection.java +++ b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotJDKReflection.java @@ -27,6 +27,7 @@ import java.lang.annotation.Annotation; import java.lang.reflect.Array; +import java.lang.reflect.Constructor; import java.lang.reflect.Executable; import java.lang.reflect.Field; import java.lang.reflect.Method; @@ -103,6 +104,17 @@ HotSpotResolvedObjectType getEnclosingClass(HotSpotResolvedObjectTypeImpl holder return (HotSpotResolvedObjectType) runtime().fromClass(javaMirror.getEnclosingClass()); } + @Override + HotSpotResolvedJavaMethod getEnclosingMethod(HotSpotResolvedObjectTypeImpl holder) { + Class javaMirror = getMirror(holder); + Method enclosingMethod = javaMirror.getEnclosingMethod(); + Executable enclosingExecutable = enclosingMethod != null ? enclosingMethod : javaMirror.getEnclosingConstructor(); + if (enclosingExecutable == null) { + return null; + } + return compilerToVM().asResolvedJavaMethod(enclosingExecutable); + } + @Override boolean equals(HotSpotObjectConstantImpl a, HotSpotObjectConstantImpl b) { return resolveObject(a) == resolveObject(b) && a.isCompressed() == b.isCompressed(); diff --git a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotJVMCIReflection.java b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotJVMCIReflection.java index 3211e0ab409..7d37739069d 100644 --- a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotJVMCIReflection.java +++ b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotJVMCIReflection.java @@ -50,6 +50,8 @@ abstract class HotSpotJVMCIReflection { abstract HotSpotResolvedObjectType getEnclosingClass(HotSpotResolvedObjectTypeImpl holder); + abstract HotSpotResolvedJavaMethod getEnclosingMethod(HotSpotResolvedObjectTypeImpl holder); + abstract boolean equals(HotSpotObjectConstantImpl hotSpotResolvedJavaType, HotSpotObjectConstantImpl that); abstract ResolvedJavaMethod.Parameter[] getParameters(HotSpotResolvedJavaMethodImpl javaMethod); diff --git a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotResolvedJavaType.java b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotResolvedJavaType.java index d1e95bb6b4d..b66f8b1853d 100644 --- a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotResolvedJavaType.java +++ b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotResolvedJavaType.java @@ -61,6 +61,9 @@ public HotSpotResolvedObjectType getArrayClass() { return arrayOfType; } + @Override + public abstract HotSpotResolvedJavaMethod getEnclosingMethod(); + /** * Checks whether this type is currently being initialized. If a type is being initialized it * implies that it was {@link #isLinked() linked} and that the static initializer is currently diff --git a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotResolvedObjectTypeImpl.java b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotResolvedObjectTypeImpl.java index 0d43dea419e..5507bedf721 100644 --- a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotResolvedObjectTypeImpl.java +++ b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotResolvedObjectTypeImpl.java @@ -1145,6 +1145,11 @@ public HotSpotResolvedObjectType getEnclosingType() { return runtime().reflection.getEnclosingClass(this); } + @Override + public HotSpotResolvedJavaMethod getEnclosingMethod() { + return runtime().reflection.getEnclosingMethod(this); + } + @Override public ResolvedJavaMethod[] getDeclaredConstructors() { link(); diff --git a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotResolvedPrimitiveType.java b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotResolvedPrimitiveType.java index 02d91f53a68..12cade5cb9d 100644 --- a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotResolvedPrimitiveType.java +++ b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotResolvedPrimitiveType.java @@ -312,6 +312,11 @@ public ResolvedJavaType getEnclosingType() { return null; } + @Override + public HotSpotResolvedJavaMethod getEnclosingMethod() { + return null; + } + @Override public ResolvedJavaMethod[] getDeclaredConstructors() { return new ResolvedJavaMethod[0]; diff --git a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/SharedLibraryJVMCIReflection.java b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/SharedLibraryJVMCIReflection.java index d7e8fd37ab2..8ac3742942e 100644 --- a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/SharedLibraryJVMCIReflection.java +++ b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/SharedLibraryJVMCIReflection.java @@ -71,6 +71,11 @@ HotSpotResolvedObjectType getEnclosingClass(HotSpotResolvedObjectTypeImpl holder throw new HotSpotJVMCIUnsupportedOperationError("requires a call Class.getEnclosingClass()"); } + @Override + HotSpotResolvedJavaMethod getEnclosingMethod(HotSpotResolvedObjectTypeImpl holder) { + throw new HotSpotJVMCIUnsupportedOperationError("requires a call Class.getEnclosingMethod()"); + } + @Override boolean equals(HotSpotObjectConstantImpl x, HotSpotObjectConstantImpl y) { if (x == y) { diff --git a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/meta/ResolvedJavaType.java b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/meta/ResolvedJavaType.java index c7a73f2fe3d..7e7787ea227 100644 --- a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/meta/ResolvedJavaType.java +++ b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/meta/ResolvedJavaType.java @@ -404,6 +404,26 @@ default ResolvedJavaMethod resolveConcreteMethod(ResolvedJavaMethod method, Reso */ ResolvedJavaType getEnclosingType(); + /** + * Returns a {@link ResolvedJavaMethod} representing the immediately enclosing + * method or constructor of the underlying type, if this type represents a local + * or anonymous class within a method. Returns {@code null} otherwise. + * + * In particular, this method returns {@code null} if the underlying + * type is a local or anonymous class immediately enclosed by a class or + * interface declaration, instance initializer or static initializer. + * + * Note that in contrast to {@link Class#getEnclosingMethod()}, this returns + * both, methods and constructors. + * + * @return the immediately enclosing method or constructor of the underlying + * class, if this type is a local or anonymous class, otherwise {@code null}. + * + * @see Class#getEnclosingMethod() + * @see Class#getEnclosingConstructor() + */ + ResolvedJavaMethod getEnclosingMethod(); + /** * Returns an array reflecting all the constructors declared by this type. This method is * similar to {@link Class#getDeclaredConstructors()} in terms of returned constructors. Calling diff --git a/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.runtime.test/src/jdk/vm/ci/runtime/test/TestResolvedJavaType.java b/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.runtime.test/src/jdk/vm/ci/runtime/test/TestResolvedJavaType.java index 8fb29fbb725..abfb8eb6a62 100644 --- a/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.runtime.test/src/jdk/vm/ci/runtime/test/TestResolvedJavaType.java +++ b/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.runtime.test/src/jdk/vm/ci/runtime/test/TestResolvedJavaType.java @@ -64,6 +64,7 @@ import java.lang.invoke.MethodHandles.Lookup; import java.lang.reflect.AccessibleObject; import java.lang.reflect.Constructor; +import java.lang.reflect.Executable; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; @@ -767,6 +768,57 @@ private void assertGetPermittedSubclasses(Class clazz) { } } + @Test + public void getEnclosingMethodTest() { + // no anonymous class -> expected null + Object obj0 = new Object(); + testEnclosingMethod(obj0.getClass(), false); + + // anonymous class -> not null + Object obj1 = new Object() {}; + testEnclosingMethod(obj1.getClass(), true); + + // local class -> not null + class Foo {}; + testEnclosingMethod(Foo.class, true); + + class Bar { + final Object obj0; + final Object obj1; + final Object obj2; + + Bar() { + // no anonymous class -> expected null + obj0 = new Object(); + + // anonymous class -> not null + obj1 = new Object() {}; + + // local class -> not null + class Foo {}; + obj2 = new Foo(); + } + } + Bar bar = new Bar(); + testEnclosingMethod(bar.obj0.getClass(), false); + testEnclosingMethod(bar.obj1.getClass(), true); + testEnclosingMethod(bar.obj2.getClass(), true); + } + + private static void testEnclosingMethod(Class clazz, boolean isEnclosed) { + ResolvedJavaType type = metaAccess.lookupJavaType(clazz); + Method enclosingMethod = clazz.getEnclosingMethod(); + Executable expected = enclosingMethod != null ? enclosingMethod : clazz.getEnclosingConstructor(); + ResolvedJavaMethod actual = type.getEnclosingMethod(); + if (expected == null) { + assertFalse(isEnclosed); + assertNull(actual); + } else { + assertTrue(isEnclosed); + assertEquals(metaAccess.lookupJavaMethod(expected), actual); + } + } + static final Map, VTable> vtables = new HashMap<>(); static class VTable {