8296967: [JVMCI] rationalize relationship between getCodeSize and getCode in ResolvedJavaMethod

Reviewed-by: never
This commit is contained in:
Doug Simon 2022-11-16 20:27:19 +00:00
parent b3ef337566
commit 37848a9ca2
8 changed files with 279 additions and 85 deletions

View File

@ -1901,9 +1901,6 @@ C2V_VMENTRY_NULL(jobjectArray, getDeclaredConstructors, (JNIEnv* env, jobject, A
}
InstanceKlass* iklass = InstanceKlass::cast(klass);
// Ensure class is linked
iklass->link_class(CHECK_NULL);
GrowableArray<Method*> constructors_array;
for (int i = 0; i < iklass->methods()->length(); i++) {
Method* m = iklass->methods()->at(i);
@ -1931,9 +1928,6 @@ C2V_VMENTRY_NULL(jobjectArray, getDeclaredMethods, (JNIEnv* env, jobject, ARGUME
}
InstanceKlass* iklass = InstanceKlass::cast(klass);
// Ensure class is linked
iklass->link_class(CHECK_NULL);
GrowableArray<Method*> methods_array;
for (int i = 0; i < iklass->methods()->length(); i++) {
Method* m = iklass->methods()->at(i);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2022, Oracle and/or its affiliates. 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
@ -45,6 +45,9 @@ final class HotSpotProfilingInfo implements ProfilingInfo {
HotSpotProfilingInfo(HotSpotMethodData methodData, HotSpotResolvedJavaMethod method, boolean includeNormal, boolean includeOSR) {
this.methodData = methodData;
this.method = method;
if (!method.getDeclaringClass().isLinked()) {
throw new IllegalArgumentException(method.format("%H.%n(%p) must be linked"));
}
this.includeNormal = includeNormal;
this.includeOSR = includeOSR;
this.isMature = methodData.isProfileMature();

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 2022, Oracle and/or its affiliates. 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
@ -256,7 +256,11 @@ final class HotSpotResolvedJavaMethodImpl extends HotSpotMethod implements HotSp
@Override
public int getCodeSize() {
return UNSAFE.getChar(getConstMethod() + config().constMethodCodeSizeOffset);
int codeSize = UNSAFE.getChar(getConstMethod() + config().constMethodCodeSizeOffset);
if (codeSize > 0 && !getDeclaringClass().isLinked()) {
return -1;
}
return codeSize;
}
@Override

View File

@ -996,11 +996,28 @@ final class HotSpotResolvedObjectTypeImpl extends HotSpotResolvedJavaType implem
@Override
public ResolvedJavaMethod[] getDeclaredConstructors() {
link();
return runtime().compilerToVm.getDeclaredConstructors(this);
}
@Override
public ResolvedJavaMethod[] getDeclaredConstructors(boolean forceLink) {
if (forceLink) {
link();
}
return runtime().compilerToVm.getDeclaredConstructors(this);
}
@Override
public ResolvedJavaMethod[] getDeclaredMethods() {
return getDeclaredMethods(true);
}
@Override
public ResolvedJavaMethod[] getDeclaredMethods(boolean forceLink) {
if (forceLink) {
link();
}
return runtime().compilerToVm.getDeclaredMethods(this);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2009, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2009, 2022, Oracle and/or its affiliates. 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
@ -36,24 +36,26 @@ import java.lang.reflect.Type;
public interface ResolvedJavaMethod extends JavaMethod, InvokeTarget, ModifiersProvider, AnnotatedElement {
/**
* Returns the bytecode of this method, if the method has code. The returned byte array does not
* contain breakpoints or non-Java bytecodes. This may return null if the
* {@linkplain #getDeclaringClass() declaring class} is not
* {@linkplain ResolvedJavaType#isLinked() linked}.
* Returns the method's bytecode. The returned bytecode does not contain breakpoints or non-Java
* bytecodes. This will return {@code null} if {@link #getCodeSize()} returns {@code <= 0} or if
* {@link #hasBytecodes()} returns {@code false}.
*
* The contained constant pool indices may not be the ones found in the original class file but
* The contained constant pool indexes may not be the ones found in the original class file but
* they can be used with the JVMCI API (e.g. methods in {@link ConstantPool}).
*
* @return the bytecode of the method, or {@code null} if {@code getCodeSize() == 0} or if the
* code is not ready.
* @return {@code null} if {@code getLinkedCodeSize() <= 0} otherwise the bytecode of the method
* whose length is guaranteed to be {@code > 0}
*/
byte[] getCode();
/**
* Returns the size of the bytecode of this method, if the method has code. This is equivalent
* to {@link #getCode()}. {@code length} if the method has code.
* Returns the size of the method's bytecode. If this method returns a value {@code > 0} then
* {@link #getCode()} will not return {@code null}.
*
* @return the size of the bytecode in bytes, or 0 if no bytecode is available
* @return 0 if the method has no bytecode, {@code -1} if the method does have bytecode but its
* {@linkplain #getDeclaringClass() declaring class} is not
* {@linkplain ResolvedJavaType#isLinked() linked} otherwise the size of the bytecode in
* bytes (guaranteed to be {@code > 0})
*/
int getCodeSize();
@ -437,15 +439,11 @@ public interface ResolvedJavaMethod extends JavaMethod, InvokeTarget, ModifiersP
}
/**
* Checks whether the method has bytecodes associated with it. Note that even if this method
* returns {@code true}, {@link #getCode} can return {@code null} if
* {@linkplain #getDeclaringClass() declaring class} is not
* {@linkplain ResolvedJavaType#isLinked() linked}.
*
* @return {@code this.getCodeSize() != 0}
* @see #getCodeSize()
* @return {@code getCodeSize() > 0}
*/
default boolean hasBytecodes() {
return getCodeSize() != 0;
return getCodeSize() > 0;
}
/**

View File

@ -339,6 +339,16 @@ public interface ResolvedJavaType extends JavaType, ModifiersProvider, Annotated
*/
ResolvedJavaMethod[] getDeclaredConstructors();
/**
* Returns an array reflecting all the constructors declared by this type. This method is
* similar to {@link Class#getDeclaredConstructors()} in terms of returned constructors.
*
* @param forceLink if {@code true}, forces this type to be {@link #link linked}
*/
default ResolvedJavaMethod[] getDeclaredConstructors(boolean forceLink) {
throw new UnsupportedOperationException();
}
/**
* Returns an array reflecting all the methods declared by this type. This method is similar to
* {@link Class#getDeclaredMethods()} in terms of returned methods. Calling this method forces
@ -346,6 +356,16 @@ public interface ResolvedJavaType extends JavaType, ModifiersProvider, Annotated
*/
ResolvedJavaMethod[] getDeclaredMethods();
/**
* Returns an array reflecting all the methods declared by this type. This method is similar to
* {@link Class#getDeclaredMethods()} in terms of returned methods.
*
* @param forceLink if {@code true}, forces this type to be {@link #link linked}
*/
default ResolvedJavaMethod[] getDeclaredMethods(boolean forceLink) {
throw new UnsupportedOperationException();
}
/**
* Returns the {@code <clinit>} method for this class if there is one.
*/

View File

@ -0,0 +1,145 @@
/*
* Copyright (c) 2022, Oracle and/or its affiliates. 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.
*/
package jdk.vm.ci.hotspot.test;
import static java.lang.reflect.Modifier.FINAL;
import static java.lang.reflect.Modifier.PRIVATE;
import static java.lang.reflect.Modifier.PROTECTED;
import static java.lang.reflect.Modifier.PUBLIC;
import static java.lang.reflect.Modifier.STATIC;
import static java.lang.reflect.Modifier.TRANSIENT;
import static java.lang.reflect.Modifier.VOLATILE;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import org.junit.Assert;
import org.junit.Test;
import jdk.vm.ci.hotspot.HotSpotJVMCIRuntime;
import jdk.vm.ci.hotspot.HotSpotResolvedJavaField;
import jdk.vm.ci.hotspot.HotSpotVMConfigAccess;
import jdk.vm.ci.meta.JavaType;
import jdk.vm.ci.meta.MetaAccessProvider;
import jdk.vm.ci.meta.ResolvedJavaField;
import jdk.vm.ci.meta.ResolvedJavaType;
import jdk.vm.ci.runtime.JVMCI;
/**
* Tests {@link HotSpotResolvedJavaField} functionality.
*/
public class HotSpotResolvedJavaFieldTest {
private static final Class<?>[] classesWithInternalFields = {Class.class, ClassLoader.class};
private static final Method createFieldMethod;
private static final Field indexField;
static {
Method m = null;
Field f = null;
try {
Class<?> typeImpl = Class.forName("jdk.vm.ci.hotspot.HotSpotResolvedObjectTypeImpl");
m = typeImpl.getDeclaredMethod("createField", JavaType.class, long.class, int.class, int.class);
m.setAccessible(true);
Class<?> fieldImpl = Class.forName("jdk.vm.ci.hotspot.HotSpotResolvedJavaFieldImpl");
f = fieldImpl.getDeclaredField("index");
f.setAccessible(true);
} catch (Exception e) {
throw new AssertionError(e);
}
createFieldMethod = m;
indexField = f;
}
/**
* Same as {@code HotSpotModifiers.jvmFieldModifiers()} but works when using a JVMCI version
* prior to the introduction of that method.
*/
private int jvmFieldModifiers() {
HotSpotJVMCIRuntime runtime = runtime();
HotSpotVMConfigAccess access = new HotSpotVMConfigAccess(runtime.getConfigStore());
int accEnum = access.getConstant("JVM_ACC_ENUM", Integer.class, 0x4000);
int accSynthetic = access.getConstant("JVM_ACC_SYNTHETIC", Integer.class, 0x1000);
return PUBLIC | PRIVATE | PROTECTED | STATIC | FINAL | VOLATILE | TRANSIENT | accEnum | accSynthetic;
}
HotSpotJVMCIRuntime runtime() {
return (HotSpotJVMCIRuntime) JVMCI.getRuntime();
}
MetaAccessProvider getMetaAccess() {
return runtime().getHostJVMCIBackend().getMetaAccess();
}
/**
* Tests that {@link HotSpotResolvedJavaField#getModifiers()} only includes the modifiers
* returned by {@link Field#getModifiers()}. Namely, it must not include
* {@code HotSpotResolvedJavaField#FIELD_INTERNAL_FLAG}.
*/
@Test
public void testModifiersForInternal() {
for (Class<?> c : classesWithInternalFields) {
ResolvedJavaType type = getMetaAccess().lookupJavaType(c);
for (ResolvedJavaField field : type.getInstanceFields(false)) {
if (field.isInternal()) {
Assert.assertEquals(0, ~jvmFieldModifiers() & field.getModifiers());
}
}
}
}
/**
* Tests that {@code HotSpotResolvedObjectTypeImpl#createField(String, JavaType, long, int)}
* always returns an {@linkplain ResolvedJavaField#equals(Object) equivalent} object for an
* internal field.
*
* @throws InvocationTargetException
* @throws IllegalArgumentException
* @throws IllegalAccessException
*/
@Test
public void testEquivalenceForInternalFields() throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
for (Class<?> c : classesWithInternalFields) {
ResolvedJavaType type = getMetaAccess().lookupJavaType(c);
for (ResolvedJavaField field : type.getInstanceFields(false)) {
if (field.isInternal()) {
HotSpotResolvedJavaField expected = (HotSpotResolvedJavaField) field;
int index = indexField.getInt(expected);
ResolvedJavaField actual = (ResolvedJavaField) createFieldMethod.invoke(type, expected.getType(), expected.getOffset(), expected.getModifiers(), index);
Assert.assertEquals(expected, actual);
}
}
}
}
@Test
public void testIsInObject() {
for (Field f : String.class.getDeclaredFields()) {
HotSpotResolvedJavaField rf = (HotSpotResolvedJavaField) getMetaAccess().lookupJavaField(f);
Assert.assertEquals(rf.toString(), rf.isInObject(runtime().getHostJVMCIBackend().getConstantReflection().forString("a string")), !rf.isStatic());
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2022, Oracle and/or its affiliates. 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
@ -45,13 +45,16 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@ -77,17 +80,13 @@ public class TestResolvedJavaMethod extends MethodUniverse {
*/
@Test
public void getCodeTest() {
for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) {
ResolvedJavaMethod m = e.getValue();
for (ResolvedJavaMethod m : joinValues(methods, constructors)) {
String ms = m.toString();
byte[] code = m.getCode();
if (code == null) {
assertTrue(m.getCodeSize() == 0);
assertEquals(ms, m.getCodeSize(), 0);
} else {
if (m.isAbstract()) {
assertTrue(code.length == 0);
} else if (!m.isNative()) {
assertTrue(code.length > 0);
}
assertTrue(ms, code.length > 0);
}
}
}
@ -97,21 +96,27 @@ public class TestResolvedJavaMethod extends MethodUniverse {
*/
@Test
public void getCodeSizeTest() {
for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) {
ResolvedJavaMethod m = e.getValue();
ResolvedJavaType unlinkedType = metaAccess.lookupJavaType(UnlinkedType.class);
assertTrue(!unlinkedType.isLinked());
for (ResolvedJavaMethod m : addExecutables(joinValues(methods, constructors), unlinkedType, false)) {
int codeSize = m.getCodeSize();
if (m.isAbstract()) {
assertTrue(codeSize == 0);
} else if (!m.isNative()) {
assertTrue(codeSize > 0);
String ms = m.toString();
if (m.isAbstract() || m.isNative()) {
assertEquals(ms, codeSize, 0);
} else if (!m.getDeclaringClass().isLinked()) {
assertEquals(ms, -1, codeSize);
} else {
assertTrue(ms, codeSize > 0);
}
}
assertTrue(!unlinkedType.isLinked());
}
@Test
public void equalsTest() {
for (ResolvedJavaMethod m : methods.values()) {
for (ResolvedJavaMethod that : methods.values()) {
List<ResolvedJavaMethod> executables = joinValues(methods, constructors);
for (ResolvedJavaMethod m : executables) {
for (ResolvedJavaMethod that : executables) {
boolean expect = m == that;
boolean actual = m.equals(that);
assertEquals(expect, actual);
@ -121,13 +126,7 @@ public class TestResolvedJavaMethod extends MethodUniverse {
@Test
public void getModifiersTest() {
for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) {
ResolvedJavaMethod m = e.getValue();
int expected = e.getKey().getModifiers();
int actual = m.getModifiers();
assertEquals(String.format("%s: 0x%x != 0x%x", m, expected, actual), expected, actual);
}
for (Map.Entry<Constructor<?>, ResolvedJavaMethod> e : constructors.entrySet()) {
for (Map.Entry<Executable, ResolvedJavaMethod> e : join(methods, constructors).entrySet()) {
ResolvedJavaMethod m = e.getValue();
int expected = e.getKey().getModifiers();
int actual = m.getModifiers();
@ -140,15 +139,11 @@ public class TestResolvedJavaMethod extends MethodUniverse {
*/
@Test
public void isClassInitializerTest() {
for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) {
for (Map.Entry<Executable, ResolvedJavaMethod> e : join(methods, constructors).entrySet()) {
// Class initializers are hidden from reflection
ResolvedJavaMethod m = e.getValue();
assertFalse(m.isClassInitializer());
}
for (Map.Entry<Constructor<?>, ResolvedJavaMethod> e : constructors.entrySet()) {
ResolvedJavaMethod m = e.getValue();
assertFalse(m.isClassInitializer());
}
}
@Test
@ -165,11 +160,7 @@ public class TestResolvedJavaMethod extends MethodUniverse {
@Test
public void isSyntheticTest() {
for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) {
ResolvedJavaMethod m = e.getValue();
assertEquals(e.getKey().isSynthetic(), m.isSynthetic());
}
for (Map.Entry<Constructor<?>, ResolvedJavaMethod> e : constructors.entrySet()) {
for (Map.Entry<Executable, ResolvedJavaMethod> e : join(methods, constructors).entrySet()) {
ResolvedJavaMethod m = e.getValue();
assertEquals(e.getKey().isSynthetic(), m.isSynthetic());
}
@ -189,11 +180,7 @@ public class TestResolvedJavaMethod extends MethodUniverse {
@Test
public void isVarArgsTest() {
for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) {
ResolvedJavaMethod m = e.getValue();
assertEquals(e.getKey().isVarArgs(), m.isVarArgs());
}
for (Map.Entry<Constructor<?>, ResolvedJavaMethod> e : constructors.entrySet()) {
for (Map.Entry<Executable, ResolvedJavaMethod> e : join(methods, constructors).entrySet()) {
ResolvedJavaMethod m = e.getValue();
assertEquals(e.getKey().isVarArgs(), m.isVarArgs());
}
@ -201,11 +188,7 @@ public class TestResolvedJavaMethod extends MethodUniverse {
@Test
public void isSynchronizedTest() {
for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) {
ResolvedJavaMethod m = e.getValue();
assertEquals(Modifier.isSynchronized(e.getKey().getModifiers()), m.isSynchronized());
}
for (Map.Entry<Constructor<?>, ResolvedJavaMethod> e : constructors.entrySet()) {
for (Map.Entry<Executable, ResolvedJavaMethod> e : join(methods, constructors).entrySet()) {
ResolvedJavaMethod m = e.getValue();
assertEquals(Modifier.isSynchronized(e.getKey().getModifiers()), m.isSynchronized());
}
@ -228,11 +211,11 @@ public class TestResolvedJavaMethod extends MethodUniverse {
private static boolean canBeStaticallyBound(Member method) {
int modifiers = method.getModifiers();
return (Modifier.isFinal(modifiers) ||
Modifier.isPrivate(modifiers) ||
Modifier.isStatic(modifiers) ||
method instanceof Constructor ||
Modifier.isFinal(method.getDeclaringClass().getModifiers())) &&
!Modifier.isAbstract(modifiers);
Modifier.isPrivate(modifiers) ||
Modifier.isStatic(modifiers) ||
method instanceof Constructor ||
Modifier.isFinal(method.getDeclaringClass().getModifiers())) &&
!Modifier.isAbstract(modifiers);
}
private static String methodWithExceptionHandlers(String p1, Object o2) {
@ -279,7 +262,7 @@ public class TestResolvedJavaMethod extends MethodUniverse {
@Test
public void getConstantPoolTest() {
for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) {
for (Map.Entry<Executable, ResolvedJavaMethod> e : join(methods, constructors).entrySet()) {
ResolvedJavaMethod m = e.getValue();
ConstantPool cp = m.getConstantPool();
assertTrue(cp.length() > 0);
@ -416,16 +399,22 @@ public class TestResolvedJavaMethod extends MethodUniverse {
}
}
public static List<ResolvedJavaMethod> addExecutables(List<ResolvedJavaMethod> to, ResolvedJavaType declaringType, boolean forceLink) {
to.addAll(List.of(declaringType.getDeclaredMethods(forceLink)));
to.addAll(List.of(declaringType.getDeclaredConstructors(forceLink)));
return to;
}
@Test
public void hasBytecodesTest() {
for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) {
ResolvedJavaMethod m = e.getValue();
assertTrue(m.hasBytecodes() == (m.isConcrete() && !m.isNative()));
}
for (Map.Entry<Constructor<?>, ResolvedJavaMethod> e : constructors.entrySet()) {
ResolvedJavaMethod m = e.getValue();
assertTrue(m.hasBytecodes());
ResolvedJavaType unlinkedType = metaAccess.lookupJavaType(UnlinkedType.class);
assertTrue(!unlinkedType.isLinked());
for (ResolvedJavaMethod m : addExecutables(joinValues(methods, constructors), unlinkedType, false)) {
boolean expect = m.getDeclaringClass().isLinked() && m.isConcrete() && !m.isNative();
boolean actual = m.hasBytecodes();
assertEquals(m.toString(), expect, actual);
}
assertTrue(!unlinkedType.isLinked());
}
@Test
@ -447,7 +436,13 @@ public class TestResolvedJavaMethod extends MethodUniverse {
}
}
static class UnlinkedType {
abstract static class UnlinkedType {
abstract void abstractMethod();
void concreteMethod() {
}
native void nativeMethod();
}
/**
@ -530,4 +525,22 @@ public class TestResolvedJavaMethod extends MethodUniverse {
}
}
}
@SafeVarargs
public static <K, V> Map<K, V> join(Map<? extends K, V>... maps) {
Map<K, V> res = new HashMap<>();
for (Map<? extends K, V> e : maps) {
res.putAll(e);
}
return res;
}
@SafeVarargs
public static <V> List<V> joinValues(Map<?, V>... maps) {
List<V> res = new ArrayList<>();
for (Map<?, V> e : maps) {
res.addAll(e.values());
}
return res;
}
}