Skip to content
This repository has been archived by the owner. It is now read-only.
Permalink
Browse files
8247246: Add explicit ResolvedJavaType.link and expose presence of de…
…fault methods

Reviewed-by: kvn
  • Loading branch information
Tom Rodriguez committed Jun 24, 2020
1 parent 6715f23 commit 29936542adcd3c6f3b90b7ecf141a252ea4bf7ce
Show file tree
Hide file tree
Showing 9 changed files with 206 additions and 2 deletions.
@@ -1768,6 +1768,18 @@ C2V_VMENTRY(void, ensureInitialized, (JNIEnv* env, jobject, jobject jvmci_type))
}
C2V_END

C2V_VMENTRY(void, ensureLinked, (JNIEnv* env, jobject, jobject jvmci_type))
if (jvmci_type == NULL) {
JVMCI_THROW(NullPointerException);
}

Klass* klass = JVMCIENV->asKlass(jvmci_type);
if (klass != NULL && klass->is_instance_klass()) {
InstanceKlass* k = InstanceKlass::cast(klass);
k->link_class(CHECK);
}
C2V_END

C2V_VMENTRY_0(jint, interpreterFrameSize, (JNIEnv* env, jobject, jobject bytecode_frame_handle))
if (bytecode_frame_handle == NULL) {
JVMCI_THROW_0(NullPointerException);
@@ -2775,6 +2787,7 @@ JNINativeMethod CompilerToVM::methods[] = {
{CC "getInterfaces", CC "(" HS_RESOLVED_KLASS ")[" HS_RESOLVED_KLASS, FN_PTR(getInterfaces)},
{CC "getComponentType", CC "(" HS_RESOLVED_KLASS ")" HS_RESOLVED_TYPE, FN_PTR(getComponentType)},
{CC "ensureInitialized", CC "(" HS_RESOLVED_KLASS ")V", FN_PTR(ensureInitialized)},
{CC "ensureLinked", CC "(" HS_RESOLVED_KLASS ")V", FN_PTR(ensureLinked)},
{CC "getIdentityHashCode", CC "(" OBJECTCONSTANT ")I", FN_PTR(getIdentityHashCode)},
{CC "isInternedString", CC "(" OBJECTCONSTANT ")Z", FN_PTR(isInternedString)},
{CC "unboxPrimitive", CC "(" OBJECTCONSTANT ")" OBJECT, FN_PTR(unboxPrimitive)},
@@ -554,7 +554,14 @@
declare_constant(InstanceKlass::linked) \
declare_constant(InstanceKlass::being_initialized) \
declare_constant(InstanceKlass::fully_initialized) \
\
/*********************************/ \
/* InstanceKlass _misc_flags */ \
/*********************************/ \
\
declare_constant(InstanceKlass::_misc_is_unsafe_anonymous) \
declare_constant(InstanceKlass::_misc_has_nonstatic_concrete_methods) \
declare_constant(InstanceKlass::_misc_declares_nonstatic_concrete_methods) \
\
declare_constant(JumpData::taken_off_set) \
declare_constant(JumpData::displacement_off_set) \
@@ -56,6 +56,8 @@ int pathmap_open(const char* name) {
if (fd >= 0) {
print_debug("path %s substituted for %s\n", alt_path, name);
return fd;
} else {
print_debug("can't open %s\n", alt_path);
}

if (strrchr(name, '/')) {
@@ -65,12 +67,16 @@ int pathmap_open(const char* name) {
if (fd >= 0) {
print_debug("path %s substituted for %s\n", alt_path, name);
return fd;
} else {
print_debug("can't open %s\n", alt_path);
}
}
} else {
fd = open(name, O_RDONLY);
if (fd >= 0) {
return fd;
} else {
print_debug("can't open %s\n", name);
}
}
return -1;
@@ -772,6 +772,11 @@ HotSpotResolvedObjectTypeImpl getResolvedJavaType(long displacement, boolean com
*/
native void ensureInitialized(HotSpotResolvedObjectTypeImpl type);

/**
* Forces linking of {@code type}.
*/
native void ensureLinked(HotSpotResolvedObjectTypeImpl type);

/**
* Checks if {@code object} is a String and is an interned string value.
*/
@@ -372,6 +372,27 @@ public boolean isLinked() {
return isArray() ? true : getInitState() >= config().instanceKlassStateLinked;
}

@Override
public void link() {
if (!isLinked()) {
runtime().compilerToVm.ensureLinked(this);
}
}

@Override
public boolean hasDefaultMethods() {
HotSpotVMConfig config = config();
int miscFlags = UNSAFE.getChar(getMetaspaceKlass() + config.instanceKlassMiscFlagsOffset);
return (miscFlags & config.jvmMiscFlagsHasDefaultMethods) != 0;
}

@Override
public boolean declaresDefaultMethods() {
HotSpotVMConfig config = config();
int miscFlags = UNSAFE.getChar(getMetaspaceKlass() + config.instanceKlassMiscFlagsOffset);
return (miscFlags & config.jvmMiscFlagsDeclaresDefaultMethods) != 0;
}

/**
* Returns the value of the state field {@code InstanceKlass::_init_state} of the metaspace
* klass.
@@ -250,6 +250,20 @@ public ResolvedJavaType resolve(ResolvedJavaType accessingClass) {
public void initialize() {
}

@Override
public void link() {
}

@Override
public boolean hasDefaultMethods() {
return false;
}

@Override
public boolean declaresDefaultMethods() {
return false;
}

@Override
public ResolvedJavaField findInstanceFieldWithOffset(long offset, JavaKind expectedType) {
return null;
@@ -137,6 +137,9 @@ String getHostArchitectureName() {
final int jvmAccEnum = getConstant("JVM_ACC_ENUM", Integer.class);
final int jvmAccInterface = getConstant("JVM_ACC_INTERFACE", Integer.class);

final int jvmMiscFlagsHasDefaultMethods = getConstant("InstanceKlass::_misc_has_nonstatic_concrete_methods", Integer.class);
final int jvmMiscFlagsDeclaresDefaultMethods = getConstant("InstanceKlass::_misc_declares_nonstatic_concrete_methods", Integer.class);

// This is only valid on AMD64.
final int runtimeCallStackSize = getConstant("frame::arg_reg_save_area_bytes", Integer.class, osArch.equals("amd64") ? null : 0);

@@ -105,6 +105,30 @@ default boolean isLeaf() {
*/
boolean isLinked();

/**
* Links this type. If this method returns normally, then future calls of {@link #isLinked} will
* return true and future calls of {@link #link} are no-ops. If the method throws an exception,
* then future calls of {@link #isLinked} will return false and future calls of {@link #link}
* will reattempt the linking step which might succeed or throw an exception.
*/
default void link() {
throw new UnsupportedOperationException("link is unsupported");
}

/**
* Checks whether this type or any of its supertypes or superinterfaces has default methods.
*/
default boolean hasDefaultMethods() {
throw new UnsupportedOperationException("hasDefaultMethods is unsupported");
}

/**
* Checks whether this type declares defaults methods.
*/
default boolean declaresDefaultMethods() {
throw new UnsupportedOperationException("declaresDefaultMethods is unsupported");
}

/**
* Determines if this type is either the same as, or is a superclass or superinterface of, the
* type represented by the specified parameter. This method is identical to
@@ -308,13 +332,15 @@ default ResolvedJavaMethod resolveConcreteMethod(ResolvedJavaMethod method, Reso

/**
* Returns an array reflecting all the constructors declared by this type. This method is
* similar to {@link Class#getDeclaredConstructors()} in terms of returned constructors.
* similar to {@link Class#getDeclaredConstructors()} in terms of returned constructors. Calling
* this method forces this type to be {@link #link linked}.
*/
ResolvedJavaMethod[] getDeclaredConstructors();

/**
* Returns an array reflecting all the methods declared by this type. This method is similar to
* {@link Class#getDeclaredMethods()} in terms of returned methods.
* {@link Class#getDeclaredMethods()} in terms of returned methods. Calling this method forces
* this type to be {@link #link linked}.
*/
ResolvedJavaMethod[] getDeclaredMethods();

@@ -49,6 +49,9 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;

import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.reflect.AccessibleObject;
@@ -64,6 +67,7 @@
import java.util.Map;
import java.util.Set;

import org.junit.Assert;
import org.junit.Test;

import jdk.internal.org.objectweb.asm.*;
@@ -335,6 +339,111 @@ public void findLeastCommonAncestorTest() {
}
}

@Test
public void linkTest() {
for (Class<?> c : classes) {
ResolvedJavaType type = metaAccess.lookupJavaType(c);
type.link();
}
}

private class HidingClassLoader extends ClassLoader {
@Override
protected Class<?> findClass(final String name) throws ClassNotFoundException {
if (name.endsWith("MissingInterface")) {
throw new ClassNotFoundException("missing");
}
byte[] classData = null;
try {
InputStream is = HidingClassLoader.class.getResourceAsStream("/" + name.replace('.', '/') + ".class");
classData = new byte[is.available()];
new DataInputStream(is).readFully(classData);
} catch (IOException e) {
Assert.fail("can't access class: " + name);
}

return defineClass(null, classData, 0, classData.length);
}

ResolvedJavaType lookupJavaType(String name) throws ClassNotFoundException {
return metaAccess.lookupJavaType(loadClass(name));
}

HidingClassLoader() {
super(null);
}

}

interface MissingInterface {
}

static class MissingInterfaceImpl implements MissingInterface {
}

interface SomeInterface {
default MissingInterface someMethod() {
return new MissingInterfaceImpl();
}
}

static class Wrapper implements SomeInterface {
}

@Test
public void linkExceptionTest() throws ClassNotFoundException {
HidingClassLoader cl = new HidingClassLoader();
ResolvedJavaType inner = cl.lookupJavaType(Wrapper.class.getName());
assertTrue("expected default methods", inner.hasDefaultMethods());
try {
inner.link();
assertFalse("link should throw an exception", true);
} catch (NoClassDefFoundError e) {
}
}

@Test
public void hasDefaultMethodsTest() {
for (Class<?> c : classes) {
ResolvedJavaType type = metaAccess.lookupJavaType(c);
assertEquals(hasDefaultMethods(type), type.hasDefaultMethods());
}
}

@Test
public void declaresDefaultMethodsTest() {
for (Class<?> c : classes) {
ResolvedJavaType type = metaAccess.lookupJavaType(c);
assertEquals(declaresDefaultMethods(type), type.declaresDefaultMethods());
}
}

private static boolean hasDefaultMethods(ResolvedJavaType type) {
if (!type.isInterface() && type.getSuperclass() != null && hasDefaultMethods(type.getSuperclass())) {
return true;
}
for (ResolvedJavaType iface : type.getInterfaces()) {
if (hasDefaultMethods(iface)) {
return true;
}
}
return declaresDefaultMethods(type);
}

static boolean declaresDefaultMethods(ResolvedJavaType type) {
if (!type.isInterface()) {
/* Only interfaces can declare default methods. */
return false;
}
for (ResolvedJavaMethod method : type.getDeclaredMethods()) {
if (method.isDefault()) {
assert !Modifier.isStatic(method.getModifiers()) : "Default method that is static?";
return true;
}
}
return false;
}

private static class Base {
}

0 comments on commit 2993654

Please sign in to comment.