Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Originally the static initialization was assuming that the ConstantPool
would be accessible via Class.getConstantPool(). However such method
only exists in the Oracle JDK and is not provided by the IBM OpenJ9 JDK.

But, both JDKs define an interface JavaLangAccess which offers method
getConstantPool( Class c ) that returns the constant pool for the given
class and the JavaLangAccess can be obtained via factory method
SharedSecrets.getJavaLangAccess().

Both SharedSecrets and JavaLangAccess have been relocated from sun.*
packages to jdk.internal.* packages in Java 9 or later. Now the code
obtains the pool using the right classes from the right packages and the
behavior is therefore portable across all JDKs available at the time of
writing
  • Loading branch information
GianMaria Romanato authored and GianMaria Romanato committed Jan 15, 2019
1 parent 83da961 commit 323fbc9
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 15 deletions.
16 changes: 13 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
sudo: false

language: java
install: mvn install -DskipTests=true -Dgpg.skip=true

jdk:
- oraclejdk8
install: mvn install -Dgpg.skip=true

jobs:
include:
- stage: adoptopenjdk.net - Eclipse OpenJ9
env:
- MAVEN_OPTS="--add-exports java.base/jdk.internal.misc=ALL-UNNAMED --add-exports java.base/jdk.internal.reflect=ALL-UNNAMED"
before_install:
- unset -v _JAVA_OPTIONS
- wget https://github.com/sormuras/bach/raw/master/install-jdk.sh
- source install-jdk.sh --url $(curl --silent https://api.adoptopenjdk.net/v2/binary/nightly/openjdk11\?openjdk_impl\=openj9\&os\=linux\&arch\=x64\&release\=latest\&type\=jdk | grep 'binary_link' | grep -Eo '(http|https)://[^"]+' | head -1)
- stage: jdk.java.net - OpenJDK - GPL
jdk: oraclejdk8

notifications:
email: false
Expand Down
44 changes: 32 additions & 12 deletions src/main/java/net/jodah/typetools/TypeResolver.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@

import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
Expand Down Expand Up @@ -54,6 +53,7 @@ public final class TypeResolver {
private static Method GET_CONSTANT_POOL;
private static Method GET_CONSTANT_POOL_SIZE;
private static Method GET_CONSTANT_POOL_METHOD_AT;
private static Object JAVA_LANG_ACCESS;
private static final Map<String, Method> OBJECT_METHODS = new HashMap<String, Method>();
private static final Map<Class<?>, Class<?>> PRIMITIVE_WRAPPERS;
private static final Double JAVA_VERSION;
Expand All @@ -72,29 +72,49 @@ public Unsafe run() throws Exception {
}
});

GET_CONSTANT_POOL = Class.class.getDeclaredMethod("getConstantPool");
String constantPoolName = JAVA_VERSION < 9 ? "sun.reflect.ConstantPool" : "jdk.internal.reflect.ConstantPool";
String constantPoolName = null;
String sharedSecretName = null;
if (JAVA_VERSION < 9) {
constantPoolName = "sun.reflect.ConstantPool";
sharedSecretName = "sun.misc.SharedSecrets";
} else {
constantPoolName = "jdk.internal.reflect.ConstantPool";
sharedSecretName = "jdk.internal.misc.SharedSecrets";
}

Class<?> sharedSecretsClass = Class.forName(sharedSecretName);
Method getJavaLangAccessMethod = sharedSecretsClass.getDeclaredMethod("getJavaLangAccess");
JAVA_LANG_ACCESS = getJavaLangAccessMethod.invoke(null);

GET_CONSTANT_POOL = JAVA_LANG_ACCESS.getClass().getDeclaredMethod("getConstantPool", Class.class);;

Class<?> constantPoolClass = Class.forName(constantPoolName);
GET_CONSTANT_POOL_SIZE = constantPoolClass.getDeclaredMethod("getSize");
GET_CONSTANT_POOL_METHOD_AT = constantPoolClass.getDeclaredMethod("getMethodAt", int.class);

// setting the methods as accessible
Field overrideField = AccessibleObject.class.getDeclaredField("override");
long overrideFieldOffset = unsafe.objectFieldOffset(overrideField);
unsafe.putBoolean(GET_CONSTANT_POOL, overrideFieldOffset, true);
unsafe.putBoolean(GET_CONSTANT_POOL_SIZE, overrideFieldOffset, true);
unsafe.putBoolean(GET_CONSTANT_POOL_METHOD_AT, overrideFieldOffset, true);
GET_CONSTANT_POOL.setAccessible(true);
GET_CONSTANT_POOL_SIZE.setAccessible(true);
GET_CONSTANT_POOL_METHOD_AT.setAccessible(true);

// additional checks - make sure we get a result when invoking the Class::getConstantPool and
// ConstantPool::getSize on a class
Object constantPool = GET_CONSTANT_POOL.invoke(Object.class);
Object constantPool = GET_CONSTANT_POOL.invoke(JAVA_LANG_ACCESS, Object.class);
GET_CONSTANT_POOL_SIZE.invoke(constantPool);

for (Method method : Object.class.getDeclaredMethods())
OBJECT_METHODS.put(method.getName(), method);

RESOLVES_LAMBDAS = true;
} catch (Exception ignore) {
} catch (Exception exception) {
if (JAVA_VERSION == 8) {
throw new IllegalStateException("Java 8 detected but lambda support initialization failed - Unsupported JDK?", exception);
} else if (JAVA_VERSION > 8) {
// TypeResolver can only work if JVM is started with parameters
// --add-exports java.base/jdk.internal.misc=ALL-UNNAMED --add-exports java.base/jdk.internal.reflect=ALL-UNNAMED
// which allow unnamed Java modules to access the jdk.internal.misc and jdk.internal.reflect packages, even if they
// are not exported by module java.base
throw new IllegalStateException("Java 9 or higher detected but internal access failed, did you remember to --add-exports?", exception);
}
}

Map<Class<?>, Class<?>> types = new HashMap<Class<?>, Class<?>>();
Expand Down Expand Up @@ -738,7 +758,7 @@ private static boolean isDefaultMethod(Method m) {
private static Member getMemberRef(Class<?> type) {
Object constantPool;
try {
constantPool = GET_CONSTANT_POOL.invoke(type);
constantPool = GET_CONSTANT_POOL.invoke(JAVA_LANG_ACCESS, type);
} catch (Exception ignore) {
return null;
}
Expand Down

0 comments on commit 323fbc9

Please sign in to comment.