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
- usage of Unsafe has been also removed
- Surefire plugin updated to be able to run TestNG tests.
- Surefire tests run in same JVM as maven to (forkedCount=0) to ensure
  that MAVEN_OPTS are propagated to test execution
- Travis file changed to also run tests on IBM OpenJ9 11.
  • Loading branch information
giamma committed Jan 15, 2019
1 parent 83da961 commit 52bdc4d
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 29 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
13 changes: 12 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,17 @@
</dependencies>

<build>
<pluginManagement>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M3</version>
<configuration>
<forkCount>0</forkCount>
</configuration>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
Expand Down Expand Up @@ -108,7 +119,7 @@
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.7.9</version>
<version>0.8.2</version>
<executions>
<execution>
<goals>
Expand Down
56 changes: 31 additions & 25 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 All @@ -29,16 +28,12 @@
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.security.AccessController;
import java.security.PrivilegedExceptionAction;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.WeakHashMap;

import sun.misc.Unsafe;

/**
* Enhanced type resolution utilities.
*
Expand All @@ -54,6 +49,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 @@ -62,39 +58,49 @@ public final class TypeResolver {
JAVA_VERSION = Double.parseDouble(System.getProperty("java.specification.version", "0"));

try {
Unsafe unsafe = AccessController.doPrivileged(new PrivilegedExceptionAction<Unsafe>() {
@Override
public Unsafe run() throws Exception {
final Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);

return (Unsafe) f.get(null);
}
});
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);;

GET_CONSTANT_POOL = Class.class.getDeclaredMethod("getConstantPool");
String constantPoolName = JAVA_VERSION < 9 ? "sun.reflect.ConstantPool" : "jdk.internal.reflect.ConstantPool";
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 +744,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 52bdc4d

Please sign in to comment.