Skip to content

Commit

Permalink
Merge branch 'master' into shaded-jar-only
Browse files Browse the repository at this point in the history
  • Loading branch information
NathanSweet committed Jun 11, 2018
2 parents e2d0bb9 + 984e6a0 commit 95f0a9b
Show file tree
Hide file tree
Showing 7 changed files with 159 additions and 125 deletions.
13 changes: 13 additions & 0 deletions .travis.yml
@@ -0,0 +1,13 @@
language: java

jdk:
- oraclejdk7
- openjdk7
- oraclejdk8

cache:
directories:
- $HOME/.m2

# use container
sudo: false
3 changes: 3 additions & 0 deletions README.md
@@ -1,5 +1,8 @@
![](https://raw.github.com/wiki/EsotericSoftware/reflectasm/images/logo.png)

[![Build Status](https://travis-ci.org/NathanSweet/reflectasm.png?branch=master)](https://travis-ci.org/NathanSweet/reflectasm)
[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.esotericsoftware/reflectasm/badge.svg)](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.esotericsoftware%22%20AND%20a%3Areflectasm)

Please use the [ReflectASM discussion group](http://groups.google.com/group/reflectasm-users) for support.

## Overview
Expand Down
150 changes: 84 additions & 66 deletions src/com/esotericsoftware/reflectasm/AccessClassLoader.java
Expand Up @@ -17,6 +17,7 @@
import java.lang.ref.WeakReference;
import java.lang.reflect.Method;
import java.security.ProtectionDomain;
import java.util.HashSet;
import java.util.WeakHashMap;

class AccessClassLoader extends ClassLoader {
Expand All @@ -29,60 +30,34 @@ class AccessClassLoader extends ClassLoader {
// Fast-path for classes loaded in the same ClassLoader as this class.
static private final ClassLoader selfContextParentClassLoader = getParentClassLoader(AccessClassLoader.class);
static private volatile AccessClassLoader selfContextAccessClassLoader = new AccessClassLoader(selfContextParentClassLoader);

static private volatile Method defineClassMethod;

static AccessClassLoader get (Class type) {
ClassLoader parent = getParentClassLoader(type);
// 1. fast-path:
if (selfContextParentClassLoader.equals(parent)) {
if (selfContextAccessClassLoader == null) {
synchronized (accessClassLoaders) { // DCL with volatile semantics
if (selfContextAccessClassLoader == null)
selfContextAccessClassLoader = new AccessClassLoader(selfContextParentClassLoader);
}
}
return selfContextAccessClassLoader;
}
// 2. normal search:
synchronized (accessClassLoaders) {
WeakReference<AccessClassLoader> ref = accessClassLoaders.get(parent);
if (ref != null) {
AccessClassLoader accessClassLoader = ref.get();
if (accessClassLoader != null)
return accessClassLoader;
else
accessClassLoaders.remove(parent); // the value has been GC-reclaimed, but still not the key (defensive sanity)
}
AccessClassLoader accessClassLoader = new AccessClassLoader(parent);
accessClassLoaders.put(parent, new WeakReference<AccessClassLoader>(accessClassLoader));
return accessClassLoader;
}
private final HashSet<String> localClassNames = new HashSet();

private AccessClassLoader (ClassLoader parent) {
super(parent);
}

public static void remove (ClassLoader parent) {
// 1. fast-path:
if (selfContextParentClassLoader.equals(parent)) {
selfContextAccessClassLoader = null;
} else {
// 2. normal search:
synchronized (accessClassLoaders) {
accessClassLoaders.remove(parent);
/** Returns null if the access class has not yet been defined. */
Class loadAccessClass (String name) {
// No need to check the parent class loader if the access class hasn't been defined yet.
if (localClassNames.contains(name)) {
try {
return loadClass(name, false);
} catch (ClassNotFoundException ex) {
throw new RuntimeException(ex); // Should not happen, since we know the class has been defined.
}
}
return null;
}

public static int activeAccessClassLoaders () {
int sz = accessClassLoaders.size();
if (selfContextAccessClassLoader != null) sz++;
return sz;
Class defineAccessClass (String name, byte[] bytes) throws ClassFormatError {
localClassNames.add(name);
return defineClass(name, bytes);
}

private AccessClassLoader (ClassLoader parent) {
super(parent);
}

protected java.lang.Class<?> loadClass (String name, boolean resolve) throws ClassNotFoundException {
protected Class<?> loadClass (String name, boolean resolve) throws ClassNotFoundException {
// These classes come from the classloader that loaded AccessClassLoader.
if (name.equals(FieldAccess.class.getName())) return FieldAccess.class;
if (name.equals(MethodAccess.class.getName())) return MethodAccess.class;
Expand All @@ -95,52 +70,95 @@ protected java.lang.Class<?> loadClass (String name, boolean resolve) throws Cla
Class<?> defineClass (String name, byte[] bytes) throws ClassFormatError {
try {
// Attempt to load the access class in the same loader, which makes protected and default access members accessible.
return (Class<?>)getDefineClassMethod().invoke(getParent(), new Object[] {name, bytes, Integer.valueOf(0), Integer.valueOf(bytes.length),
getClass().getProtectionDomain()});
return (Class<?>)getDefineClassMethod().invoke(getParent(),
new Object[] {name, bytes, Integer.valueOf(0), Integer.valueOf(bytes.length), getClass().getProtectionDomain()});
} catch (Exception ignored) {
// continue with the definition in the current loader (won't have access to protected and package-protected members)
}
return defineClass(name, bytes, 0, bytes.length, getClass().getProtectionDomain());
}

// As per JLS, section 5.3,
// "The runtime package of a class or interface is determined by the package name and defining class loader of the class or interface."
static boolean areInSameRuntimeClassLoader(Class type1, Class type2) {

if (type1.getPackage()!=type2.getPackage()) {
// As per JLS, section 5.3,
// "The runtime package of a class or interface is determined by the package name and defining class loader of the class or
// interface."
static boolean areInSameRuntimeClassLoader (Class type1, Class type2) {
if (type1.getPackage() != type2.getPackage()) {
return false;
}
ClassLoader loader1 = type1.getClassLoader();
ClassLoader loader2 = type2.getClassLoader();
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
if (loader1==null) {
return (loader2==null || loader2==systemClassLoader);
}
if (loader2==null) {
return loader1==systemClassLoader;
if (loader1 == null) {
return (loader2 == null || loader2 == systemClassLoader);
}
return loader1==loader2;
if (loader2 == null) return loader1 == systemClassLoader;
return loader1 == loader2;
}

private static ClassLoader getParentClassLoader (Class type) {
static private ClassLoader getParentClassLoader (Class type) {
ClassLoader parent = type.getClassLoader();
if (parent == null) parent = ClassLoader.getSystemClassLoader();
return parent;
}
private static Method getDefineClassMethod() throws Exception {

static private Method getDefineClassMethod () throws Exception {
// DCL on volatile
if (defineClassMethod==null) {
synchronized(accessClassLoaders) {
defineClassMethod = ClassLoader.class.getDeclaredMethod("defineClass", new Class[] {String.class, byte[].class, int.class,
int.class, ProtectionDomain.class});
if (defineClassMethod == null) {
synchronized (accessClassLoaders) {
defineClassMethod = ClassLoader.class.getDeclaredMethod("defineClass",
new Class[] {String.class, byte[].class, int.class, int.class, ProtectionDomain.class});
try {
defineClassMethod.setAccessible(true);
}
catch (Exception ignored) {
} catch (Exception ignored) {
}
}
}
return defineClassMethod;
}

static AccessClassLoader get (Class type) {
ClassLoader parent = getParentClassLoader(type);
// 1. fast-path:
if (selfContextParentClassLoader.equals(parent)) {
if (selfContextAccessClassLoader == null) {
synchronized (accessClassLoaders) { // DCL with volatile semantics
if (selfContextAccessClassLoader == null)
selfContextAccessClassLoader = new AccessClassLoader(selfContextParentClassLoader);
}
}
return selfContextAccessClassLoader;
}
// 2. normal search:
synchronized (accessClassLoaders) {
WeakReference<AccessClassLoader> ref = accessClassLoaders.get(parent);
if (ref != null) {
AccessClassLoader accessClassLoader = ref.get();
if (accessClassLoader != null)
return accessClassLoader;
else
accessClassLoaders.remove(parent); // the value has been GC-reclaimed, but still not the key (defensive sanity)
}
AccessClassLoader accessClassLoader = new AccessClassLoader(parent);
accessClassLoaders.put(parent, new WeakReference<AccessClassLoader>(accessClassLoader));
return accessClassLoader;
}
}

static public void remove (ClassLoader parent) {
// 1. fast-path:
if (selfContextParentClassLoader.equals(parent)) {
selfContextAccessClassLoader = null;
} else {
// 2. normal search:
synchronized (accessClassLoaders) {
accessClassLoaders.remove(parent);
}
}
}

static public int activeAccessClassLoaders () {
int sz = accessClassLoaders.size();
if (selfContextAccessClassLoader != null) sz++;
return sz;
}
}
39 changes: 18 additions & 21 deletions src/com/esotericsoftware/reflectasm/ConstructorAccess.java
Expand Up @@ -22,7 +22,7 @@
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;

public abstract class ConstructorAccess<T> {
abstract public class ConstructorAccess<T> {
boolean isNonStaticMemberClass;

public boolean isNonStaticMemberClass () {
Expand All @@ -48,16 +48,13 @@ static public <T> ConstructorAccess<T> get (Class<T> type) {
String className = type.getName();
String accessClassName = className + "ConstructorAccess";
if (accessClassName.startsWith("java.")) accessClassName = "reflectasm." + accessClassName;
Class accessClass;

AccessClassLoader loader = AccessClassLoader.get(type);
try {
accessClass = loader.loadClass(accessClassName);
} catch (ClassNotFoundException ignored) {
Class accessClass = loader.loadAccessClass(accessClassName);
if (accessClass == null) {
synchronized (loader) {
try {
accessClass = loader.loadClass(accessClassName);
} catch (ClassNotFoundException ignored2) {
accessClass = loader.loadAccessClass(accessClassName);
if (accessClass == null) {
String accessClassNameInternal = accessClassName.replace('.', '/');
String classNameInternal = className.replace('.', '/');
String enclosingClassNameInternal;
Expand All @@ -80,17 +77,18 @@ static public <T> ConstructorAccess<T> get (Class<T> type) {
constructor = type.getDeclaredConstructor(enclosingType); // Inner classes should have this.
modifiers = constructor.getModifiers();
} catch (Exception ex) {
throw new RuntimeException("Non-static member class cannot be created (missing enclosing class constructor): "
+ type.getName(), ex);
throw new RuntimeException(
"Non-static member class cannot be created (missing enclosing class constructor): " + type.getName(), ex);
}
if (Modifier.isPrivate(modifiers)) {
throw new RuntimeException(
"Non-static member class cannot be created (the enclosing class constructor is private): " + type.getName());
"Non-static member class cannot be created (the enclosing class constructor is private): "
+ type.getName());
}
}
String superclassNameInternal = Modifier.isPublic(modifiers) ?
"com/esotericsoftware/reflectasm/PublicConstructorAccess" :
"com/esotericsoftware/reflectasm/ConstructorAccess";
String superclassNameInternal = Modifier.isPublic(modifiers)
? "com/esotericsoftware/reflectasm/PublicConstructorAccess"
: "com/esotericsoftware/reflectasm/ConstructorAccess";

ClassWriter cw = new ClassWriter(0);
cw.visit(V1_1, ACC_PUBLIC + ACC_SUPER, accessClassNameInternal, null, superclassNameInternal, null);
Expand All @@ -100,7 +98,7 @@ static public <T> ConstructorAccess<T> get (Class<T> type) {
insertNewInstanceInner(cw, classNameInternal, enclosingClassNameInternal);

cw.visitEnd();
accessClass = loader.defineClass(accessClassName, cw.toByteArray());
accessClass = loader.defineAccessClass(accessClassName, cw.toByteArray());
}
}
}
Expand All @@ -110,14 +108,13 @@ static public <T> ConstructorAccess<T> get (Class<T> type) {
} catch (Throwable t) {
throw new RuntimeException("Exception constructing constructor access class: " + accessClassName, t);
}
if (!(access instanceof PublicConstructorAccess) && !AccessClassLoader.areInSameRuntimeClassLoader(type, accessClass)) {
if (!(access instanceof PublicConstructorAccess) && !AccessClassLoader.areInSameRuntimeClassLoader(type, accessClass)) {
// Must test this after the try-catch block, whether the class has been loaded as if has been defined.
// Throw a Runtime exception here instead of an IllegalAccessError when invoking newInstance()
throw new RuntimeException(
(!isNonStaticMemberClass ?
"Class cannot be created (the no-arg constructor is protected or package-protected, and its ConstructorAccess could not be defined in the same class loader): " :
"Non-static member class cannot be created (the enclosing class constructor is protected or package-protected, and its ConstructorAccess could not be defined in the same class loader): ")
+ type.getName());
throw new RuntimeException((!isNonStaticMemberClass
? "Class cannot be created (the no-arg constructor is protected or package-protected, and its ConstructorAccess could not be defined in the same class loader): "
: "Non-static member class cannot be created (the enclosing class constructor is protected or package-protected, and its ConstructorAccess could not be defined in the same class loader): ")
+ type.getName());
}
access.isNonStaticMemberClass = isNonStaticMemberClass;
return access;
Expand Down
21 changes: 11 additions & 10 deletions src/com/esotericsoftware/reflectasm/FieldAccess.java
Expand Up @@ -108,7 +108,11 @@ public void setFields (Field[] fields) {

abstract public float getFloat (Object instance, int fieldIndex);

/** @param type Must not be the Object class, an interface, a primitive type, or void. */
static public FieldAccess get (Class type) {
if (type.getSuperclass() == null)
throw new IllegalArgumentException("The type must not be the Object class, an interface, a primitive type, or void.");

ArrayList<Field> fields = new ArrayList<Field>();
Class nextClass = type;
while (nextClass != Object.class) {
Expand All @@ -133,16 +137,13 @@ static public FieldAccess get (Class type) {
String className = type.getName();
String accessClassName = className + "FieldAccess";
if (accessClassName.startsWith("java.")) accessClassName = "reflectasm." + accessClassName;
Class accessClass = null;

AccessClassLoader loader = AccessClassLoader.get(type);
try {
accessClass = loader.loadClass(accessClassName);
} catch (ClassNotFoundException ignored) {
Class accessClass = loader.loadAccessClass(accessClassName);
if (accessClass == null) {
synchronized (loader) {
try {
accessClass = loader.loadClass(accessClassName);
} catch (ClassNotFoundException ignored2) {
accessClass = loader.loadAccessClass(accessClassName);
if (accessClass == null) {
String accessClassNameInternal = accessClassName.replace('.', '/');
String classNameInternal = className.replace('.', '/');

Expand Down Expand Up @@ -170,7 +171,7 @@ static public FieldAccess get (Class type) {
insertSetPrimitive(cw, classNameInternal, fields, Type.CHAR_TYPE);
insertGetString(cw, classNameInternal, fields);
cw.visitEnd();
accessClass = loader.defineClass(accessClassName, cw.toByteArray());
accessClass = loader.defineAccessClass(accessClassName, cw.toByteArray());
}
}
}
Expand Down Expand Up @@ -424,8 +425,8 @@ static private void insertSetPrimitive (ClassWriter cw, String classNameInternal
break;
case Type.DOUBLE:
setterMethodName = "setDouble";
loadValueInstruction = DLOAD; // (LLOAD and DLOAD actually load two slots)
maxLocals++;
loadValueInstruction = DLOAD;
maxLocals++; // (LLOAD and DLOAD actually load two slots)
break;
default:
setterMethodName = "set";
Expand Down

0 comments on commit 95f0a9b

Please sign in to comment.